On my web page I have two divs, A and B:
The DOM looks as follows (simplified). The main thing to note here, is that the divs are on the same level in the DOM hierarchy and they are not ordered by anything. Also, the outer div does not only contain A and B but also more divs C,D,E etc. B may not necessarily be overlapped by A. It could also be C, lying behind A.
<div>
<div style="...">B</div>
<div style="...">A</div>
</div>
A click handler is attached to the outer div, capturing mouse click events on A or B in the bubbling phase. A click into the intersection of A and B will cause an click event on A which bubbles up to my click handler that is now fired.
Now the problem: Under certain conditions the handler decides that the event should not be handled by A but should belong to B. If an event should be handled by B, the same click handler will be fired, but the event objects currentTarget
property will be B instead of A.
How can I achieve this?
I've already looked at css3 pointer-handler and some event dispatching methods in JS, but could not really come up with a solution here.
Possible solution 1 (not cross-browser compatible) ?:
I think i might be possible to use the pointer-events css3 property. If the handler decides that the event should be handled by B, it sets the pointer-events to none. Then, it retriggers the mouse click. Question here: Is it possible to retrigger a mouse click with only the coordinates, not specifying a specific element?
Anyway, the support of pointer-events is limited: http://caniuse.com/pointer-events
You can try to pass the event to B
(using jQuery):
(pseudo-code)
var $A = $('#A'), $B = $('#B');
$B.on('click', function () {
console.log('B clicked');
});
$('#outer').on('click', function (evt) {
if (Math.random() < 0.1) { // << pseudo condition
$B.trigger(evt);
}
});
http://jsfiddle.net/hzErh/
Update:
using a function like this:
// this function should (did not enough testing) return all elements at a given
// point in the order of top to bottom.
var elementsFromPoint = function (x, y, root) {
var buffer = [], result = [], el, i = 0, lim;
while ((el = document.elementFromPoint(x, y)) && [root, document.body, document.body.parentNode].indexOf(el) < 0) {
result.push(el);
buffer.push({el: el, visibility: el.style.visibility});
el.style.visibility = 'hidden';
}
for (lim = buffer.length; i < lim; i += 1) {
buffer[i].el.style.visibility = buffer[i].visibility;
}
return result;
};
you can get the elements for a given x/y coordinate. It's a bit hacky and needs more testing though.
$('#outer').on('click', function (evt) {
var elements = elementsFromPoint(
evt.pageX + $(this).offset().left,
evt.pageY + $(this).offset().top,
this
);
console.log(elements);
});
demo: http://jsfiddle.net/hzErh/1/
Update 2: this version should give better performance for many elements:
// this function should (did not enough testing) return all elements at a given
// point, though the order is not representative for the visible order.
var elementsFromPoint = function (root, x, y) {
var result = [], i = 0, lim = root.childNodes.length, bb;
for (; i < lim; i += 1) {
bb = root.childNodes[i].getBoundingClientRect();
if (bb.left <= x && x <= bb.right && bb.top <= y && y <= bb.bottom) {
result.push(root.childNodes[i]);
}
}
return result;
};
demo: http://jsfiddle.net/CTdZp/2/
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With