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。
您 运行 处于 blur
和 click
事件之间的竞争状态。
click
事件实际上是同一元素上的组合 mousedown
和 mouseup
事件。
如果您更改输入然后单击您的容器,则您拥有的事件是:
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(' ');
}, 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"> </span>
<input type="text" />
<div class="rows-container">
<div class="row">Click</div>
</div>
您可以使用此示例查看 mousedown/mouseup/click 事件的具体工作原理。
- 点击容器(不离开鼠标),将鼠标拖到容器外,然后离开鼠标。您会看到在容器上有一个
mousedown
,在其他元素(如 body
)上有一个 mouseup
,但没有 click
事件。
- 点击容器(不离开鼠标),将鼠标拖入容器内,然后离开鼠标。您会看到现在您 有一个
click
事件。
堆垛机,
我有一个奇怪的案例。有一个容器元素,它包含一些带有 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。
您 运行 处于 blur
和 click
事件之间的竞争状态。
click
事件实际上是同一元素上的组合 mousedown
和 mouseup
事件。
如果您更改输入然后单击您的容器,则您拥有的事件是:
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(' ');
}, 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"> </span>
<input type="text" />
<div class="rows-container">
<div class="row">Click</div>
</div>
您可以使用此示例查看 mousedown/mouseup/click 事件的具体工作原理。
- 点击容器(不离开鼠标),将鼠标拖到容器外,然后离开鼠标。您会看到在容器上有一个
mousedown
,在其他元素(如body
)上有一个mouseup
,但没有click
事件。 - 点击容器(不离开鼠标),将鼠标拖入容器内,然后离开鼠标。您会看到现在您 有一个
click
事件。