繁体   English   中英

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

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

我想在JavaScript中将侦听器添加到动态创建的元素中,但是它似乎不起作用。 它不会引发任何错误,所以我不知道该从何开始。 你有什么想法?

 { 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> 

核心问题是由于req()函数是异步的-意味着req()函数已被调用,但将来会在某个未知的时刻完成。 在每个req()等待完成脚本时,继续使用.header__flipper选择器调用addListeners()函数-但由于异步行为,尚未创建.header__flipper元素,因此未添加事件侦听器。

作为一个演示,我已经向addListeners()函数添加了超时,因此它在被调用之前会等待1秒钟。 这使req()函数有时间完成,并允许事件侦听器正确连接。

但是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> 

解决方案是将事件侦听器附加到父选择器(如@Nishad所建议)。 这个想法是将click事件侦听器附加到父元素(如#container ),然后在侦听器回调函数中检查事件目标是否为新的动态元素之一。

在您的情况下,在按钮内添加<span class="header__flipper__aria" aria-hidden="true">&rarr;</span>会使事情变得有些复杂,因为事件目标可能是<button><span> 这要求我们检查事件目标是否是那些元素之一。

 { 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> 

替代解决方案

一种替代方法是在创建动态元素时将事件侦听器附加到回调中每个动态元素内的按钮,如下所示:

 { 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> 

问题是您在fetch请求完成之前添加了事件侦听器。 调用addListeners时,触发器尚未在DOM中。

我已经修改了req方法,以返回fetch的承诺。 使用Promise.all ,所有代码将一直等到完成所有三个提取。 这仍然不能完全解决问题,代码知道何时完成提取,但是与将cityTemplate添加到DOM的req方法不同。

有两种解决方案:

  1. Promise.all处理程序中使用setTimeout 这很可能会延迟将事件侦听器添加足够长的时间,以至于无法将模板添加到DOM中。 我添加了一些console.log语句,这些语句将显示Promise.all日志行出现在最后一个呈现的日志行之前。
  2. req方法返回您自己创建的承诺,而不是fetch承诺。 cityTemplate添加到DOM后,解决自行创建的cityTemplate 这样,您可以确定Promise.all在DOM中的所有内容之前都不会实现。

解决方案1并不是非常可靠的解决方案,应避免使用。 解决方案2提供所需的控制类型。 我的答案显示了解决方案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> 

您需要使用以下代码段触发动态创建的元素的点击事件

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

对于JavaScript解决方案,您可以参考以下内容: 向动态添加的按钮添加onclick事件?

暂无
暂无

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

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