19

I need to capture an event instead of letting it bubble. This is what I want:

<body>
   <div>
   </div>
</body>

From this sample code I have a click event bounded on the div and the body. I want the body event to be called first. How do I go about this?

Toosick
  • 460
  • 1
  • 6
  • 14

3 Answers3

41

Use event capturing instead:-

$("body").get(0).addEventListener("click", function(){}, true);

Check the last argument to "addEventListener" by default it is false and is in event bubbling mode. If set to true will work as capturing event.

For cross browser implementation.

var bodyEle = $("body").get(0);
if(bodyEle.addEventListener){
   bodyEle.addEventListener("click", function(){}, true);
}else if(bodyEle.attachEvent){
   document.attachEvent("onclick", function(){
       var event = window.event;
   });
}

IE8 and prior by default use event bubbling. So I attached the event on document instead of body, so you need to use event object to get the target object. For IE you need to be very tricky.

pvnarula
  • 2,771
  • 18
  • 22
  • @FelixKling Works on IE9 but not below – A. Wolff Jun 22 '13 at 09:29
  • I'm going to try this out. https://github.com/private-face/jquery.bind-first. Hopefully it does what I'm going for. – Toosick Jun 22 '13 at 09:30
  • 1
    @FelixKling This will work in IE 9 but now in prior to it. I am updating for that – pvnarula Jun 22 '13 at 09:30
  • @roasted: Fixed my comment already, wasn't sure if it was already supported in IE9. – Felix Kling Jun 22 '13 at 09:30
  • @pvnarula Thanks for the effort in your response. Always like the quick little jsfiddle. +1 – Toosick Jun 22 '13 at 09:38
  • @PrestonTighe I am glad you like it. – pvnarula Jun 22 '13 at 09:39
  • how to do it with event delegation – Strikers Jun 04 '15 at 12:40
  • Hi @challet, the jQuery is browsers compatible. What they have done is keeping the event listener in bubbling mode always. You can't change it in jQuery. They have tried to make it work in all browser so no exception. I hope this answers your question – pvnarula Jul 29 '15 at 15:20
  • Hi @pvnarula it does answer the question, thanks. I've tried to catch the events in their capturing phase through a special jQuery namespace event. It was supposed to be re-triggered from a hacked jQuery event handler, but it seems like a dead end, at least for me. – challet Aug 31 '15 at 15:10
  • @challet What is the actual problem you are facing? – pvnarula Sep 15 '15 at 08:15
  • You can also do `$('body')[0].addEventListener('click', function(){}, true);` – thdoan Oct 18 '16 at 10:47
  • You could call `setCapture` for IE. That should remove all hacks. Haven't tried it though because I don't support unmaintained browsers. But it used to be so when I still worked with IE6. – ygoe Dec 21 '17 at 19:09
  • You don't need jQuery to select a node though. `var bodyEle = document.getElementsByTagName('body')[0]` – Sergiy Ostrovsky Oct 01 '18 at 13:14
3

I'd do it like this:

$("body").click(function (event) {
  // Do body action

  var target = $(event.target);
  if (target.is($("#myDiv"))) {
    // Do div action
  }
});
Jonathan
  • 8,771
  • 4
  • 41
  • 78
  • although this might not seem the proper answer to this question, it's still a way to accomplish the expected behavior. With one single click event handled by the browser, you might have a faster response time and guide you business logic easier. I wouldn't downvote for this answer – Alwin Kesler Nov 15 '14 at 17:58
  • @AlwinKesler How is this not the proper answer? It does exactly what OP wants in the most readable and concise way presented here. – Jonathan Dec 12 '14 at 08:39
  • can't agree more. What I meant was that I saw a downvote to your answer. I just express my opinion saying that this kind of approach helps minimize events handling. However, I can't say which method is faster – Alwin Kesler Dec 15 '14 at 20:05
  • 1
    The first sentence in the question clearly states that it is about capturing and not bubbling: "I need to capture an event instead of letting it bubble." - this answer does not address capturing vs. bubbling, therefore a down-vote – xorcus Dec 08 '15 at 09:09
  • This answer is incorrect. jQuery `click` binds on the bubble phase. The bubble phase occurs after the capture phase. jQuery does not support event capturing ( explanation: http://stackoverflow.com/questions/7163616/why-does-jquery-event-model-does-not-support-event-capture-and-just-supports-eve ). – Elliot B. Jan 25 '16 at 21:34
  • 1
    @ElliotB. I know the difference but it seemed an XY to me: "I want the body event to be called first. How do I go about this?" - This would be one way to achieve this behaviour. But you are right in that this has nothing to do with the event capture phase. – Jonathan Jan 25 '16 at 21:46
  • @Jonathan, as this is still being found in 2019 in answers, I would suggest explaining your answer a bit more by giving context/confirmation that its an alternative to relying on capture, and instead a solution on re-thinking how one does their click events. – redfox05 Feb 28 '19 at 19:52
1

More generally than @pvnarula's answer:

var global_handler = function(name, handler) {
    var bodyEle = $("body").get(0);
    if(bodyEle.addEventListener) {
        bodyEle.addEventListener(name, handler, true);
    } else if(bodyEle.attachEvent) {
        handler = function(){
            var event = window.event;
            handler(event)
        };
        document.attachEvent("on" + name, handler)
    }
    return handler
}
var global_handler_off = function(name, handler) {
    var bodyEle = $("body").get(0);
    if(bodyEle.removeEventListener) {
       bodyEle.removeEventListener(name, handler, true);
    } else if(bodyEle.detachEvent) {
       document.detachEvent("on" + name, handler);
    }
}

Then to use:

shield_handler = global_handler("click", function(ev) {
    console.log("poof")
})

// disable
global_handler_off("click", shield_handler)
shield_handler = null;
Abram
  • 413
  • 1
  • 3
  • 13