[英]DOM - timing of simultaneous events vs setTimeout
假設我有一個包含多個子元素的元素,並且想在鼠標進入或離開容器時運行一些代碼。 如果我天真地寫:
var onHover = function (el, f) {
el.addEventListener('mouseover', function () {
f(true);
});
el.addEventListener('mouseout', function () {
f(false);
});
};
然后,在某些情況下,我將獲得所需的行為-取決於回調f
的性質。 但是,當鼠標在容器中從一個子對象移到另一個子對象時, f(false)
立即運行,后跟f(true)
。 我不希望發生這種情況-我只希望f
在鼠標整體進入或離開容器時運行,而不是稱為機槍風格,因為用戶將鼠標拖動到容器內部的元素上。
這是我想出的解決方案:
var onHover = function (el, f) {
var previousMouseover = false;
var receivedMouseover = false;
var pushing = false;
var pushEv = function () {
if (pushing) { return; }
pushing = true;
setTimeout(function () {
pushing = false;
if (previousMouseover !== receivedMouseover) {
f(receivedMouseover);
previousMouseover = receivedMouseover;
}
});
};
el.addEventListener('mouseover', function () {
receivedMouseover = true;
pushEv();
});
el.addEventListener('mouseout', function () {
receivedMouseover = false;
pushEv();
});
};
與第一個解決方案一樣,該解決方案通過在mouseover
事件發生之前發送mouseout
事件來假定並起作用。 我也想知道這是否是正式通過任何W3C文件規定,但不是這個問題的話題,甚至如果不是的話,它會很容易通過設置寫功能的算法,盡管該兩個獨立的變量,說receivedMouseover
和receivedMouseout
所述的內部mouseover
和mouseout
回調,這兩者都是則的內部檢查setTimeout
回調。
問題是:是否需要在運行由任何一個事件注冊的任何setTimeout
回調之前都處理mouseover
和mouseout
事件?
使用mouseenter
和mouseleave
事件,而不是mouseover
和mouseout
事件。
由於您已將事件偵聽器附加到父元素,因此可以在執行操作之前將事件來源( event.target
)與父元素( this
或event.currentTarget
)進行比較。 您可以執行以下操作;
var onHover = function (el, f) {
el.addEventListener('mouseover', function (evt) {
this === evt.target && f(true);
});
el.addEventListener('mouseout', function (evt) {
this === evt.target && f(false);
});
};
大多數元素都會冒泡,因此在某些時候這可能是完成此工作的正確方法。
編輯:如注釋中所述,在某些情況下,例如,當父元素未定義填充或邊距且子元素覆蓋所有父元素時, mouseover
和mouseout
事件可能會出現問題。 即使沒有,鼠標的速度也可能足夠快,以致JS引擎無法在父元素上對鼠標進行采樣。 這個事實在本文中得到了很好的解釋 。
因此,如接受的答案中所述,我想使用mouseenter
和mouseleave
事件可以解決此問題。 因此正確的代碼應該像;
var onHover = function (el, f) {
el.addEventListener('mouseenter', () => f(true));
el.addEventListener('mouseleave', () => f(false));
};
編輯2:好吧...實際上,在這種特定情況下,有一種使用mouseover
和mouseout
的安全方法。 這是關於在子級上使用CSS pointer-events
屬性,該屬性使它們無法為鼠標活動發出事件。
var container = document.getElementById('container'); container.addEventListener('mouseover', function (ev) { console.log(container === ev.target); }); container.addEventListener('mouseout', function (ev) { console.log(container === ev.target); });
#container * { pointer-events: none }
<div id="container"> <div> <span>text</span> </div> </div>
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.