1

I am not sure if this is a good question or not but I would love to hear some inputs on this.

Imagine there are 2 kinds of events:

A: a mouse click
B: mouse is currently inside a box

Also, imagine there are 2 kinds of event handler functions:

f: console.log("mouse is clicked")
g: console.log("mouse is clicked inside the box")

Now what I want the code to do is, when the mouse is clicked anywhere but inside the box, display the message "mouse is clicked". However, when the mouse is clicked inside the box, display the message "mouse is clicked inside the box".

My current way of implementation looks like follows:

if (A) then {f()}
if (A and B) then {g()}

In short, I registered f as a listener for A, and created a new event that fires when both A and B hold, and registered g ans a listener for that event.

of course, when the mouse is clicked inside the box, I get two messages: "mouse is clicked inside the box" "mouse is clicked"

rather than the single "mouse is clicked inside the box" that I am looking for.

Is there any formal way of defining what is happening, and what is the best way to address it? In short, I want the event handler g to have precedence over the handler f, such that when g is invoked it suppresses the invocation of f.

Evan Pu
  • 2,099
  • 5
  • 21
  • 36

2 Answers2

2

What you need to read up on is event bubbling and propagation in jQuery.

A jQuery event handler callback will be passed an event object as a parameter. This contains a method that can be called to stop the event propagation, or 'bubbling' up to any parent handlers.

For your example, this means you can bind an event to the window/whole document for 'mouse is clicked', and then bind an event to only the box for 'mouse is clicked inside the box' - but stop this event from reaching the parent handler.

$(body).click(function() {
    console.log("mouse is clicked");   
});

$("#box").click(function(e) {
    e.stopPropagation();
    console.log("mouse is clicked inside the box");
});

On parent handlers (event bubbling)

To be more specific, the handlers themselves do not have a parent/child relationship - they are just bound directly to the DOM elements. What do have a parent/child relationship is the DOM elements themselves.

In this example, #box is a child of the body element (although probably with other elements in between). When a click is triggered on #box, the handler will be checked for the element, and then the element's parent, and then the element's parent's parent etc ... all the way up the DOM tree. This will continue until either the event is stopped from bubbling, or the top of the tree is reached.

In this way, you could add another element inside box and add another handler to do the same thing, i.e. #button - and if you stopped propagation of the event, neither the #box or body message would appear.

Alex Osborn
  • 9,831
  • 3
  • 33
  • 44
  • thanks for the answer, but can you explain in this example which is the parent handler of which? And how is such parenthood specified? I understand that if the mouse is clicked and the handler for $(box).click is invoked first, the chain will stop. But how do you enforce the handler for $(body).click not be invoked beforehand? – Evan Pu Feb 19 '13 at 04:08
  • Sure, modified answer above. – Alex Osborn Feb 19 '13 at 20:44
1

This can be achieved with one event listener:

window.addEventListener('click', function (e) {
    if (document.getElementById('box').contains(e.target)) {
        alert('Box clicked!');
    } else {
        alert('Not box clicked!');
    }
}, false);

or jQuery:

$(window).click(function (e) {
    if ($('#box')[0].contains(e.target)) {
        alert('Box clicked!');
    } else {
        alert('Not box clicked!');
    }
});
Teemu
  • 22,918
  • 7
  • 53
  • 106