从 chrome-extension-popup 的无序列表中删除一个项目也会删除该项目之后的项目
Removing one item from an unordered list from chrome-extension-popup also removes the item(s) that comes after that
简介
假设我有一个包含 3 个项目的列表(这些项目是用户在 chrome 扩展弹出窗口中单击按钮时添加的 URL,请参见图片)。当我单击项目编号 3 的 'X' 按钮时,它只删除了该项目。之后,当我单击 'X' 项目编号 2 时,第二个项目被删除。最后,当单击第一个项目的 'X' 时,它也会被删除。一切都按预期工作。当我尝试相反的操作时,出现了问题。
问题
我们有相同的 3 项列表。现在,我决定先删除第 1 项,而不是先删除第 3 项。当我这样做时,所有 3 项都立即消失了。然后我再次重新加载这 3 个项目并尝试从删除项目编号 2 开始,这次只删除项目 2 和项目 3,剩下项目 1。这是一个奇怪的错误,因为似乎每当我单击 'X' 按钮时,连续的项目与我要删除的项目一起被删除。
在 Google 点击 'X' 之前:
点击 'X' 后只剩下 Facebook:
然后我开始使用调试器。
调试器
所以我觉得这很奇怪,我试图用调试器解决这个问题。当仅按 Google 按下 'X' 按钮时,我收到以下错误(与上图相同):
然后我去了 popup.js 并查看了包含这些行的特定代码行:
// the specific URL to delete
list.removeChild(items[j]);
在代码部分我post代码部分有更详细的介绍。我搜索了 Google 上的错误,发现了这个:removeChild。这里错误描述如下:
"If the child was in fact a child of element and so existing on the DOM, but was removed".
现在这让我想知道,我再次查看了我的代码,但据我所知,我没有删除 Google 之后的 URL,只有 Google本身。我试图在纸上解决它,但不幸的是徒劳无功。我唯一能想到的就是我应该从 "addToDom" 函数中删除 "createButtonEvents"。但如果我这样做,我将无法再删除项目编号 1(但我可以删除项目 2 而无需删除项目 3)。我认为这个问题比我最初想象的要复杂得多,所以我无法自己解决这个问题,这就是我在这里问这个问题的原因。现在是 'code' 部分。
代码
任何想要查看完整代码(所有行和文件)的人,请前往此页面: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();
});
}
编辑
以防万一有人没有读过它,我将在这里再次重复调试器部分中的句子:
我唯一能想到的就是我应该从"addToDom"函数中删除"createButtonEvents"。但如果我这样做,我将无法再删除项目编号 1(但我可以删除项目 2 而无需删除项目 3)。我认为问题比我最初想象的要复杂得多。
我知道 'buttons class' 上的多个事件处理程序可能同时工作,但我不知道如何以其他方式格式化代码。
控制台中的错误告诉您需要将节点传递给 removeChild,看起来您没有传递 DOM 元素。
document.getElementsByClassName 创建一个 live 元素列表。
createButtonEvents();
被多次调用,并添加了多个使用 j
作为实时集合索引的事件侦听器。因为侦听器使用每个添加事件侦听器调用唯一的匿名函数,所以它们不被认为是相同的并且不会被丢弃。
因此删除最后一个元素是有效的,因为在第一次删除后没有更多的“第 j 个”元素要删除。但是当您删除第一个元素时,因为列表是活动的,之后还有另一个要删除的第一个元素,因为有多个事件侦听器...
我尝试删除 addToDom
中的 createButtonEvents()
调用,这修复了控制台错误消息,因为代码不再尝试删除不存在的节点。
引入了一个新错误:删除第一个按钮有效,因为它在实时集合中的索引为 0。但是删除会更改活动列表中剩余项目的索引。现在删除第二项会删除第三项 link 因为它的索引已更改为 1,而索引为 1 的内容现在为 0。
更新示例解决方案:
删除createButtonEvents
函数。嵌套 listenJ
和捕获匿名函数 j
,索引实时 HTML 集合,是错误的根本原因。
删除 restore
中对 createButtonEvents
的调用
将addToDom
中的调用createButtonEvents
替换为
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);
}
urlList
和 titleList
的更新已使用 Chrome 之外的测试值进行了测试:Chrome 需要恢复存储更新。
简介
假设我有一个包含 3 个项目的列表(这些项目是用户在 chrome 扩展弹出窗口中单击按钮时添加的 URL,请参见图片)。当我单击项目编号 3 的 'X' 按钮时,它只删除了该项目。之后,当我单击 'X' 项目编号 2 时,第二个项目被删除。最后,当单击第一个项目的 'X' 时,它也会被删除。一切都按预期工作。当我尝试相反的操作时,出现了问题。
问题
我们有相同的 3 项列表。现在,我决定先删除第 1 项,而不是先删除第 3 项。当我这样做时,所有 3 项都立即消失了。然后我再次重新加载这 3 个项目并尝试从删除项目编号 2 开始,这次只删除项目 2 和项目 3,剩下项目 1。这是一个奇怪的错误,因为似乎每当我单击 'X' 按钮时,连续的项目与我要删除的项目一起被删除。
在 Google 点击 'X' 之前:
点击 'X' 后只剩下 Facebook:
然后我开始使用调试器。
调试器
所以我觉得这很奇怪,我试图用调试器解决这个问题。当仅按 Google 按下 'X' 按钮时,我收到以下错误(与上图相同):
然后我去了 popup.js 并查看了包含这些行的特定代码行:
// the specific URL to delete
list.removeChild(items[j]);
在代码部分我post代码部分有更详细的介绍。我搜索了 Google 上的错误,发现了这个:removeChild。这里错误描述如下:
"If the child was in fact a child of element and so existing on the DOM, but was removed".
现在这让我想知道,我再次查看了我的代码,但据我所知,我没有删除 Google 之后的 URL,只有 Google本身。我试图在纸上解决它,但不幸的是徒劳无功。我唯一能想到的就是我应该从 "addToDom" 函数中删除 "createButtonEvents"。但如果我这样做,我将无法再删除项目编号 1(但我可以删除项目 2 而无需删除项目 3)。我认为这个问题比我最初想象的要复杂得多,所以我无法自己解决这个问题,这就是我在这里问这个问题的原因。现在是 'code' 部分。
代码
任何想要查看完整代码(所有行和文件)的人,请前往此页面: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();
});
}
编辑
以防万一有人没有读过它,我将在这里再次重复调试器部分中的句子:
我唯一能想到的就是我应该从"addToDom"函数中删除"createButtonEvents"。但如果我这样做,我将无法再删除项目编号 1(但我可以删除项目 2 而无需删除项目 3)。我认为问题比我最初想象的要复杂得多。
我知道 'buttons class' 上的多个事件处理程序可能同时工作,但我不知道如何以其他方式格式化代码。
控制台中的错误告诉您需要将节点传递给 removeChild,看起来您没有传递 DOM 元素。
document.getElementsByClassName 创建一个 live 元素列表。
createButtonEvents();
被多次调用,并添加了多个使用 j
作为实时集合索引的事件侦听器。因为侦听器使用每个添加事件侦听器调用唯一的匿名函数,所以它们不被认为是相同的并且不会被丢弃。
因此删除最后一个元素是有效的,因为在第一次删除后没有更多的“第 j 个”元素要删除。但是当您删除第一个元素时,因为列表是活动的,之后还有另一个要删除的第一个元素,因为有多个事件侦听器...
我尝试删除 addToDom
中的 createButtonEvents()
调用,这修复了控制台错误消息,因为代码不再尝试删除不存在的节点。
引入了一个新错误:删除第一个按钮有效,因为它在实时集合中的索引为 0。但是删除会更改活动列表中剩余项目的索引。现在删除第二项会删除第三项 link 因为它的索引已更改为 1,而索引为 1 的内容现在为 0。
更新示例解决方案:
删除
createButtonEvents
函数。嵌套listenJ
和捕获匿名函数j
,索引实时 HTML 集合,是错误的根本原因。删除
中对restore
createButtonEvents
的调用将
addToDom
中的调用createButtonEvents
替换为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); }
urlList
和 titleList
的更新已使用 Chrome 之外的测试值进行了测试:Chrome 需要恢复存储更新。