简体   繁体   English

如何向动态创建的元素添加类

[英]How to add a class to dynamically created element

I want to add listener to dynamically created element in JavaScript but It doesn't seem to work. 我想在JavaScript中将侦听器添加到动态创建的元素中,但是它似乎不起作用。 It throws no error so I have no idea on what i have to begin with. 它不会引发任何错误,所以我不知道该从何开始。 Do you have any ideas? 你有什么想法?

 { const API_KEY = "9829fe5eab6c963bbe4850df2d7de4aa"; const container = document.getElementById("container"); const flippers = document.getElementsByClassName("header__flipper"); const cityTemplate = () => { const template = `<section class="weather"> <button class="header__flipper"><span aria-hidden="true">&rarr;</span></button> <header class="header"> <h1 class="header__heading">dfgdfgd </h1> </header> </section>`; return template; }; const addListeners = (collection, ev, fn) => { for (let i = 0; i < collection.length; i++) { collection[i].addEventListener(ev, fn, false); } } const req = (id, key) => { const url = `https://api.openweathermap.org/data/2.5/forecast?id=${id}&APPID=${key}`; fetch(url).then((res) => { res.json().then((data) => { container.innerHTML += cityTemplate(data); }); }) } req("6695624", API_KEY); req("6695624", API_KEY); req("6695624", API_KEY); addListeners(flippers, "click", () => { alert("test"); }) } 
 <div id="container"></div> 

The core issue is due to the req() function being asynchronous - meaning the req() function is called but it finishes at some unknown point in the future. 核心问题是由于req()函数是异步的-意味着req()函数已被调用,但将来会在某个未知的时刻完成。 While each req() is waiting to finish the script continues and the addListeners() function is called, using the .header__flipper selector - but due to the asynchronous behavior the .header__flipper elements aren't created yet so the event listeners aren't added. 在每个req()等待完成脚本时,继续使用.header__flipper选择器调用addListeners()函数-但由于异步行为,尚未创建.header__flipper元素,因此未添加事件侦听器。

As a demo, I've added a timeout to the addListeners() function so it waits 1 second before being called. 作为一个演示,我已经向addListeners()函数添加了超时,因此它在被调用之前会等待1秒钟。 This gives the req() functions time to complete and allows the event listeners to attach correctly. 这使req()函数有时间完成,并允许事件侦听器正确连接。

However - setTimeout() IS NOT the solution - the snippet below is only for demonstrating the issue, scroll down for the correct solution. 但是setTimeout()不是解决方案 -下面的代码段仅用于演示问题,请向下滚动以获取正确的解决方案。

 { const API_KEY = "9829fe5eab6c963bbe4850df2d7de4aa"; const container = document.getElementById("container"); const cityTemplate = () => { const template = `<section class="weather"> <button class="header__flipper"><span aria-hidden="true">&rarr;</span></button> <header class="header"> <h1 class="header__heading">dfgdfgd </h1> </header> </section>`; return template; }; const addListeners = (collection, ev, fn) => { for (let i = 0; i < Array.from(collection).length; i++) { collection[i].addEventListener(ev, fn, false); } } const req = (id, key) => { const url = `https://api.openweathermap.org/data/2.5/forecast?id=${id}&APPID=${key}`; fetch(url).then((res) => { res.json().then((data) => { container.innerHTML += cityTemplate(data); }); }) } req("6695624", API_KEY); req("6695624", API_KEY); req("6695624", API_KEY); // For Demo Only // The req() function is asynchronous so the addListeners() function was attempting to attach to the elements before they were created window.setTimeout(function() { addListeners(document.getElementsByClassName("header__flipper"), "click", () => { alert("test"); }) }, 1000) } 
 <div id="container"></div> 

Solution

The solution is to attach the event listeners to a parent selector (as @Nishad has recommended). 解决方案是将事件侦听器附加到父选择器(如@Nishad所建议)。 The idea is to attach the click event listener to a parent element (like #container ), and within the listener callback function check if the event target is one of the new dynamic elements. 这个想法是将click事件侦听器附加到父元素(如#container ),然后在侦听器回调函数中检查事件目标是否为新的动态元素之一。

In your case, the addition of the <span class="header__flipper__aria" aria-hidden="true">&rarr;</span> within the button complicates things a bit because the event target could be either the <button> or the <span> . 在您的情况下,在按钮内添加<span class="header__flipper__aria" aria-hidden="true">&rarr;</span>会使事情变得有些复杂,因为事件目标可能是<button><span> This requires us to check if the event target is either of those elements. 这要求我们检查事件目标是否是那些元素之一。

 { const API_KEY = "9829fe5eab6c963bbe4850df2d7de4aa"; const container = document.getElementById("container"); const cityTemplate = () => { const template = `<section class="weather"> <button class="header__flipper"><span class="header__flipper__aria" aria-hidden="true">&rarr;</span></button> <header class="header"> <h1 class="header__heading">dfgdfgd </h1> </header> </section>`; return template; }; const addListeners = (collection, ev, fn) => { collection.addEventListener(ev, fn, false); } const req = (id, key) => { const url = `https://api.openweathermap.org/data/2.5/forecast?id=${id}&APPID=${key}`; fetch(url).then((res) => { res.json().then((data) => { container.innerHTML += cityTemplate(data); }); }) } req("6695624", API_KEY); req("6695624", API_KEY); req("6695624", API_KEY); addListeners(document.getElementById("container"), "click", (event) => { var classes = event.target.classList; if (classes.contains("header__flipper") || classes.contains("header__flipper__aria")) { alert("test"); } }) } 
 <div id="container"></div> 

Alternative Solution 替代解决方案

An alternative would be to attach the event listener to the button within each dynamic element in the callback when the dynamic element is created, like this: 一种替代方法是在创建动态元素时将事件侦听器附加到回调中每个动态元素内的按钮,如下所示:

 { const API_KEY = "9829fe5eab6c963bbe4850df2d7de4aa"; const container = document.getElementById("container"); const cityTemplate = () => { const newEl = document.createElement("section"); newEl.classList.add("weather"); const template = `<button class="header__flipper"><span class="header__flipper__aria" aria-hidden="true">&rarr;</span></button> <header class="header"> <h1 class="header__heading">dfgdfgd </h1> </header>`; newEl.innerHTML = template; return newEl; }; const req = (id, key) => { const url = `https://api.openweathermap.org/data/2.5/forecast?id=${id}&APPID=${key}`; fetch(url).then((res) => { res.json().then((data) => { const city = cityTemplate(data); city.querySelector("button").addEventListener("click", function(){ alert("test"); }, false); container.appendChild(city); }); }) } req("6695624", API_KEY); req("6695624", API_KEY); req("6695624", API_KEY); } 
 <div id="container"></div> 

The problem is that you add the event listeners before the fetch requests have completed. 问题是您在fetch请求完成之前添加了事件侦听器。 The triggers aren't yet in the DOM at the time you call addListeners . 调用addListeners时,触发器尚未在DOM中。

I've modified the req method to return the promise for the fetch . 我已经修改了req方法,以返回fetch的承诺。 With a Promise.all the code will wait till all three fetches are done. 使用Promise.all ,所有代码将一直等到完成所有三个提取。 This still doesn't fully solve the problem, the code knows when the fetches are done but that is not the same as the req method having added the cityTemplate to the DOM. 这仍然不能完全解决问题,代码知道何时完成提取,但是与将cityTemplate添加到DOM的req方法不同。

Two solutions come to mind: 有两种解决方案:

  1. Use a setTimeout in the Promise.all handler. Promise.all处理程序中使用setTimeout This will most likely delay adding the event listeners long enough for the templates to be added in the DOM. 这很可能会延迟将事件侦听器添加足够长的时间,以至于无法将模板添加到DOM中。 I've added some console.log statements that will show the Promise.all log line appears before the last rendered log line. 我添加了一些console.log语句,这些语句将显示Promise.all日志行出现在最后一个呈现的日志行之前。
  2. Have the req method return a promise that you create yourself, instead of the fetch promise. req方法返回您自己创建的承诺,而不是fetch承诺。 Resolve the self created promise after the cityTemplate has been added to the DOM. cityTemplate添加到DOM后,解决自行创建的cityTemplate This way you know for sure that the Promise.all won't get fulfilled until everything is in the DOM. 这样,您可以确定Promise.all在DOM中的所有内容之前都不会实现。

Solution 1 is not a very robust solution and should be avoided. 解决方案1并不是非常可靠的解决方案,应避免使用。 Solution 2 provides the kind of control you want. 解决方案2提供所需的控制类型。 My answer show a basic setup for solution 2, it doesn't do any kind of error handling. 我的答案显示了解决方案2的基本设置,它不执行任何类型的错误处理。

 { const API_KEY = "9829fe5eab6c963bbe4850df2d7de4aa"; const container = document.getElementById("container"); const flippers = document.getElementsByClassName("header__flipper"); const cityTemplate = () => { const template = `<section class="weather"> <button class="header__flipper"><span aria-hidden="true">&rarr;</span></button> <header class="header"> <h1 class="header__heading">dfgdfgd </h1> </header> </section>`; return template; }; const addListeners = (collection, ev, fn) => { for (let i = 0; i < collection.length; i++) { collection[i].addEventListener(ev, fn, false); } } const req = (id, key) => { console.log(`getting ${id}`); // Return a new promise, this promise will be fulfilled once the template // has been added with the retrieved data. return new Promise(resolve => { const url = `https://api.openweathermap.org/data/2.5/forecast?id=${id}&APPID=${key}`; // Get the data fetch(url).then((res) => { res.json().then((data) => { // Add the template to the DOM container.innerHTML += cityTemplate(data); console.log(`rendered ${id}`); // Relove that promise that was returned by the method. resolve(); }); }) }); } // Wait for all three promises to be done. These promises will be fulfilled after // the DOM has been updated. Promise.all([req("6695624", API_KEY), req("6695624", API_KEY), req("6695624", API_KEY)]) .then(() => { console.log(`promise all done`); // There is no longer a need for a timeout, due to the change to the // req method. addListeners(flippers, "click", () => { alert("test"); }) }); } 
 <div id="container"></div> 

you need to use the below code snippet to trigger click event for dynamically created elements 您需要使用以下代码段触发动态创建的元素的点击事件

$(document).on("click", ".className", function(){
    alert('this works')
});

for JavaScript solution, you can refer this : adding onclick event to dynamically added button? 对于JavaScript解决方案,您可以参考以下内容: 向动态添加的按钮添加onclick事件?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM