6

On my web page I have two divs, A and B:

enter image description here

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

Erik
  • 11,944
  • 18
  • 87
  • 126
  • Have you looked at https://developer.mozilla.org/en/CSS/pointer-events – j08691 Mar 28 '12 at 14:55
  • Yes, the main problem is, that I need to dynamically decide whether I want to have the event on A or B – Erik Mar 28 '12 at 15:03
  • already in place, what feature can I use? – Erik Mar 28 '12 at 15:06
  • Did you try to create a invisible element in top of target bottom element? or you can get the x, y, positions from your mouse event and if is inside the target element area and position dispatch the click event on it. – Gabriel Gartz Mar 28 '12 at 15:20
  • Thats true. I could do some maths and get all the divs whose boxes are around the clicked point. As the amount of divs I have to check can be thousands, I would like to avoid this, if possible. – Erik Mar 28 '12 at 15:25

1 Answers1

3

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/

Yoshi
  • 54,081
  • 14
  • 89
  • 103
  • @Erik I just wanted to simulate your *"Under certain conditions"* ;) – Yoshi Mar 28 '12 at 15:15
  • Ah, right! Thats a good solution then. Unfortunately I was not exact enough describing my problem. In my outer div there can be many div boxes like a and b and it is totally random which ones lies behind any other. So the main problem gets here, how do I know which box lies behind the clicked? – Erik Mar 28 '12 at 15:17
  • @Erik Ah, one thing you could do is a simple hit-test on the mouse-coords and all child-elements – Yoshi Mar 28 '12 at 15:25
  • Yes, we are already discussing this in the other comment section (on my post). Unfortunately I have really a lot of divs and searching will be costly (time/cpu). – Erik Mar 28 '12 at 15:48
  • Interesting update! I did not know about elementFromPoint. Looks as if could save me a lot. I'll check it out – Erik Mar 28 '12 at 15:50
  • 1
    @Erik I added another version. As you've written above that the number of elements *can be thousands*, the new version should give a lot better performance, as there is no redrawing involved. – Yoshi Mar 28 '12 at 16:25
  • @Yoshi Can I delete everything but the last example, to clean this up? Great function btw. – Luke Stanley Sep 26 '12 at 00:10
  • I'm not sure getBoundingClientRect is reliable on a scrolled page - http://javascript.info/tutorial/coordinates#the-combined-approach may help, but with bottom and right coords too. – Luke Stanley Sep 26 '12 at 01:23
  • @LukeStanley Yes please go ahead, just leave those parts that originally answered the question in such a way that it still does ;) – Yoshi Sep 26 '12 at 06:52
  • done, @Yoshi would also like to note these related question: http://stackoverflow.com/questions/3277369/simulate-a-click-by-using-x-y-coordinates-javascript http://stackoverflow.com/questions/10305477/simulate-click-at-x-y-coordinates-using-javascript?lq=1 so that this answer is easier to find – Luke Stanley Sep 26 '12 at 23:34