I have a bunch of divs postioned absolutely on top of each other. When I bind a click event to all of them, only the top div responds. How can I send the event to all divs under the cursor?
问题:
回答1:
Taking FelixKling's suggestion to use document.elementFromPoint()
and Amberlamps's fiddle, and employing jQuery for the DOM interactions, I ended up with the following :
$divs = $("div").on('click.passThrough', function (e, ee) {
var $el = $(this).hide();
try {
console.log($el.text());//or console.log(...) or whatever
ee = ee || {
pageX: e.pageX,
pageY: e.pageY
};
var next = document.elementFromPoint(ee.pageX, ee.pageY);
next = (next.nodeType == 3) ? next.parentNode : next //Opera
$(next).trigger('click.passThrough', ee);
} catch (err) {
console.log("click.passThrough failed: " + err.message);
} finally {
$el.show();
}
});
DEMO
try/catch/finally
is used to ensure elements are shown again, even if an error occurs.
Two mechanisms allow the click event to be passed through or not :
- attaching the handler to only selected elements (standard jQuery).
- namespacing the click event,
click.passThrough
analogous toevent.stopPropagation()
.
Separately or in combination, these mechanisms offer some flexibility in controlling the attachment and propagation of "passThrough" behaviour. For example, in the DEMO, try removing class p
from the "b" element and see how the propagation behaviour has changed.
As it stands, the code needs to be edited to get different application-level behaviour. A more generalized solution would :
- allow for programmatic attachment of app-specific behaviour
- allow for programmatic inhibition of "passThrough" propagation, analogous to
event.stopPropagation()
.
Both of these ambitions might be achieved by establishing a clickPassthrough
event in jQuery, with underlying "passThrough" behaviour, but more work would be involved to achieve that. Maybe someone would like to have a go.
回答2:
This is not as easy as you might think. This is a solution that I came up with. I only tested it in Chrome and I did not use any framework.
The following snippet is just for add a click event to every div
in the document, that outputs its class name when triggered.
var divs = document.getElementsByTagName("div");
for(var i = 0; i < divs.length; i++) {
divs[i].onclick = function() {
console.log("class clicked: " + this.className);
};
}
Attaching a click event to the body element so that every single click event is noticed by our script.
if(document.addEventListener) {
document.body.addEventListener("click", countDivs);
} else if(document.attachEvent) {
document.attachEvent("onclick", countDivs);
}
Iterate through all divs that you want to check (you might want to adjust here to your preferred range of divs). Generate their computed style and check whether the mouse coordinates are within the range of the div´s position plus its width and height. Do not trigger click event when the div is our source element because the click event has already been fired by then.
function countDivs(e) {
e = e || window.event;
for(var i = 0; i < divs.length; i++) {
var cStyle = window.getComputedStyle(divs[i]);
if(divs[i] !== e.target && e.pageX >= parseInt(cStyle.left) && e.pageX <= (parseInt(cStyle.left) + parseInt(cStyle.width)) && e.pageY >= parseInt(cStyle.top) && e.pageY <= (parseInt(cStyle.top) + parseInt(cStyle.height))) {
divs[i].click();
}
}
}
CSS:
.a, .b, .c {
position: absolute;
height: 100px;
width: 100px;
border: 1px #000 solid
}
.a {
top: 100px;
left: 100px;
}
.b {
top: 120px;
left: 120px;
}
.c {
top: 140px;
left: 140px;
}
HTML:
<div class="a"></div>
<div class="b"></div>
<div class="c"></div>
I also added a jsFiddle
回答3:
A simple way could be to use elementFromPoint():
http://jsfiddle.net/SpUeN/1/
var clicks = 0,cursorPosition={};
$('div').click(function (e) {
if(typeof cursorPosition.X === 'undefined') {
cursorPosition.X = e.pageX;
cursorPosition.Y = e.pageY;
}
clicks++;
e.stopPropagation();
$(this).addClass('hided');
var underELEM = document.elementFromPoint(cursorPosition.X, cursorPosition.Y);
if (underELEM.nodeName.toUpperCase() === "DIV") $(underELEM).click();
else {
$('#clicks').html("Clicks: " + clicks);
$('.hided').removeClass('hided');
clicks=0;
cursorPosition = {};
}
});
回答4:
If you are stacking elements absolutely it may be simpler to stack them all in a positioned container, and handle the events from this parent. You can then manipulate its children without having to measure anything.