最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

html - How to put a EventListener for all divs generated by JavaScript - Stack Overflow

programmeradmin8浏览0评论

I am trying to create a plex project: a Youtube UI clone, and I had a problem with JS

the idea

the idea is: on scroll more divs should be created automatically, similar to the first one (and that's working fine)

the problem

but when I want to put an event listener for click, JavaScript does not handle the events on subsequent divs, but in reality, there are automatically generated 50 ~ 100 other divs (with the same class) so I think there is something wrong with .length

here the website for giving you a idea of the problem:

/

here where I think the problem is:

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 the 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)

I am trying to create a plex project: a Youtube UI clone, and I had a problem with JS

the idea

the idea is: on scroll more divs should be created automatically, similar to the first one (and that's working fine)

the problem

but when I want to put an event listener for click, JavaScript does not handle the events on subsequent divs, but in reality, there are automatically generated 50 ~ 100 other divs (with the same class) so I think there is something wrong with .length

here the website for giving you a idea of the problem:

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

here where I think the problem is:

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 the 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./Laaouatni/yt-clone

Share Improve this question edited Aug 28, 2022 at 10:29 Lajos Arpad 77.4k40 gold badges117 silver badges222 bronze badges asked Dec 22, 2021 at 20:37 Laaouatni AnasLaaouatni Anas 3,9352 gold badges13 silver badges34 bronze badges 5
  • "a youtube UI clone" - why? – Dai Commented Dec 22, 2021 at 20:39
  • 1 Your scroll event-listener needs to be passive. – Dai Commented Dec 22, 2021 at 20:40
  • @Dai what is mean passive ? (i am a begginer) – Laaouatni Anas Commented Dec 22, 2021 at 20:41
  • See stackoverflow./questions/37721782/… – Dai Commented Dec 22, 2021 at 20:55
  • event delegation can also solve the problem where your dynamically created divs don't have an event listener attached to them. stackoverflow./questions/1687296/… – Andrew L Commented Dec 22, 2021 at 21:17
Add a ment  | 

3 Answers 3

Reset to default 4 +50

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 ments by @Dai, you might also want to consider using the passive option when adding your scroll event listener to improve performance.

Introduction

When you call addEventListener, only the tags that exist receive the event. Future tags that do not exist yet will not have that event. You need to somehow guarantee that future elements also have the event. There are multiple ways that you can use to achieve this.

onclick

One very simple way to guarantee that your click event will be handled is to specify an onclick attribute to the tag, like

onclick="yourfunction

Example:

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>

Adding event listeners on the fly

It is also possible to somehow mark the items that already have been initialized and subsequently add the event only to items that are not yet initialized and subsequently initialize them.

Example:

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>

Adding an event listener to an HTML node

You can simply call videoComponent.addEventListener.

Example:

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

Courtesy to Mutation Observer for creating new elements

You can use Mutation Observer to detect tag creation and attach events to it. It should be avoided if possible, more often than not you have a simpler solution, but, for the sake of pleteness, here's an example taken from the link shared above:

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
});

Event delegation

Last, but not least, as others already mentioned, you can delegate your event to the parent node, but in that case you will need to find out what the originator of the event was, which is the event object's target field.

Example:

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>

In addition to @Nick's answer, yes it is possible to do it with vanilla JS but if JQuery is available it makes it super simple and far more readable.

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

The first parameter is event type (click, hover etc.). You may use any conventional selector for the second parameter.

发布评论

评论列表(0)

  1. 暂无评论