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

ctrmrzij  于 2023-04-04  发布在  Java
关注(0)|答案(3)|浏览(157)

我正在创建一个复杂的项目:一个Youtube UI克隆,我有一个JS的问题

理念

其思想是:在滚动条上会自动创建更多的div,类似于第一个div(工作正常)

问题

但是当我想为click放置一个事件侦听器时,JavaScript并不处理后续div上的事件,但实际上,会自动生成50 ~ 100个其他div(具有相同的类),所以我认为.length有问题

这里是一个网站,可以让你了解这个问题:

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,它会执行click函数,但是生成的div不能执行click。我不知道是DOM没有更新还是我应该写更多的东西?在互联网上没有解决这个问题的结果(我希望能找到有经验的人,谁可以帮助我在这里)

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

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

j7dteeu8

j7dteeu81#

您的问题是,您在页面加载时初始添加事件侦听器。这意味着添加事件侦听器的for循环将仅循环页面初始加载时存在的video-container元素。添加事件侦听器后创建的任何video-container元素都不会附加事件侦听器。即使你在一个元素上使用.cloneNode(),这个元素有一个附加了addEventListener()的事件监听器,这个事件监听器也不会被复制到新的元素上,因为addEventListener()事件处理程序不会被复制。
一个想法是在每次调用createVideo()时为videoComponent添加一个新的事件侦听器。这样做的问题是,这可能会导致您向页面添加 * 许多 * 事件侦听器,这可能会降低网页的性能。
更好的方法是使用event delegation。(在本例中为<main>元素)。当嵌套在父<main>元素下的video-container元素之一被单击时,单击事件将“冒泡”到<main>元素,触发父<main>元素上的click事件监听器。在附加到父<main>元素的事件监听器中,可以使用e.target获取对最初单击的video-container元素的引用,其中e是事件侦听器回调函数中提供的Event对象。由于单击事件侦听器位于父main容器上,因此任何新创建的子元素的单击事件侦听器都将工作,因为来自子元素的事件将冒泡到父主容器的事件处理程序。
要在代码中实现事件委托,您可以删除添加单个事件侦听器的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`
  }
});

这里我们还检查了clicked元素(e.target)实际上是一个视频容器元素,通过查看它是否是matches()选择器.video-container。这是必要的,因为我们的mainContainer事件侦听器可以触发任何从它的任何子元素、孙元素等冒泡到它的点击事件。
正如@Dai的评论中所指出的,在添加滚动事件侦听器时,您可能还需要考虑使用passive选项来提高性能。

tpgth1q7

tpgth1q72#

简介

当你调用addEventListener时,只有已经存在的标签会接收到这个事件。未来的标签如果还不存在,就不会有这个事件。你需要以某种方式保证未来的元素也会有这个事件。你可以使用多种方法来实现这一点。

onclick

保证click事件得到处理的一个非常简单的方法是为标记指定一个onclick属性,如
onclick="yourfunction
示例:

function foo(element) {
    alert([...element.parentNode.children].indexOf(element));
}

function addDiv() {
    let container = document.getElementById("container");
    container.innerHTML += `
        <p onclick="foo(this);">${container.children.length}</p>
    `;
};
<input type="button" onclick="addDiv()" value="Click Here!">
<div id="container">
</div>

动态添加事件监听器

还可以以某种方式标记已经被初始化的项,并且随后仅将事件添加到尚未初始化的项,并且随后初始化它们。
示例:
一个二个一个一个

为HTML节点添加事件监听器

你可以直接调用videoComponent.addEventListener
示例:

function foo(element) {
    alert([...element.parentNode.children].indexOf(element));
}

function addDiv() {
    let container = document.getElementById("container");
    let newNode = container.children[0].cloneNode(true);
    newNode.innerText = container.children.length;
    container.appendChild(newNode);
    newNode.addEventListener("click", function() {
        foo(this);
    });
};

document.getElementById("container").children[0].addEventListener("click", function() {
    foo(this);
});
<input type="button" onclick="addDiv()" value="Click Here!">
<div id="container">
    <p>0</p>
</div>

突变观察器

图片来源:Mutation Observer for creating new elements
你可以使用Mutation Observer来检测标签的创建并将事件附加到标签上。如果可能的话,应该避免这样做,通常你有一个更简单的解决方案,但是,为了完整起见,这里有一个从上面分享的链接中提取的例子:

MutationObserver = window.MutationObserver || window.WebKitMutationObserver;

$("#foo").live("click",function(e) {
    e.preventDefault();
    $(this).append($("<div />").html("new div").attr("id","bar"));
});

// define a new observer
var obs = new MutationObserver(function(mutations, observer) {
    // look through all mutations that just occured
    for(var i=0; i<mutations.length; ++i) {
        // look through all added nodes of this mutation
        for(var j=0; j<mutations[i].addedNodes.length; ++j) {
            // was a child added with ID of 'bar'?
            if(mutations[i].addedNodes[j].id == "bar") {
                console.log("bar was added!");
            }
        }
    }
});

// have the observer observe foo for changes in children
obs.observe($("#foo").get(0), {
  childList: true
});

事件委托

最后,但并非最不重要的是,正如其他人已经提到的,您可以将事件委托给父节点,但在这种情况下,您需要找出事件的发起者是什么,即事件对象的target字段。
示例:

function foo(element) {
    alert([...element.parentNode.children].indexOf(element));
}

function addDiv() {
    let container = document.getElementById("container");
    container.innerHTML += `
        <p>${container.children.length}</p>
    `;
};

document.getElementById("container").addEventListener("click", function(event) {
    foo(event.target);
});
<input type="button" onclick="addDiv()" value="Click Here!">
<div id="container">
</div>
dtcbnfnu

dtcbnfnu3#

除了@Nick的回答之外,是的,可以用vanilla JS来做,但是如果JQuery可用,它会变得超级简单,可读性更强。

$(document).on('click','.video-container',function(e){
   //your desired actions
})

第一个参数是事件类型(点击,悬停等)。您可以使用任何传统的选择器作为第二个参数。

相关问题