如何对 Shadow DOM 事件进行去抖动/节流处理?

4

我正在内部给我的阴影元素附加事件处理程序(我没有将事件传播到用户),处理 dragover 事件。一切都很顺利,直到我尝试去除事件的抖动时。当我使用 setTimeout 时,事件会被更改,就好像它是在 (事件目标为) <my-element> 上触发的。

为了更清楚地说明这一点;<my-element> 有它自己的阴影 dom,其中包含一些元素。如果没有 setTimeout,事件会如预期地在阴影元素之一上触发。例如,在 <my-element> 的阴影 dom 中的一个 <li><button> 上。但一旦我尝试用 setTimeout 去除事件的抖动,事件目标就会更改为 <my-element>

所以我的问题是;如何/能否去除阴影 dom 事件的抖动?

HTML

<html>
    <head></head>
    <body>
       <my-element>
           #shadow-root (open)
               <ul>
                   <li draggable="true">
                        <button>Hi</button>
                   </li>
                   <li draggable="true">
                        <button>Hi</button>
                   </li>
               </ul>
              <ul class="drop-target">
              </ul>
       </my-element>
    </body>
</html>

Javascript:

    //...class code
    attachEventHandler(){
        let self = this;

        self.shadowElement.addEventListener('dragover', (function debouncedDragOverFactory(){
            let timeoutId = 0,
                evObj;

            return function debouncedDragOver(ev){
                evObj = ev; //Only tracking the last event
                if(!timeoutId){
                    timeoutId = setTimeout(function(){
                        self.onDragOver(evObj); //self is 'this' pointer of the class this code is in.
                                                //onDragOver handles the drag event, obvisouly. 
                        timeoutId = 0;
                    }, 100);
                }
            };
        })());
    }

[更新]
这里有一个 Plunker 示例

我在第二个事件处理程序中对 setTimeout 设置了1秒的延迟。因此,您将看到一堆'DIV'节点名称滚动,然后是'LIST-EXAMPLE'节点名称。

这些

位于shadowRoot中。

LIST-EXAMPLE 是用户在窗口范围内看到的事件。

为了避免事件变化,在第三个事件处理程序中,我复制了我想要的数据。


我相信 setTimeout() 默认绑定到 window,但我对这个黑暗世界不熟悉,不敢肯定是否是这个原因。 - zer00ne
那也是我的猜测。我将要尝试一下,看看在影子根上是否有不同的窗口上下文...可能没有。我想我只需从事件中获取所需的数据并传递即可。 - Jerinaw
没有。我希望在shadowRoot上有像iframe一样的contentWindow这样的东西。 - Jerinaw
也许 contentWindow 不是正确的视角。由于该函数来自 ShaDOM,因此请尝试使用 parent - zer00ne
是的,我试过了。最终你会到达文档,它不像shadowRoot的文档那样。 - Jerinaw
显示剩余2条评论
2个回答

2
所以我猜在我的更新中我回答了自己的问题。
这是一个 Plunker example
我在第二个事件处理程序中将 setTimeout 延迟了 1 秒。所以你会看到一堆“DIV”节点名称滚动,然后是“LIST-EXAMPLE”节点名称。
div 在 shadowRoot 中。
LIST-EXAMPLE 是用户在窗口范围内看到的事件。
为了避免事件改变,在第三个事件处理程序中,我复制了我想要的数据。
所以你可以像平常一样去防抖,但一定要复制你需要的事件数据,并在你的 worker 函数中使用该副本。
要用可能的解决方案更新我的代码...
//...class code
attachEventHandler(){
    let self = this;

    self.shadowElement.addEventListener('dragover', (function debouncedDragOverFactory(){
        let timeoutId = 0,
            evTarget;

        return function debouncedDragOver(ev){
            evTarget = ev.target; //Copy the data off that I need
            if(!timeoutId){
                timeoutId = setTimeout(function(){
                    self.onDragOver(evTarget);
                    timeoutId = 0;
                }, 100);
            }
        };
    })());
}

2
你可以将需要的数据作为setTimeout函数的第三个参数传递:
li.addEventListener('dragover', function(e){
    setTimeout(function(nodeName){
        output.textContent = '3' + nodeName + '\n' + output.textContent;    
    }, 2000, e.target.nodeName);
});

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接