从 DOM 中删除除一个子树之外的所有节点

Removing all nodes from the DOM except for one subtree

我的页面结构如下:

<body>
  <div class="one">
    <div class="two">
      <p class="three">Some text</p>
    </div>
  </div>
  <div class="four">
    <div class="five">
      <p class="six">Some other text</p>
    </div>
  </div>
</body>

给定一个选择器,例如 .five,我想从 DOM 中删除所有元素,同时保留 .four > .five > .six 的层次结构。换句话说,删除所有元素后,我应该留下:

<body>
  <div class="four">
    <div class="five">
      <p class="six">Some other text</p>
    </div>
  </div>
</body>

我想出了以下解决这个问题的办法:

function removeElementsExcept(selector) {
    let currentElement = document.querySelector(selector)
    while (currentElement !== document.body) {
        const parent = currentElement.parentNode
        for (const element of parent.children) {
            if (currentElement !== element) {
                parent.removeChild(element)
            }
        }
        currentElement = parent
    }
}

这对于上述情况已经足够好了,为此我创建了一个 JSfiddle

然而,当我尝试 运行 它在更复杂的网页上,例如在 https://developer.mozilla.org/en-US/docs/Web/API/Node/removeChild 上使用 removeElementsExcept('#sect1') 之类的调用时,我希望只有蓝色 div 包含文本“注意:只要引用...”及其内容将保留在页面上。但是,如果您尝试 运行 这样做,页面上还会保留许多其他元素以及蓝色 div

我在我的函数中做错了什么?

parent.removeChild(element) 更改迭代集合的长度,因此跳过元素。您可以使用 [...parent.children]HTMLCollection 散布到数组中,使其可以安全移除。

另一种方法是通过遍历目标元素的所有子节点和所有父节点来构建一组要保留的节点。然后删除不在集合中的所有其他节点。我没有 运行 基准。

const removeElementsExcept = el => {
  const keptEls = new Set();

  for (let currEl = el; currEl; currEl = currEl.parentNode) {
    keptEls.add(currEl);
  }

  for (const childEl of [...el.querySelectorAll("*")]) {
    keptEls.add(childEl);
  }

  for (const el of [...document.querySelectorAll("body *")]) {
    if (!keptEls.has(el)) {
      el.remove();
    }
  }
};

removeElementsExcept(document.querySelector(".five"));
.four {
  background: red;
  height: 100px;
  padding: 1em;
}
.five {
  background: blue;
  height: 100px;
  padding: 1em;
}
.six {
  background: yellow;
  height: 100px;
  padding: 1em;
}
<div class="one">
  <div class="two">
    <p class="three">Some text</p>
  </div>
</div>
<div class="four">
  <div class="five">
    <p class="six">Some other text</p>
  </div>
</div>

发生这种情况是因为您正在修改正在迭代的集合。您可以通过手动调整用于查看子项的索引来解决此问题。

function removeElementsExcept(selector) {
    let currentElement = document.querySelector(selector)
    while (currentElement !== document.body) {
        const parent = currentElement.parentNode;
        let idx = 0;
        while (parent.children.length > 1) {
            const element = parent.children[idx];
            if (currentElement !== element) {
                parent.removeChild(element)
            } else {
                idx = 1;
            }
        }
        currentElement = parent
    }
}