16

As you know, events usually bubble up in JavaScript, so the event handler of the element that fires the event is executed first, then the event handler of the parent element is called and so on. This behaviour causes some problems on a project I'm currently working on, I would rather have the execution order reversed.

I figured out a solution that is using timeouts:

$(element).mouseover(function(){
    var that = this;
    setTimeout(function() {
       //actual event handler, but references to "this" are replaced with "that"
    },$(this).parents().length)
 });

So basically, the event handlers are executed after a short timeout, the waiting time depends on the the depth of the element in the DOM-tree: The event handler of the the html-element is executed right away, the event handler of the body element is executed after waiting 1ms and so on. So the execution order of the events is reversed.

The results of my first tests are positive, but I'm still not sure if there are any problems or drawbacks with this solution. What do you think of this solution? Other ideas on how to solve this problems are also highly appreciated.

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Simon
  • 3,509
  • 18
  • 21
  • 1
    I don't understand why you're setting the *amount of parents* as the *time in milliseconds* for the timeout. – pimvdb Nov 18 '11 at 12:32
  • 2
    He's doing that to ensure that executing event handler on leaf element will be delayed so much, that it will be executed last in chain. – WTK Nov 18 '11 at 13:01

3 Answers3

28

Reverse event bubbling is called capture phase.

See the DOM event architecture

Pass true as the 3rd argument to Event.addEventListener to have it trigger on capture phase

el.addEventListener("click", function () {
  console.log("i run in capture");
}, true);

Of course it won't work in legacy platforms. You'll need a DOM3 events shim to emulate the event system. Feel free to contribute to the DOM-shim

Raynos
  • 166,823
  • 56
  • 351
  • 396
0

You could try to emulate it, but probably it could make lots of troubles.

Here is very very simple example (and on jsfiddle). The idea is to aggregate callbacks from each event, and calling them in reverse order in document event handler (we also clear our list, to ensure new queue on next click).

<div id="a">
    <div id="b">
        <a href="/test">Click Me!</a>
    </div>
</div>
var cb = [];

$(document).click(function(evt){

    cb.unshift(function(){
        console.log('document');
    });
    var i;
    for(i=0; i<cb.length; ++i){
        cb[i].call(null, evt);
    }
    cb = [];
});

$("#a").click(function(){
    cb.unshift(function(){
        console.log('div#a');
    });
});

$("#b").click(function(){
    cb.unshift(function(){
        console.log('div#b');
    });
});

$('a').click(function(evt){
    cb.unshift(function(evt){
        evt.preventDefault();
        console.log('a');
    });
});

Maybe you need to change your design? Could you post more information, why you need event capturing?

Adam Jurczyk
  • 2,153
  • 12
  • 16
  • Thanks for your answer. The reason why I need this reverse event handler execution is quite complicated and hard to explain in short. Basically, I have mouseover handlers for different elements of an arbitrary html document. Usually, event handlers of the outer elements are executed first, because you move your mouse pointer over outer elements before you get to the inner elements.But on elements that contain inner elements without having any padding,this fails because you don't have to move over the outer elements to reach the inner ones.So I want to reverse the order for consistent behaviour – Simon Nov 18 '11 at 22:15
  • Hmmm... you could allways use some kind of 'inheritance', like here: http://jsfiddle.net/GEFAq/2/ And it should work (trigger is synchronous, as far as i see). This is basic concept - in elements handler stop propagation and trigger same event on parent element before performing any other action. – Adam Jurczyk Nov 21 '11 at 15:41
  • But, if you really dont care about old browsers, than don't bother and go with @Raynos solution :) – Adam Jurczyk Nov 21 '11 at 15:45
  • I already implemented Raynow Solution and it works really well. Fortunately, it is not a public site and I can put up some system requirements. I also left my first solution (with the timeouts) as fallback-solution for browsers that do not implement addEventListener(). Thanks for your ideas! – Simon Nov 21 '11 at 20:40
-1

I can see one huge drawback of your solution is fact, that for every handled event you have to traverse DOM upwards starting with this to establish number of parents.

Traversing in such manner in every event handler = low performance.

WTK
  • 16,583
  • 6
  • 35
  • 45
  • Every time an event fires, it runs all the way from the document root down to the element which triggered the event, then all the way back to the root. Taking an extra trip up to the document root in your handler code isn't going to take an excessive amount of time. – Sam Dufel Jul 23 '12 at 21:57