如何遍历javascript中选定范围内的每个节点?

How to iterate over every node in a selected range in javascript?

在 javascript 中实现富文本编辑器时,我需要对选定范围内的每个文本节点应用一些更改。 Range 对象提供了获取所选范围的 startContainerendContainerstartOffsetendOffset 的接口。我如何遍历中间的每个 DOM 节点?

var selection = window.getSelection();
var range = selection.getRange(0);
// How can I iterate over every node within the range?

range.commonAncestorContainer 将为您提供包含该范围的节点。如果它给你一个文本节点,那么这是你范围内的唯一节点。

如果它给你一个元素,你可以使用NodeIterator,或el.querySelectorAll('*')来获取其中的节点。

并非所有这些都在您的范围内,因此请使用 range.intersectsNode(el) 进行确认。

根据提示,您可以使用NodeIterator进入range.commonAncestorContainer

这是一个片段:

var _iterator = document.createNodeIterator(
    range.commonAncestorContainer,
    NodeFilter.SHOW_ALL, // pre-filter
    {
        // custom filter
        acceptNode: function (node) {
            return NodeFilter.FILTER_ACCEPT;
        }
    }
);

var _nodes = [];
while (_iterator.nextNode()) {
    if (_nodes.length === 0 && _iterator.referenceNode !== range.startContainer) continue;
    _nodes.push(_iterator.referenceNode);
    if (_iterator.referenceNode === range.endContainer) break;
}

您应该使用 NodeFilter.SHOW_ALL,因为您的范围可以包含多个 nodeTypes. If you know what you are selecting, you can check this reference 以正确选择 NodeFilter


编辑:我也想指出document.createTreeWalker()

主要区别在于 document.createTreeWalker() 允许您的 acceptNode 过滤器 return NodeFilter.FILTER_REJECTNodeFilter.FILTER_SKIP 具有真正的区别。

引自NodeFilter docs

FILTER_REJECT:

Value to be returned by the NodeFilter.acceptNode() method when a node should be rejected. For TreeWalker, child nodes are also rejected. For NodeIterator, this flag is synonymous with FILTER_SKIP.

Ps: NodeFilter.FILTER_REJECTNodeFilter.acceptNode() 文档不正确。