我们说我有一个包含3个项目的列表(这些项目是用户在点击Chrome扩展弹出窗口内的按钮时添加的URL,见图片)。当我点击' X'项目编号3的按钮,它只删除该项目。之后,当我点击' X'项目编号2,第二项被删除。最后,点击' X'它也被删除的第一个项目。一切都按预期工作。当我尝试相反时,会出现问题。
我们有3个项目的相同列表。现在,我决定先删除第1项,而不是先删除第3项。当我这样做时,所有3个项目都立即消失了。然后我再次重新加载了3个项目,并尝试从删除项目编号2开始,这次只删除项目2和项目3,剩余项目1。这是一个奇怪的错误,因为每当我点击一个' X'按钮连续项目将与我要删除的项目一起删除。
点击' X'谷歌:
点击' X'只剩下Facebook:
所以我认为这很奇怪,我试图用调试器解决问题。只按下' X'谷歌按钮(与上图相同):
然后我去了popup.js并查看了包含这些行的特定代码行:
// the specific URL to delete
list.removeChild(items[j]);
在代码部分中,我更详细地发布了代码部分。我在Google上搜索了错误并找到了这个:removeChild。这里,错误描述如下:
"如果孩子实际上是元素的孩子并且存在于DOM上,但已被删除" 。
现在这让我很奇怪,我再次凝视着我的代码,但据我所知,我还没有删除Google之后的网址,只有谷歌本身。我试图在纸上解决这个问题但不幸的是徒劳无功。我唯一能想到的是我应该删除" createButtonEvents"来自" addToDom"功能。但是,如果我这样做,我就不能再删除第1项(但是我可以删除第2项而不删除第3项)。我认为这个问题比我原先想象的要复杂得多,所以我不能自己解决这个问题,这也就是我在这里问这个问题的原因。现在来了'代码'部分。
对于想要查看整个代码(所有行和文件)的任何人,请转到此页面:GitHub
对于那些只关注发生这种情况的特定代码的人:
我的popup.js文件的一部分:
document.addEventListener('DOMContentLoaded', function() {
restore();
document.getElementById('add').addEventListener('click', fetchUrl);
document.getElementById('clear').addEventListener('click', clearAll);
});
function restore() {
// get the tab link and title
chrome.storage.local.get({urlList:[], titleList:[]}, function(data) {
urlList = data.urlList;
titleList = data.titleList;
// add the titles and url's to the DOM
var n = urlList.length;
for (var i = 0; i < n; i++) {
addToDom(urlList[i], titleList[i]);
}
// 'X' button handlers only when all URL's are in DOM
createButtonEvents();
});
}
function addToDom(url, title) {
// change the (greeting) text message
document.getElementById("div").innerHTML = "<h2 id='title'>Saved Pages</h2>";
// Build the new DOM elements programmatically
var newItem = document.createElement('li');
var newLink = document.createElement('a');
var thisButton = document.createElement('button');
newLink.textContent = title;
thisButton.textContent = 'X';
thisButton.setAttribute('class', 'buttons');
thisButton.setAttribute('tabindex', -1);
newItem.setAttribute('class', 'items');
newLink.setAttribute('href', url);
newLink.setAttribute('target', '_blank');
newLink.setAttribute('tabindex', -1);
newLink.setAttribute('id', 'item');
newItem.appendChild(thisButton);
newItem.appendChild(newLink);
document.getElementById('list').appendChild(newItem);
createButtonEvents();
}
function createButtonEvents() {
// create event listeners for all the 'X' buttons next to list items
// after the 'addToDom' function has been executed
var allButtons = document.getElementsByClassName('buttons');
for (var j = 0, k = allButtons.length; j < k; j++) {
listenJ(j);
}
function listenJ(j) {
allButtons[j].addEventListener('click', () => removeMe(j));
}
}
function removeMe(j) {
// remove it from the DOM
var items = document.getElementsByClassName('items');
var list = document.getElementById('list');
// the specific URL to delete
list.removeChild(items[j]);
// return the DOM to original state
if (items.length === 0) {
document.getElementById('list').innerHTML = '';
document.getElementById('div').innerHTML = '<h3>No content yet! Click "add link" to add the link of the current website!</h3>';
}
// remove it from chrome-storage
chrome.storage.local.get({urlList:[], titleList:[]}, function(data) {
urlList = data.urlList;
titleList = data.titleList;
urlList.splice(j, 1);
titleList.splice(j, 1);
// update chrome storage
saveList();
});
}
&#13;
如果有人没有阅读,我会再次在调试器部分重复这句话:
我唯一能想到的就是我应该删除&#34; createButtonEvents&#34;来自&#34; addToDom&#34;功能。但是,如果我这样做,我就不能再删除第1项(但是我可以删除第2项而不删除第3项)。我认为这个问题比我最初认为的要复杂得多 。
我知道&#39;按钮类上的多个事件处理程序&#39;可能在同一时间工作,但我不知道如何以另一种方式格式化代码。
答案 0 :(得分:2)
document.getElementsByClassName会创建 直播 元素列表。
多次调用 createButtonEvents();
并添加多个事件侦听器,这些侦听器使用j
作为实时集合的索引。因为侦听器使用每个添加事件侦听器调用唯一的匿名函数,所以它们不被视为相同且不会被丢弃。
因此删除最后一个元素是有效的,因为没有更多的&#34; jth&#39;第一次删除后要删除的元素。但是当你删除第一个元素时,因为列表是实时的,之后还有另一个要删除的第一个元素,因为有多个事件监听器......
我尝试删除createButtonEvents()
中的addToDom
调用,该调用修复了控制台错误消息,因为代码不再尝试删除不存在的节点。
引入了一个新错误:删除第一个按钮,因为它的索引在实时收藏中为0。但删除会更改实时列表中剩余项目的权限。现在删除第二个项目会删除第三个链接,因为它的索引已更改为1,而索引为1的内容现在为0。
<小时/> 更新了样本解决方案:
删除createButtonEvents
功能。嵌套listenJ
和捕获j
的匿名函数(索引实时HTML集合)是错误的根本原因。
移除createButtonEvents
restore
的来电
将createButtonEvents
中的来电addToDom
替换为
thisButton.addEventListener('click', removeMe);
以便列表中的所有按钮获得相同的点击处理程序,无论该按钮是从存储中恢复还是稍后添加。
在removeMe
中,找到点击的按钮,在列表中找到当前,动态位置并将其删除。找到的位置与存储数据数组中项目的位置匹配:
function removeMe() {
// remove list item (parent of button clicked) from the DOM
var list = document.getElementById('list');
// find position of list element with button clicked:
for( var j = 0, child = list.firstChild;
child;
child = child.nextSibling, ++j) {
if (child == this.parentNode) {
break;
}
}
list.removeChild(this.parentNode); // this is button, parent is <li>
// return the DOM to original state
if (!list.firstChild) {
document.getElementById('list').innerHTML = '';
document.getElementById('div').innerHTML = '<h3>No content yet! Click "add link" to add the link of the current website!</h3>';
}
// remove from Chrome storage using position in j
// urlList = data.urlList;
// titleList = data.titleList;
// urlList.splice(j, 1);
// titleList.splice(j, 1);
}
使用Chrome以外的测试值测试了urlList
和titleList
的更新:Chrome存储更新需要恢复。
答案 1 :(得分:0)
控制台中的错误告诉您需要将Node传递给removeChild,看起来您没有传递DOM元素。