setTimeout和事件传播

时间:2016-04-05 09:16:16

标签: javascript jquery events

我想创建一个通过点击任意位置触发的一次性事件。单击按钮即可创建此事件。我不希望在点击按钮时触发事件,只有任何后续点击(包括按钮)。

所以说我有一些类似下面的HTML:

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

以下javascript(jquery):

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

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

我想避免停止事件传播,但在上面的示例中,事件绑定到文档,然后事件冒泡,事件被触发。

解决此问题的一种方法似乎是将事件创建包装在超时中:

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

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

现在,这很好用,但我想知道这是否安全。一些执行顺序的概念是否保证这一点总是有效,或者只是偶然的工作?我知道还有其他解决方案(例如另一个嵌套的.one()事件),但我特意寻找setTimeout和事件传播如何互操作的答案。

以下小提琴显示两个div。第一个行为错误(文档事件立即触发)。单击第二个div,然后在文档(白色区域)上的任何位置说明所需行为:

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

3 个答案:

答案 0 :(得分:8)

事件冒泡意味着,在事件触发最深可能元素后,它会以嵌套顺序触发父项。

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

  

添加消息

     

在Web浏览器中,只要事件发生,就会添加消息   是附加到它的事件监听器。如果没有听众,那么   事件丢失了。因此,单击具有单击事件处理程序的元素   将添加一条消息 - 与任何其他事件一样。

     

调用setTimeout将在时间之后向队列添加消息   作为第二个参数传递。如果队列中没有其他消息,   消息立即处理;但是,如果有消息,   setTimeout消息必须等待其他消息   处理。因此,第二个参数表示最小值   时间而不是保证时间。

因此,您所拥有的行为不是偶然的,您可以保证在处理您在超时后添加的消息之前,处理队列中的消息。

答案 1 :(得分:2)

我对你想要的东西的理解:点击&#34;激活&#34;按钮,页面上任何位置的下一次单击(包括&#34;激活&#34;按钮)都应触发特殊事件。使用标志很容易处理:

var activationFlag = false;

$('#btn').click(function(event){
  event.preventDefault();

  if(!activationFlag) {
    event.stopPropagation();
    console.log('Global event activated');
  }

  activationFlag = true;
});

$('#someparent').click(function(event){
  if(activationFlag) {
    console.log('Time for a special event');
  } else {
    event.preventDefault();
    event.stopPropagation();
  }
});

答案 2 :(得分:2)

Here is a workaround使用标志而不是第二个事件:

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')
    }
  });
});