How to find the nearest common ancestors of two or more nodes?

前端 未结 14 1952
-上瘾入骨i
-上瘾入骨i 2020-11-30 04:03

Users selects two or more elements in a HTML page. What i want to accomplish is to find those elements\' common ancestors (so body node would be the common ancestor if none

相关标签:
14条回答
  • 2020-11-30 04:28

    You can also use a DOM Range (when supported by the browser, of course). If you create a Range with the startContainer set to the earlier node in the document and the endContainer set to the later node in the document, then the commonAncestorContainer attribute of such a Range is the deepest common ancestor node.

    Here is some code implementing this idea:

    function getCommonAncestor(node1, node2) {
        var dp = node1.compareDocumentPosition(node2);
        // If the bitmask includes the DOCUMENT_POSITION_DISCONNECTED bit, 0x1, or the
        // DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC bit, 0x20, then the order is implementation
        // specific.
        if (dp & (0x1 | 0x20)) {
            if (node1 === node2) return node1;
    
            var node1AndAncestors = [node1];
            while ((node1 = node1.parentNode) != null) {
                node1AndAncestors.push(node1);
            }
            var node2AndAncestors = [node2];
            while ((node2 = node2.parentNode) != null) {
                node2AndAncestors.push(node2);
            }
    
            var len1 = node1AndAncestors.length;
            var len2 = node2AndAncestors.length;
    
            // If the last element of the two arrays is not the same, then `node1' and `node2' do
            // not share a common ancestor.
            if (node1AndAncestors[len1 - 1] !== node2AndAncestors[len2 - 1]) {
                return null;
            }
    
            var i = 1;
            for (;;) {
                if (node1AndAncestors[len1 - 1 - i] !== node2AndAncestors[len2 - 1 - i]) {
                    // assert node1AndAncestors[len1 - 1 - i - 1] === node2AndAncestors[len2 - 1 - i - 1];
                    return node1AndAncestors[len1 - 1 - i - 1];
                }
                ++i;
            }
            // assert false;
            throw "Shouldn't reach here!";
        }
    
        // "If the two nodes being compared are the same node, then no flags are set on the return."
        // http://www.w3.org/TR/DOM-Level-3-Core/core.html#DocumentPosition
        if (dp == 0) {
            // assert node1 === node2;
            return node1;
    
        } else if (dp & 0x8) {
            // assert node2.contains(node1);
            return node2;
    
        } else if (dp & 0x10) {
            // assert node1.contains(node2);
            return node1;
        }
    
        // In this case, `node2' precedes `node1'. Swap `node1' and `node2' so that `node1' precedes
        // `node2'.
        if (dp & 0x2) {
            var tmp = node1;
            node1 = node2;
            node2 = tmp;
        } else {
            // assert dp & 0x4;
        }
    
        var range = node1.ownerDocument.createRange();
        range.setStart(node1, 0);
        range.setEnd(node2, 0);
        return range.commonAncestorContainer;
    }
    
    0 讨论(0)
  • 2020-11-30 04:33

    The commonAncestorContainer property of the he Range API mentioned above, alongside its selectNode, makes this a no-brainer.

    Run ("display") this code in Firefox's Scratchpad or a similar editor:

    var range = document.createRange();
    var nodes = [document.head, document.body];  // or any other set of nodes
    nodes.forEach(range.selectNode, range);
    range.commonAncestorContainer;
    

    Note that both APIs are not supported by IE 8 or below.

    0 讨论(0)
提交回复
热议问题