如何为 JavaScript 生成的所有 div 放置一个 EventListener

Posted

技术标签:

【中文标题】如何为 JavaScript 生成的所有 div 放置一个 EventListener【英文标题】:how to put a EventListener for all divs generated by JavaScript 【发布时间】:2022-01-24 00:27:16 【问题描述】:

我正在尝试创建一个复杂的项目:一个 youtube UI 克隆,我遇到了 js 问题

想法

这个想法是在滚动上自动创建更多的 div,类似于第一个(这很好)

问题

但是当我想放一个click的eventListener时,javascript认为html中只有一个div, 但实际上,自动生成了50~100个其他div(同一个类) 所以我认为.lenght 有问题

这里是让您了解问题的网站:

https://laaouatni.github.io/yt-clone/

我认为问题出在这里:

function createVideo() 
    let videoComponent = videoContainer.cloneNode(true);
    mainContainer.appendChild(videoComponent);

...

    <main>
        <div class="video-container">

           ...

        </div>
        <!-- here javascript will put other divs on scroll -->
    </main>

...
window.addEventListener("scroll", function() 
   let scrollPercentage = (window.scrollY / (document.body.scrollHeight - window.innerHeight)) * 100;

    if (scrollPercentage > 70) 
        createVideo();
    

let videoContainer = document.querySelector(".video-container");
let allVideoContainer = document.querySelectorAll(".video-container");

for (let index = 0; index < allVideoContainer.length; index++) 
    allVideoContainer[index].addEventListener("click", function() 
        console.log("clicked video N " + index);
    )

如果我单击手动编写的 div,它会执行单击功能,但是对于生成的 div,单击不起作用。我不知道是 DOM 没有更新还是我应该写更多的东西?在网上没有解决问题的结果(我希望找到有经验的人,可以在这里帮助我)

如果您需要,所有代码 github repo(帮助我们)

https://github.com/Laaouatni/yt-clone

【问题讨论】:

“一个 youtube UI 克隆” - 为什么 您的 scroll 事件监听器必须是 passive @Dai passive 是什么意思? (我是初学者) 见***.com/questions/37721782/… 事件委托还可以解决动态创建的 div 没有附加事件侦听器的问题。 ***.com/questions/1687296/… 【参考方案1】:

您的问题是您最初是在页面加载时添加事件侦听器。这意味着添加事件侦听器的for 循环将仅遍历页面初始加载时出现的video-container 元素。添加事件侦听器后创建的任何video-container 元素都不会附加事件侦听器。即使您在带有addEventListener() 附加的事件侦听器的元素上使用.cloneNode(),也不会将事件侦听器复制到新元素,因为不会复制addEventListener() 事件处理程序。

一个想法是在每次调用createVideo() 时向videoComponent 添加一个新的事件侦听器。这样做的问题是,这可能会导致您向页面添加 许多 事件侦听器,这会降低网页的性能。

一个更好的主意是使用event delegation。事件委托的想法是您可以将事件侦听器添加到一个父元素(在您的情况下为 &lt;main&gt; 元素)。当您单击嵌套在父&lt;main&gt; 元素下的video-container 元素之一时,单击事件将"bubble" up 发送到&lt;main&gt; 元素,触发父&lt;main&gt; 元素上的单击事件侦听器。在附加到父&lt;main&gt;元素的事件监听器中,您可以使用e.target获取对最初点击的video-container元素的引用,其中e是事件监听器回调函数中提供的Event对象。由于 click 事件侦听器位于父 main 容器上,因此任何新创建的子元素的 click 事件侦听器都将起作用,因为来自子元素的事件将冒泡到父主容器的事件处理程序。

要在您的代码中实现事件委托,您可以删除添加单个事件侦听器的for 循环,而是将您的点击事件侦听器添加到您的mainContainer 元素中。这将捕获单击其子级时冒泡的单击事件:

mainContainer.addEventListener("click", function(e)  // e = event object
  if (e.target && e.target.matches(".video-container")) 
    const clickedVideoContainer = e.target;
    // do stuff with `clickedVideoContainer`
  
);

在这里,我们还检查被点击的元素 (e.target) 实际上是一个视频容器元素,方法是查看它是否为matches() 选择器.video-container。这是必需的,因为我们的 mainContainer 事件监听器可以触发任何从其子代、孙代等冒泡的点击事件。

正如@Dai 在 cmets 中所指出的,您可能还需要考虑在添加滚动事件侦听器时使用 passive option 以提高性能。

【讨论】:

以上是关于如何为 JavaScript 生成的所有 div 放置一个 EventListener的主要内容,如果未能解决你的问题,请参考以下文章

如何为所有浏览器垂直居中div?

如何为给定的javascript生成调用图? [关闭]

如何为多个动态 div 设置多个 Cookie

如何为链接创建onclick

如何为所有 SpecFlow 功能重新生成设计器代码

如何为包含所有存储行的现有 SQL Server 表生成 INSERT 脚本?