动态 HTML 上的事件绑定,使用 fetch 并在 forEach 内
Event binding on dynamic HTML, using fetch and within forEach
我正在尝试在下面显示的 HTML 元素上添加一个事件侦听器,它在 forEach 循环中动态生成。我最初从 JSON 占位符 API 得到了正确的响应,但是, none 我尝试将事件绑定到我在之后生成的任何 HTML 元素上当我尝试该事件时,最初的反应似乎产生了结果。
我要么根本没有 'Clicked',要么……addEventListener
不是函数,具体取决于我放置 addEventListener
代码的位置。
有没有办法以一种干净且可重复使用的方式实现这一点?
另外,这是通过 vanilla JS 生成动态 HTML 的首选方式吗?或者我应该手动生成它,例如使用 document.createElement()
和 appendChild()
?
最终,我的意思是将点击目标的 ID 传递给 getPosts
函数 fetch URL。
如有任何帮助,我们将不胜感激!
fetch('https://jsonplaceholder.typicode.com/users')
.then(res => res.json())
.then(data => createResult(data));
function createResult(data) {
const container = document.getElementById('result');
data.forEach((user) => {
const { id, name, email, address: { city, street } } = user;
let result =
`<div class="user" data-uid=${id}>
<h5 id="user-${id}"> User ID: ${id} </h5>
<ul class="w3-ul">
<li> User Full Name : ${name}</li>
<li> User Email : ${email} </li>
<li> User Address : ${city}, ${street} </li>
</ul>
</div>`;
container.innerHTML += result;
document.body.addEventListener("click", function (e) {
if (e.target &&
e.target.classList.contains("user")) {
console.log('Clicked');
}
});
});
}
function getPosts(e) {
fetch(`https://jsonplaceholder.typicode.com/users/${user.id}`)
.then(res => res.json())
.then(data => console.log(data));
}
<div id=result></div>
您应该使每个结果成为一个元素并仅向该元素添加事件侦听器
let element = document.createElement('div')
element.innerHTML = result
element = element.firstChild
element.addEventListener("click", function (e) {
console.log('Clicked', element.getAttribute('data-uid'));
});
container.appendChild(element);
可运行示例
fetch('https://jsonplaceholder.typicode.com/users')
.then(res => res.json())
.then(data => createResult(data));
function createResult(data) {
const container = document.getElementById('result');
data.forEach((user) => {
const { id, name, email, address: { city, street } } = user;
let result =
`<div class="user" data-uid=${id}>
<h5 id="user-${id}"> User ID: ${id} </h5>
<ul class="w3-ul">
<li> User Full Name : ${name}</li>
<li> User Email : ${email} </li>
<li> User Address : ${city}, ${street} </li>
</ul>
</div>`;
let element = document.createElement('div')
element.innerHTML = result
element = element.firstChild
element.addEventListener("click", function (e) {
console.log('Clicked', element.getAttribute('data-uid'));
});
container.appendChild(element);
});
}
function getPosts(e) {
fetch(`https://jsonplaceholder.typicode.com/users/${user.id}`)
.then(res => res.json())
.then(data => console.log(data));
}
<div id=result></div>
如果
应该工作
- 您将 eventListener 附加到 document.body
- 您将第三个参数添加到 addEventListener 调用,指定使用 CAPTURE 而不是 BUBBLING。
function createResult(data) {
const container = document.getElementById('result');
data.forEach((user) => {
const { id, name, email, address: { city, street } } = user;
let result =
`<div class="user" data-uid=${id}>
<h5 id="user-${id}"> User ID: ${id} </h5>
<ul class="w3-ul">
<li> User Full Name : ${name}</li>
<li> User Email : ${email} </li>
<li> User Address : ${city}, ${street} </li>
</ul>
</div>`;
container.innerHTML += result;
});
}
document.body.addEventListener("click", function (e) {
if (e.target &&
e.target.classList.contains("user")) {
console.log('Clicked element ' + e.target.data.uid);
}
}, false);
您的方法有两个问题。第一个问题是您为 data
.
中的每个元素将 click
处理程序添加到 document.body
如果您只调用 createResult()
一次,则移动 .addEventListener()
call before or after the .forEach()
container.addEventListener("click", ...);
data.forEach(...);
如果您多次调用 createResult()
然后移动 .addEventListener()
out of it and maybe call it on DOMContentLoaded
window.addEventListener("DOMContentLoaded", () => {
document.getElementById('result').addEventListener("click", ...);
});
第二个问题是e.target
。 e.target
是触发 click
事件的元素。这很可能不是 div.result
,而是 div.result
中的一个元素。你必须向上遍历DOM,直到找到div.result
。一种可能的方法是使用 Element.closest()
or a loop and Node.parentElement
.
fakeFetch().then(createResult);
function createResult(data) {
const container = document.getElementById('result');
container.addEventListener("click", function (e) {
const user = e.target.closest(".user");
if (user) {
getPosts(user.dataset.uid);
}
});
data.forEach((user) => {
const { id, name, email, address: { city, street } } = user;
let result =
`<div class="user" data-uid="${id}">
<h5 id="user-${id}"> User ID: ${id} </h5>
<ul class="w3-ul">
<li> User Full Name : ${name}</li>
<li> User Email : ${email} </li>
<li> User Address : ${city}, ${street} </li>
</ul>
</div>`;
container.innerHTML += result;
});
}
function getPosts(userId) {
console.log(userId);
}
function fakeFetch() {
return new Promise(resolve => {
const user = [
{ id: 1, name: "a", mail: "a@a.a", address: { city: "a", street: "a" } },
{ id: 2, name: "b", mail: "b@b.b", address: { city: "b", street: "b" } }
];
setTimeout(resolve(user), 1000);
})
}
<div id="result"></div>
作为第二个注释:
不要使用 .innerHTML
. This will overwrite the content of the element and therefor remove any event handlers. Use Element.insertAdjacentHTML()
代替。
container.innerHTML += result
然后会是
container.insertAdjacentHTML("beforeend", result);
我正在尝试在下面显示的 HTML 元素上添加一个事件侦听器,它在 forEach 循环中动态生成。我最初从 JSON 占位符 API 得到了正确的响应,但是, none 我尝试将事件绑定到我在之后生成的任何 HTML 元素上当我尝试该事件时,最初的反应似乎产生了结果。
我要么根本没有 'Clicked',要么……addEventListener
不是函数,具体取决于我放置 addEventListener
代码的位置。
有没有办法以一种干净且可重复使用的方式实现这一点?
另外,这是通过 vanilla JS 生成动态 HTML 的首选方式吗?或者我应该手动生成它,例如使用 document.createElement()
和 appendChild()
?
最终,我的意思是将点击目标的 ID 传递给 getPosts
函数 fetch URL。
如有任何帮助,我们将不胜感激!
fetch('https://jsonplaceholder.typicode.com/users')
.then(res => res.json())
.then(data => createResult(data));
function createResult(data) {
const container = document.getElementById('result');
data.forEach((user) => {
const { id, name, email, address: { city, street } } = user;
let result =
`<div class="user" data-uid=${id}>
<h5 id="user-${id}"> User ID: ${id} </h5>
<ul class="w3-ul">
<li> User Full Name : ${name}</li>
<li> User Email : ${email} </li>
<li> User Address : ${city}, ${street} </li>
</ul>
</div>`;
container.innerHTML += result;
document.body.addEventListener("click", function (e) {
if (e.target &&
e.target.classList.contains("user")) {
console.log('Clicked');
}
});
});
}
function getPosts(e) {
fetch(`https://jsonplaceholder.typicode.com/users/${user.id}`)
.then(res => res.json())
.then(data => console.log(data));
}
<div id=result></div>
您应该使每个结果成为一个元素并仅向该元素添加事件侦听器
let element = document.createElement('div')
element.innerHTML = result
element = element.firstChild
element.addEventListener("click", function (e) {
console.log('Clicked', element.getAttribute('data-uid'));
});
container.appendChild(element);
可运行示例
fetch('https://jsonplaceholder.typicode.com/users')
.then(res => res.json())
.then(data => createResult(data));
function createResult(data) {
const container = document.getElementById('result');
data.forEach((user) => {
const { id, name, email, address: { city, street } } = user;
let result =
`<div class="user" data-uid=${id}>
<h5 id="user-${id}"> User ID: ${id} </h5>
<ul class="w3-ul">
<li> User Full Name : ${name}</li>
<li> User Email : ${email} </li>
<li> User Address : ${city}, ${street} </li>
</ul>
</div>`;
let element = document.createElement('div')
element.innerHTML = result
element = element.firstChild
element.addEventListener("click", function (e) {
console.log('Clicked', element.getAttribute('data-uid'));
});
container.appendChild(element);
});
}
function getPosts(e) {
fetch(`https://jsonplaceholder.typicode.com/users/${user.id}`)
.then(res => res.json())
.then(data => console.log(data));
}
<div id=result></div>
如果
应该工作- 您将 eventListener 附加到 document.body
- 您将第三个参数添加到 addEventListener 调用,指定使用 CAPTURE 而不是 BUBBLING。
function createResult(data) {
const container = document.getElementById('result');
data.forEach((user) => {
const { id, name, email, address: { city, street } } = user;
let result =
`<div class="user" data-uid=${id}>
<h5 id="user-${id}"> User ID: ${id} </h5>
<ul class="w3-ul">
<li> User Full Name : ${name}</li>
<li> User Email : ${email} </li>
<li> User Address : ${city}, ${street} </li>
</ul>
</div>`;
container.innerHTML += result;
});
}
document.body.addEventListener("click", function (e) {
if (e.target &&
e.target.classList.contains("user")) {
console.log('Clicked element ' + e.target.data.uid);
}
}, false);
您的方法有两个问题。第一个问题是您为 data
.
click
处理程序添加到 document.body
如果您只调用 createResult()
一次,则移动 .addEventListener()
call before or after the .forEach()
container.addEventListener("click", ...);
data.forEach(...);
如果您多次调用 createResult()
然后移动 .addEventListener()
out of it and maybe call it on DOMContentLoaded
window.addEventListener("DOMContentLoaded", () => {
document.getElementById('result').addEventListener("click", ...);
});
第二个问题是e.target
。 e.target
是触发 click
事件的元素。这很可能不是 div.result
,而是 div.result
中的一个元素。你必须向上遍历DOM,直到找到div.result
。一种可能的方法是使用 Element.closest()
or a loop and Node.parentElement
.
fakeFetch().then(createResult);
function createResult(data) {
const container = document.getElementById('result');
container.addEventListener("click", function (e) {
const user = e.target.closest(".user");
if (user) {
getPosts(user.dataset.uid);
}
});
data.forEach((user) => {
const { id, name, email, address: { city, street } } = user;
let result =
`<div class="user" data-uid="${id}">
<h5 id="user-${id}"> User ID: ${id} </h5>
<ul class="w3-ul">
<li> User Full Name : ${name}</li>
<li> User Email : ${email} </li>
<li> User Address : ${city}, ${street} </li>
</ul>
</div>`;
container.innerHTML += result;
});
}
function getPosts(userId) {
console.log(userId);
}
function fakeFetch() {
return new Promise(resolve => {
const user = [
{ id: 1, name: "a", mail: "a@a.a", address: { city: "a", street: "a" } },
{ id: 2, name: "b", mail: "b@b.b", address: { city: "b", street: "b" } }
];
setTimeout(resolve(user), 1000);
})
}
<div id="result"></div>
作为第二个注释:
不要使用 .innerHTML
. This will overwrite the content of the element and therefor remove any event handlers. Use Element.insertAdjacentHTML()
代替。
container.innerHTML += result
然后会是
container.insertAdjacentHTML("beforeend", result);