Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

setTimeout and event propagation

I want to create a one-time event triggered by clicking anywhere. This event is created by clicking a button. I do not want the event to trigger upon clicking the button, only any subsequent clicks anywhere (including the button).

So say I've got some html like the following:

<body>
  <div id="someparent">
    <div id="btn"></div>
  </div>
</body>

And the following javascript (jquery):

$('#btn').click( function() {
  $(document).one('click', function() {
    console.log('triggered');
  });
});

$('#someparent').click(function() {
  // this must always be triggered
});

I want to avoid stopping event propagation, but in the above example, the event is bound to document, the event then bubbles up, and the event is triggered.

One way to fix this seems to be to wrap the event creation in a timeout:

$('#btn').click( function() {
  setTimeout(function() {
    $(document).one('click', function() {
      console.log('triggered');
    });
  }, 1);
});

$('#someparent').click(function() {
  // this must always be triggered
});

Now, this works fine, but I'm wondering whether this is safe. Does some notion of order of execution guarantee that this will always work, or is it just working by chance? I know there are other solutions (another nested .one() event for instance), but I'm specifically looking for an answer to how setTimeout and event propagation interoperates.

The following fiddle shows two divs. The first one has the wrong behaviour (the document event is triggered immediately). Clicking the second div, and then anywhere on document (white area) illustrates the wanted behaviour:

https://jsfiddle.net/Lwocuuf8/7/

like image 617
jkgeyti Avatar asked Apr 05 '16 09:04

jkgeyti


People also ask

What are the three phases of event propagation?

The standard DOM Events describes 3 phases of event propagation: Capturing phase – the event goes down to the element. Target phase – the event reached the target element. Bubbling phase – the event bubbles up from the element.

What is the difference between event stopPropagation and event stopImmediatePropagation?

stopPropagation allows other event handlers on the same element to be executed, while stopImmediatePropagation prevents this.

What is the difference between event capturing and bubbling?

With bubbling, the event is first captured and handled by the innermost element and then propagated to outer elements. With capturing, the event is first captured by the outermost element and propagated to the inner elements.

How do you stop event propagation?

To stop an event from further propagation in the capturing and bubbling phases, you call the Event. stopPropation() method in the event handler. Note that the event. stopPropagation() method doesn't stop any default behaviors of the element e.g., link click, checkbox checked.


2 Answers

Event bubbling means that, after an event triggers on the deepest possible element, it then triggers on parents in nesting order.

From: https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop#Adding_messages

Adding messages

In web browsers, messages are added any time an event occurs and there is an event listener attached to it. If there is no listener, the event is lost. So a click on an element with a click event handler will add a message--likewise with any other event.

Calling setTimeout will add a message to the queue after the time passed as second argument. If there is no other message in the queue, the message is processed right away; however, if there are messages, the setTimeout message will have to wait for other messages to be processed. For that reason the second argument indicates a minimum time and not a guaranteed time.

So, the behaviour you have, is not by chance, you are guaranteed that the messages in the queue will be processed before processing the message you add after a timeout.

like image 50
Andrea Casaccia Avatar answered Oct 22 '22 09:10

Andrea Casaccia


Here is a workaround by using a flag instead of a second event :

var documentWaitingClick = false;
$(function() {
  $(document).on('click', function(e) {
    if (documentWaitingClick) {
      //simulate the "one"
      documentWaitingClick = false;
      console.log('document click');
    } else if (e.target.tagName === 'BUTTON') {
      documentWaitingClick = true;
      console.log('button click')
    }
  });
});
like image 25
TecHunter Avatar answered Oct 22 '22 09:10

TecHunter