mouseup、click 事件和 DOM 操作

mouseup, click events and DOM manipulations

堆垛机,

我有一个奇怪的案例。有一个容器元素,它包含一些带有 class "clickable" 的其他元素。 clickable 'click' 的事件侦听器使用委托附加到容器。

<input type="text"/>
<div class="container">
    <div class="clickable">Click</div>
</div>

$container.on('click', '.clickable', incrementCounter);

容器外还有一个输入元素。在 'change' 事件中,容器的内容在下一个事件循环回合中被替换为相同的内容(使用 $container.html())(现实点:-)。

$input.on('change', function() {
    setTimeout(function() {
        $container.html('<div class="clickable">Click</div>');
    }, 0);
});

现在情况:在输入字段中输入内容(不要单击任何地方或按 Enter)。然后马上点击clickable

发生了什么:'change' 事件导致容器在下一个事件循环轮次上重新渲染,并且永远不会生成 'click' 事件。

可以通过将 'click' 事件替换为 'mouseup' 事件来解决。

问题:为什么会有这样的差异?

参见plunk

您 运行 处于 blurclick 事件之间的竞争状态。
click 事件实际上是同一元素上的组合 mousedownmouseup 事件。

如果您更改输入然后单击您的容器,则您拥有的事件是:

mousedown (on row)
blur (on input)
mouse up (on row)

由于鼠标按下和鼠标弹起事件受到(模糊)干扰,您无法获得 click 事件。

我为相关事件添加了一个console.log,你可以查看这个片段:

$(function() {
  var rowHtml = '<div class="row">Click</div>';
  
  var $rowsContainer = $('.rows-container');
  var $counter = $('.counter');
  var $blinker = $('.blinker');
  var $input = $('input[type=text]');
  
  $input.on('change', blink);
  $rowsContainer.on('mousedown', function(e) {
    console.log('mousedown', e.target);
  });
  $rowsContainer.on('mouseup', function(e) {
    console.log('mouseup', e.target);
  });
  $rowsContainer.on('click', function(e) {
    console.log('click', e.target);
  });
  $input.on('blur', function(e) {
    console.log('blur', e.target);
  });
  
  $rowsContainer.on('click', '.row', incrementCounter);
  
  // this works as expected
  //$rowsContainer.on('mouseup', '.row', incrementCounter);
  
  function incrementCounter() {
    var oldValue = parseInt($counter.text(), 10) || 0;
    $counter.text(1 + oldValue);
  }
  
  function blink() {
    setTimeout(function() {
      $rowsContainer.html(rowHtml);
    }, 0)

    $blinker.html('☼');
    
    setTimeout(function() {
      $blinker.html('&nbsp;');
    }, 300);
  }
});
.counter,
.blinker {
  display: inline-block;
  width: 1.2em;
}
.rows-container {
  border: 1px dotted #ccc;
  margin: 1em;padding: 2em;
}
.row {
  padding: 1em;
  background-color: #eef;
}
p {
  font-size: 12px;
  line-height: 12px;
}
<script data-require="jquery@2.1.4" data-semver="2.1.4" src="https://code.jquery.com/jquery-2.1.4.js"></script>
<h1>Click vs. mouseup whith DOM manipulation</h1>
<p>On the input field change event a little sun shortly appears on the right
  and the content of the "Click" parent is redrawn with the identical content.
</p>
<p>Clicking on "Click" increments the counter. It works by delegation of the
  event handling to the "Click" container.
</p>
<p>Try to enter something in the text field and trigger the "change" event
  by clicking on "Click". Sun appears but counter is unchanged.</p>
<p>When listening to 'mouseup' event instead of the 'click' everything works.</p>

<h2>Why?</h2>
<span class="counter">0</span>
<span class="blinker">&nbsp;</span>
<input type="text" />
<div class="rows-container">
  <div class="row">Click</div>
</div>

您可以使用此示例查看 mousedown/mouseup/click 事件的具体工作原理。

  1. 点击容器(不离开鼠标),将鼠标拖到容器外,然后离开鼠标。您会看到在容器上有一个 mousedown,在其他元素(如 body)上有一个 mouseup,但没有 click 事件。
  2. 点击容器(不离开鼠标),将鼠标拖入容器内,然后离开鼠标。您会看到现在您 有一个 click 事件。