Issue
I am trying to create a complex project: a youtube UI clone, and I had a problem with js
the idea
the idea is on scroll create automatically more divs, similar to the first one (and that's work fine)
the problem
but when I want to put a eventListener of click, JavaScript thinks only one div in html,
but in reality, there are automatically generated 50 ~ 100 other divs (with the same class)
so I think is something wrong with .lenght
here the website for giving you a idea of the problem:
https://laaouatni.github.io/yt-clone/
here where I think is the problem:
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);
})
}
if I click the div that was written manually, it executes the click function, but with generated divs the click does not work. I don't know if the DOM doesn't update or should I write something more? on the internet there is no result for solving the problem (I hope to find someone with experience, who can help me here)
if you need, for all code github repo (to help us)
https://github.com/Laaouatni/yt-clone
Solution
Your issue is that you're adding your event listeners initially when the page loads. This means that your for
loop that adds your event listeners will only loop over the video-container
elements that are present on the initial load of your page. Any video-container
elements created after you've added your event listeners won't have an event listener attached to them. Even if you are using .cloneNode()
on an element that has an event listener attached with addEventListener()
, the event listener won't be copied across to the new element as addEventListener()
event handlers are not copied.
One idea is to add a new event listener to videoComponent
every time createVideo()
is called. The problem with this is that this can lead to you adding many event listeners to your page, which can slow down the performance of your webpage.
A better idea is to use event delegation. The idea of event delegation is that you can add your event listener to one parent element (in your case the <main>
element). When one of your video-container
elements nested under the parent <main>
element is clicked, the click event will "bubble" up to the <main>
element, triggering the click event listener on the parent <main>
element. Within the event listener attached to the parent <main>
element, you can obtain a reference to the originally clicked video-container
element using e.target
, where e
is the Event object provided in the event listeners callback function. As the click event listener is on the parent main
container, the click event listener for any newly created children elements will work, as the event from the child element will bubble up to the parent main container's event handler.
To achieve event delegation in your code, you can remove your for
loop that adds the individual event listenerrs and instead add your click event listener to your mainContainer
element. This will capture click events that bubble up to it when its children are clicked:
mainContainer.addEventListener("click", function(e) { // e = event object
if (e.target && e.target.matches(".video-container")) {
const clickedVideoContainer = e.target;
// do stuff with `clickedVideoContainer`
}
});
Here we're also checking that the clicked element (e.target
) is in fact a video-container element by seeing if it matches()
the selector .video-container
. This is needed as our mainContainer
event listener can trigger for any click events that bubble up to it from any of its children, grandchildren, etc.
As also noted in the comments by @Dai, you might also want to consider using the passive
option when adding your scroll event listener to improve performance.
Answered By - Nick Parsons
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.