Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's the best way to find the first common parent of two DOM nodes in javascript?

My question is exactly that but in context I want to examine the selection object, compare the anchorNode and focusNode and if they are different then find the first common parent element.

var selected = window.getSelection();
var anchor = selection.anchorNode;
var focus = selection.focusNode;

if ( anchor != focus ) {
   // find common parent...
}
like image 623
roborourke Avatar asked Nov 15 '25 12:11

roborourke


2 Answers

Since this question and accepted answer are very dated, I'd like to suggest using a more modern DOM API, Range:

function findFirstCommonAncestor(nodeA, nodeB) {
    let range = new Range();
    range.setStart(nodeA, 0);
    range.setEnd(nodeB, 0);
    // There's a compilication, if nodeA is positioned after
    // nodeB in the document, we created a collapsed range.
    // That means the start and end of the range are at the
    // same position. In that case `range.commonAncestorContainer`
    // would likely just be `nodeB.parentNode`.
    if(range.collapsed) {
        // The old switcheroo does the trick.
        range.setStart(nodeB, 0);
        range.setEnd(nodeA, 0);
    }
    return range.commonAncestorContainer;
}
like image 133
Lasse Avatar answered Nov 17 '25 09:11

Lasse


I would try something like this, assuming no JS library:

function findFirstCommonAncestor(nodeA, nodeB, ancestorsB) {
    var ancestorsB = ancestorsB || getAncestors(nodeB);
    if(ancestorsB.length == 0) return null;
    else if(ancestorsB.indexOf(nodeA) > -1) return nodeA;
    else if(nodeA == document) return null;
    else return findFirstCommonAncestor(nodeA.parentNode, nodeB, ancestorsB);
}

using this utilities:

function getAncestors(node) {
    if(node != document) return [node].concat(getAncestors(node.parentNode));
    else return [node];
}

if(Array.prototype.indexOf === undefined) {
    Array.prototype.indexOf = function(element) {
        for(var i=0, l=this.length; i<l; i++) {
            if(this[i] == element) return i;
        }
        return -1;
    };
}

Then you can call findFirstCommonAncestor(myElementA, myElementB).

like image 45
Alsciende Avatar answered Nov 17 '25 07:11

Alsciende



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!