DOM - 同步事件的时间与setTimeout

时间:2018-04-26 23:29:49

标签: javascript dom language-lawyer dom-events event-loop

假设我有一个包含多个子元素的元素,并希望在鼠标进入或离开容器时运行一些代码。如果我天真地写道:

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

此解决方案与第一个解决方案一样,假设mouseout事件在mouseover事件发生之前发送,并且工作正常。我还想知道 是否由任何W3C文档正式指定,但这不是这个问题的主题,即使不是这样,也很容易编写一个函数算法尽管如此,通过设置两个单独的变量,在receivedMouseoverreceivedMouseout回调中说mouseovermouseout,然后在{{{}}内部检查这两个变量1}}回调。

问题是:在运行任何一个事件的任何setTimeout回调之前,是否需要同时处理mouseovermouseout个事件

2 个答案:

答案 0 :(得分:1)

使用mouseentermouseleave个事件代替mouseovermouseout

答案 1 :(得分:0)

由于您已将事件侦听器附加到父元素,因此您可以在执行操作之前将事件源(event.target)与父元素(thisevent.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);
    });
};

大多数元素在某些时候起泡,这可能是完成这项工作的正确方法。

修改如评论中所述,mouseovermouseout事件在某些情况下可能会出现问题,例如父元素没有定义填充或边距以及子元素覆盖所有父母。即使他们没有速度,鼠标的速度也足够快,以至于JS引擎无法在父元素上采样鼠标。 This fact is beautifuly explained in this article

因此,正如接受的答案所述,我认为mouseentermouseleave事件可以解决这个问题。因此,正确的代码应该是;

var onHover = function (el, f) {
    el.addEventListener('mouseenter', () => f(true));
    el.addEventListener('mouseleave', () => f(false));
};

编辑2:嗯......实际上,在这种特殊情况下使用mouseovermouseout是一种安全的方法。它是关于在子项上使用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>
&#13;
&#13;
&#13;