【问题标题】:Removing one item from an unordered list from chrome-extension-popup also removes the item(s) that comes after that从 chrome-extension-popup 的无序列表中删除一项也会删除之后的项目
【发布时间】:2016-12-28 20:32:33
【问题描述】:

简介

假设我有一个包含 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]);

代码部分,我更详细地发布了代码部分。我在谷歌上搜索了这个错误,发现了这个:removeChild。在这里,错误描述如下:

“如果子元素实际上是元素的子元素,因此存在于 DOM 中,但被移除了”

现在这让我想知道,我再次凝视着我的代码,但据我所知,我没有删除谷歌之后的 URL,只删除了谷歌本身。我试图在纸上解决它,但不幸的是徒劳无功。我唯一能想到的是我应该从“addToDom”函数中删除“createButtonEvents”。但如果我这样做,我将无法再删除第 1 项(但我可以在不删除第 3 项的情况下删除第 2 项)。我认为这个问题比我最初想象的要复杂得多,所以我自己无法解决这个问题,这就是我在这里问这个问题的原因。现在是“代码”部分。

代码

任何想要查看完整代码(所有行和文件)的人,请前往此页面: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 项(但我可以在不删除第 3 项的情况下删除第 2 项)。我认为这个问题比我最初想象的要复杂得多

我知道“按钮类”上的多个事件处理程序可能同时工作,但我不知道如何以另一种方式格式化代码。

【问题讨论】:

    标签: javascript google-chrome google-chrome-extension event-handling popupwindow


    【解决方案1】:

    document.getElementsByClassName 创建一个实时元素列表。

    createButtonEvents(); 被多次调用,并添加了多个事件侦听器,这些侦听器使用j 作为实时集合的索引。因为监听器使用每个 add 事件监听器调用独有的匿名函数,所以它们不被认为是相同的并且不会被丢弃。

    因此删除最后一个元素是可行的,因为在第一次删除后没有更多“第 j 个”元素要删除。但是当你删除第一个元素时,因为列表是活动的,之后还有另一个第一个元素要删除,这是因为有多个事件监听器...

    我尝试删除 addToDom 中的 createButtonEvents() 调用,该调用修复了控制台错误消息,因为代码不再尝试删除不存在的节点。

    引入了一个新错误:删除第一个按钮有效,因为它的索引在实时集合中为 0。但是删除会更改活动列表中剩余项目的索引。现在删除第 2 个项目会删除第 3 个链接,因为它的索引已更改为 1,而索引为 1 的内容现在为 0。


    更新的示例解决方案:
    1. 删除createButtonEvents 函数。嵌套的listenJ 和用于捕获j 的匿名函数(索引实时HTML 集合)是该错误的根本原因。

      删除restore中对createButtonEvents的调用

      addToDom中的调用createButtonEvents替换为

          thisButton.addEventListener('click', removeMe);
      

      以便列表中的所有按钮都获得相同的点击处理程序,无论按钮是从存储中恢复还是稍后添加。

    2. 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);
      
      
      }
      

    urlListtitleList 的更新已使用 Chrome 之外的测试值进行了测试:需要恢复 Chrome 存储更新。

    【讨论】:

    • 啊,我明白了,所以最后我不得不使用“this”。非常感谢,新年快乐!
    • @AndrewIce 我确实看到你发表过类似的评论,但是当我试图打开你的评论时它已经被删除了哈哈..尽管如此,非常感谢:)。
    【解决方案2】:

    控制台的错误告诉你需要传递一个Node给removeChild,看起来你没有传递一个DOM元素。

    【讨论】:

      猜你喜欢
      • 2023-01-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-08-05
      • 2022-12-20
      • 1970-01-01
      • 2020-02-11
      • 1970-01-01
      相关资源
      最近更新 更多