代码之家  ›  专栏  ›  技术社区  ›  jcarpenter2

DOM-同步事件的计时与setTimeout

  •  0
  • jcarpenter2  · 技术社区  · 7 年前

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

    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文档正式指定,但这不是这个问题的主题,即使不是这样,通过设置两个单独的变量(例如 receivedMouseover receivedMouseout 的内部 鼠标悬停 灭鼠器 回调,然后在 setTimeout 回调。

    问题是:是否要求 鼠标悬停 灭鼠器 要处理的事件 之前 任何 设置超时 是否运行由任一事件注册的回调?

    2 回复  |  直到 7 年前
        1
  •  1
  •   Matti Virkkunen    7 年前

    使用 mouseenter mouseleave 事件而不是 mouseover mouseout

        2
  •  0
  •   Redu    7 年前

    由于已将事件侦听器附加到父元素,因此可以比较事件源( 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引擎无法在父元素上对鼠标进行采样。 This fact is beautifuly explained in this article

    因此,正如公认的答案所述,我认为 mouseenter mouseleave 有很多事件可以解决这个问题。相应地,正确的代码应该是这样的;

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

    编辑2: 好实际上有一种安全的使用方法 鼠标悬停 灭鼠器 在这种特殊情况下。这是关于使用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>