JavaScript:将元素添加到DOM并继承事件处理程序 [英] JavaScript: Prepend element to DOM and inherit event handler
问题描述
我在< textarea>
和一个< button>
><形式> 。提交后,我调用 e.preventDefault()
并通过 AJAX
提交表单。从那里我返回查询和 PREPEND
,在此列表顶部的< div>
内的信息。
I have a <textarea>
and a <button>
inside of a <form>
. When submitted, I call e.preventDefault()
and submit the form via AJAX
. From there I return the query and PREPEND
at information inside of a <div>
at the top of this list.
此外,我已经为每个项目提供了删除的功能,这在客户端是即时的,但也通过 AJAX提交表单
将被完全删除。这有点工作。
Also, I have given each item the ability to be deleted, which is instant on the client side but also submits a form via AJAX
to be completely removed. This is sort of working.
- 留空屏幕(没有添加项目),创建一个并删除它没有问题
- 有空白屏幕,添加两个项目,删除最新项目没有问题但删除第二个项目(这是第一个或最旧的项目)返回错误。它试图删除自己和最新的项目。因此,如果我有三个,它将删除自己和最新的,只留下项目#2。加入的项目越多,情况就越糟。
- 让新的Prepended元素继承事件处理程序
- 仅删除所选项目
当用户加载页面时,会立即查询存储在数据库中的项目并将其添加到屏幕中。
When the user loads the page, items that are stored in the database are immediately queried and added to the screen.
继续在第一个代码示例中找到 const delPostFunc
。这是一个立即调用的匿名函数,以确保最初添加到屏幕的任何项目都被分配了单击
事件处理程序。
Go ahead and find const delPostFunc
in the first code example. This is an anonymous function that is called immediately, to ensure that any items that are initially added to the screen are assigned the click
event handler.
当用户通过 submitPostBtn.addEventListener('click',e => {
)提交新项目时,在第一个示例的底部,两个调用。第二个例子中有一个到 const submitPost
, AJAX
,而 const在第二个例子中,returnNewestPost
, AJAX
。这个 returnNewestPost
调用返回一些 DATA
来自数据库,恰好是插入的最新项目,然后 PREPENDS
此项目位于列表顶部, displayPostWrapper.prepend(newPostDiv);
并最终调用 delPostFunc();
函数以尝试重新分配事件处理新插入的项目。这是因为 innerHTML
删除了应该在的所有事件处理程序元素,或者这就是我所相信的。
When a user submits a new item via submitPostBtn.addEventListener('click', e => {
, at the bottom of the first example, two calls are made. One to const submitPost
, AJAX
in the second example, and const returnNewestPost
, AJAX
in the second example. This returnNewestPost
call returns some DATA
from the database, which just so happens to be the newest item inserted, and then it PREPENDS
this item to the top of the list, displayPostWrapper.prepend(newPostDiv);
and finally calls the delPostFunc();
function in an attempt to reassign the event handler to newly inserted items. This is because innerHTML
removes any event handlers that are supposed to be on an element, or that is what I am lead to believe.
// DELETE POST VARIABLES
let deletePostBtn = document.querySelectorAll('button[name="delete_post"]');
const displayPostWrapper = document.querySelector('.col-8.pt-4');
let displayPostSection = document.querySelectorAll('.col-8.pt-4 .row');
let postID = document.querySelectorAll('#delete-post-id');
// SUBMIT POST VARIABLES
const submitPostBtn = document.querySelector('#submit-post-button');
const submitPostID = document.querySelector('#submit-post-id');
const submitPostContent = document.querySelector('#submit-post-content');
const submitPostName = document.querySelector('#submit-post-name');
// MAKING THE CALL TO DELETE THE POST
const delPostFunc = () => {
console.log(deletePostBtn);
deletePostBtn = document.querySelectorAll('button[name="delete_post"]');
console.log(deletePostBtn);
if (deletePostBtn) {
for (let i = 0; i < deletePostBtn.length; i++) {
deletePostBtn[i].addEventListener('click', e => {
e.preventDefault();
postID = document.querySelectorAll('#delete-post-id');
displayPostSection = document.querySelectorAll('.col-8.pt-4 .row');
console.log(postID[i].value);
// ${postID[i]} comes from `const postID` at the top
deletePostPromise('http://localhost/mouthblog/ajax/delete_post.ajax.php', `id=${postID[i].value}`);
console.log(deletePostBtn);
displayPostSection[i].remove();
console.log(deletePostBtn);
});
}
}
}
// CALL `delPostFunc()` FOR THE INITIAL `deletePostBtn` ON SCREEN
delPostFunc();
// MAKING CALL TO SUBMIT NEW POST
if (submitPostBtn) {
submitPostBtn.addEventListener('click', e => {
e.preventDefault();
// SUBMIT POST
submitPost('http://localhost/mouthblog/ajax/submit_post.ajax.php',
`id=${submitPostID.value}&name=${submitPostName.value}&content=${submitPostContent.value}`)
.then(() => {
// RETURN THAT SAME POST
returnNewestPost('http://localhost/mouthblog/api/newest_post.php')
.then(data => {
// INSERT POST INTO DOM
const newPostDiv = document.createElement('div');
newPostDiv.setAttribute('class', 'row');
newPostDiv.innerHTML = `
<article class="col-10 offset-1">
<h2 class="h2">${data.user_name}</h2>
<small>${data.date_created}</small>
<form action="//localhost/mouthblog/blog.php" method="POST">
<button class="btn btn-danger" name="delete_post" type="submit">DELETE</button>
<input id="delete-post-id" name="post_id" type="hidden" value="${data.id}">
</form>
<hr>
<p class="lead">${data.content}</p>
</article>
`;
console.log(`INSERTING ${data.id}`);
displayPostWrapper.prepend(newPostDiv);
console.log(`INSERT ${data.id} COMPLETE`);
// GIVE THE `newPostDiv`'s `delete button` THE CLICK EVENT HANDLER
console.log(`RUNNING delPostFunc()`);
delPostFunc(); // BOOM!
console.log(`delPostFunc() COMPLETE`);
});
});
});
}
这些是AJAX的承诺,只需加入
These are the promises for the AJAX just incase
// GET REQUEST TO RETRIEVE EVERY POST
const get = (url) => {
return new Promise((resolve, reject) => {
const xhttp = new XMLHttpRequest();
xhttp.open('GET', url, true);
xhttp.onload = () => {
if (xhttp.status == 200) {
resolve(JSON.parse(xhttp.response));
} else {
reject(xhttp.statusText);
}
};
xhttp.onerror = () => {
reject(xhttp.statusText);
};
xhttp.send();
});
}
// DELETE SPECIFIC POST
const deletePostPromise = (url, postID) => {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('POST', url, true);
xhr.onload = () => {
if (xhr.status == 200) {
console.log('if (xhr.status == 200)');
resolve();
} else {
reject(xhr.statusText);
}
};
xhr.onerror = () => {
reject(xhr.statusText);
};
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
xhr.send(postID);
});
}
// SUBMIT A NEW POST
const submitPost = (url, user_id, user_name, content) => {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('POST', url, true);
xhr.onload = () => {
if (xhr.status == 200) {
console.log('resolving');
resolve();
} else {
reject(xhr.statusText);
}
};
xhr.onerror = () => {
reject(xhr.statusText);
};
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
xhr.send(user_id, user_name, content);
});
};
// RETURN THE NEWEST BLOG POST
const returnNewestPost = (url) => {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.onload = () => {
if (xhr.status == 200) {
console.log('resolving');
resolve(JSON.parse(xhr.response));
} else {
reject(xhr.statusText);
}
};
xhr.onerror = () => {
reject(xhr.statusText);
};
xhr.send();
});
}
推荐答案
这个问题最简单的答案是使用活动委派重写脚本。
The simplest answer to this question is to rewrite the script using Event Delegation.
事件委托允许我们将单个事件监听器附加到父元素,该元素将为匹配选择器的所有后代触发,无论这些后代现在是存在还是将来添加。
Event delegation allows us to attach a single event listener, to a parent element, that will fire for all descendants matching a selector, whether those descendants exist now or are added in the future.
比较OP中的脚本并比较一下。重写的脚本具有更少的代码,更少的循环,更少的变量,并且更容易维护和读取。
Compare the script from the OP and compare this one. The rewritten script has less code, less loops, less variables and is a lot easier to maintain and read through.
如果您想比较细节,事件委托开始于的行if(displayPostWrapper&& submitPostBtn){
If you would like to compare specifics, event delegation starts on the line with if (displayPostWrapper && submitPostBtn) {
const submitPostBtn = document.querySelector('#submit-post-button');
const submitPostID = document.querySelector('#submit-post-id');
const submitPostContent = document.querySelector('#submit-post-content');
const submitPostName = document.querySelector('#submit-post-name');
const displayPostWrapper = document.querySelector('.col-8.pt-4');
// GET REQUEST TO RETRIEVE EVERY POST
const get = (url) => {
return new Promise((resolve, reject) => {
const xhttp = new XMLHttpRequest();
xhttp.open('GET', url, true);
xhttp.onload = () => {
if (xhttp.status == 200) {
resolve(JSON.parse(xhttp.response));
} else {
reject(xhttp.statusText);
}
};
xhttp.onerror = () => {
reject(xhttp.statusText);
};
xhttp.send();
});
}
// DELETE SPECIFIC POST
const deletePostPromise = (url, postID) => {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('POST', url, true);
xhr.onload = () => {
if (xhr.status == 200) {
console.log('if (xhr.status == 200)');
resolve();
} else {
reject(xhr.statusText);
}
};
xhr.onerror = () => {
reject(xhr.statusText);
};
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
xhr.send(postID);
});
}
// SUBMIT A NEW POST
const submitPost = (url, user_id, user_name, content) => {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('POST', url, true);
xhr.onload = () => {
if (xhr.status == 200) {
console.log('resolving');
resolve();
} else {
reject(xhr.statusText);
}
};
xhr.onerror = () => {
reject(xhr.statusText);
};
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
xhr.send(user_id, user_name, content);
});
};
// RETURN THE NEWEST BLOG POST
const returnNewestPost = (url) => {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.onload = () => {
if (xhr.status == 200) {
console.log('resolving');
resolve(JSON.parse(xhr.response));
} else {
reject(xhr.statusText);
}
};
xhr.onerror = () => {
reject(xhr.statusText);
};
xhr.send();
});
}
// MAKING THE CALL TO DELETE THE POST
if (displayPostWrapper && submitPostBtn) {
displayPostWrapper.addEventListener('click', e => {
if (e.target && e.target.nodeName == 'BUTTON') {
e.preventDefault();
const row = e.target.parentElement.parentElement.parentElement;
const form = e.target.parentElement;
const postID = e.target.parentElement.childNodes[3].value;
deletePostPromise('http://localhost/mouthblog/ajax/delete_post.ajax.php', `id=${postID}`);
row.remove();
} // if
}); // click event
submitPostBtn.addEventListener('click', e => {
e.preventDefault();
submitPost('http://localhost/mouthblog/ajax/submit_post.ajax.php',
`id=${submitPostID.value}&name=${submitPostName.value}&content=${submitPostContent.value}`)
.then(() => {
returnNewestPost('http://localhost/mouthblog/api/newest_post.php')
.then(data => {
console.log(data);
const newPost = document.createElement('div');
newPost.setAttribute('class', 'row');
newPost.innerHTML = `
<article class="col-10 offset-1">
<h2 class="h2">${data.user_name}</h2>
<small>${data.date_created}</small>
<form action="//localhost/mouthblog/blog.php" method="POST">
<button class="btn btn-danger" name="delete_post" type="submit">DELETE</button>
<input id="delete-post-id" name="post_id" type="hidden" value="${data.id}">
</form>
<hr>
<p class="lead">${data.content}</p>
</article>
`;
displayPostWrapper.prepend(newPost);
}) // then
}) // then
}); // click event
} // if
这篇关于JavaScript:将元素添加到DOM并继承事件处理程序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!