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



我正试图创建一个复杂的项目:Youtube UI克隆,但JS 出现了问题

这个想法

其想法是:在滚动时,应该自动创建更多的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

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

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

一个更好的想法是使用事件委派。事件委派的思想是,您可以将事件侦听器添加到一个父元素(在您的情况下是<main>元素)。当嵌套在父<main>元素下的video-container元素之一被单击时;气泡;直到<main>元素,触发父<main>元素上的点击事件监听器。在附加到父<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`
}
});

在这里,我们还通过查看被点击的元素(e.target)是否是选择器.video-containermatches()来检查它实际上是视频容器元素。这是必要的,因为我们的mainContainer事件侦听器可以触发任何子代、孙代等弹出的点击事件。

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

简介

当您调用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>

动态添加事件侦听器

也可以以某种方式标记已经初始化的项,然后将事件仅添加到尚未初始化的项中,然后对其进行初始化。

示例:

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>
`;

for (let item of document.querySelectorAll(".container > div:not(.initialized)")) {
item.addEventListener("click", () => {
foo(this);
this.classList.add("initialized");
});
}
};
<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>

突变观察者

由突变观察者创建新元素提供

你可以使用突变观察者来检测标签的创建并将事件附加到它上。如果可能的话,应该避免这种情况,通常你有一个更简单的解决方案,但为了完整性,这里有一个来自上面共享的链接的例子:

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>

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

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

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

最新更新