How to get nodes lying inside a range with javascript?

后端 未结 9 2071
被撕碎了的回忆
被撕碎了的回忆 2020-11-29 03:59

I\'m trying to get all the DOM nodes that are within a range object, what\'s the best way to do this?

var selection = window.getSelection(); //what the user          


        
相关标签:
9条回答
  • 2020-11-29 04:30

    The Rangy library has a Range.getNodes([Array nodeTypes[, Function filter]]) function.

    0 讨论(0)
  • 2020-11-29 04:30

    I wrote the perfect code for this and it works 100% for every node :

    function getNodesInSelection() {
        
    var range = window.getSelection().getRangeAt(0);
    var node = range.startContainer;
    
    var ranges = []
    var nodes = []
            
    while (node != null) {        
        
        var r = document.createRange();
        r.selectNode(node)
        
        if(node == range.startContainer){
            r.setStart(node, range.startOffset)
        }
        
        if(node == range.endContainer){
            r.setEnd(node, range.endOffset)
        }
        
        
        ranges.push(r)
        nodes.push(node)
        
        node = getNextElementInRange(node, range)
    }
         
    // do what you want with ranges and nodes
    }
    

    here are some helper functions

    function getClosestUncle(node) {
    
    var parent = node.parentElement;
    
    while (parent != null) {
        var uncle = parent.nextSibling;
        if (uncle != null) {
            return uncle;
        }
        
        uncle = parent.nextElementSibling;
        if (uncle != null) {
            return uncle;
        }
        
        parent = parent.parentElement
    }
    
    return null
    }
     
    
                        
    function getFirstChild(_node) {
    
    var deep = _node
    
    while (deep.firstChild != null) {
        
        deep = deep.firstChild
    }
    
    return deep
    }
      
    
                        
    function getNextElementInRange(currentNode, range) {
    
    var sib = currentNode.nextSibling;
    
    if (sib != null && range.intersectsNode(sib)) {
        return getFirstChild(sib)
    }
            
    var sibEl = currentNode.nextSiblingElemnent;
    
    if (sibEl != null && range.intersectsNode(sibEl)) {
        return getFirstChild(sibEl)
    }
    
    var uncle = getClosestUncle(currentNode);
    var nephew = getFirstChild(uncle)
    
    if (nephew != null && range.intersectsNode(nephew)) {
        return nephew
    }
    
    return null
    }
    
    0 讨论(0)
  • 2020-11-29 04:35

    The getNextNode will skip your desired endNode recursively if its a parent node.

    Perform the conditional break check inside of the getNextNode instead:

    var getNextNode = function(node, skipChildren, endNode){
      //if there are child nodes and we didn't come from a child node
      if (endNode == node) {
        return null;
      }
      if (node.firstChild && !skipChildren) {
        return node.firstChild;
      }
      if (!node.parentNode){
        return null;
      }
      return node.nextSibling 
             || getNextNode(node.parentNode, true, endNode); 
    };
    

    and in while statement:

    while (startNode = getNextNode(startNode, false , endNode));
    
    0 讨论(0)
  • 2020-11-29 04:38

    Annon, great work. I've modified the original plus included Stefan's modifications in the following.

    In addition, I removed the reliance on Range, which converts the function into an generic algorithm to walk between two nodes. Plus, I've wrapped everything into a single function.

    Thoughts on other solutions:

    • Not interested in relying on jquery
    • Using cloneNode lifts the results to a fragment, which prevents many operations one might want to conduct during filtering.
    • Using querySelectAll on a cloned fragment is wonky because the start or end nodes may be within a wrapping node, hence the parser may not have the closing tag?

    Example:

    <div>
        <p>A</p>
        <div>
            <p>B</p>
            <div>
                <p>C</p>
            </div>
        </div>
    </div>
    

    Assume start node is the "A" paragraph, and the end node is the "C" paragraph . The resulting cloned fragment would be:

    <p>A</p>
        <div>
            <p>B</p>
            <div>
                <p>C</p>
    

    and we're missing closing tags? resulting in funky DOM structure?

    Anyway, here's the function, which includes a filter option, which should return TRUE or FALSE to include/exclude from results.

    var getNodesBetween = function(startNode, endNode, includeStartAndEnd, filter){
        if (startNode == endNode && startNode.childNodes.length === 0) {
            return [startNode];
        };
    
        var getNextNode = function(node, finalNode, skipChildren){
            //if there are child nodes and we didn't come from a child node
            if (finalNode == node) {
                return null;
            }
            if (node.firstChild && !skipChildren) {
                return node.firstChild;
            }
            if (!node.parentNode){
                return null;
            }
            return node.nextSibling || getNextNode(node.parentNode, endNode, true);
        };
    
        var nodes = [];
    
        if(includeStartAndEnd){
            nodes.push(startNode);
        }
    
        while ((startNode = getNextNode(startNode, endNode)) && (startNode != endNode)){
            if(filter){
                if(filter(startNode)){
                    nodes.push(startNode);
                }
            } else {
                nodes.push(startNode);
            }
        }
    
        if(includeStartAndEnd){
            nodes.push(endNode);
        }
    
        return nodes;
    };
    
    0 讨论(0)
  • 2020-11-29 04:44

    below code solve your problem

        <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>payam jabbari</title>
    <script src="http://code.jquery.com/jquery-2.0.2.min.js" type="text/javascript"></script>
    <script type="text/javascript">
    
    $(document).ready(function(){
        var startNode = $('p.first').contents().get(0);
    var endNode = $('span.second').contents().get(0);
    var range = document.createRange();
    range.setStart(startNode, 0);
    range.setEnd(endNode, 5);
    var selection = document.getSelection();
    selection.addRange(range);
    // below code return all nodes in selection range. this code work in all browser
    var nodes = range.cloneContents().querySelectorAll("*");
    for(var i=0;i<nodes.length;i++)
    {
       alert(nodes[i].innerHTML);
    }
    });
    </script>
    </head>
    
    <body>
    <div>
    
    <p class="first">Even a week ago, the idea of a Russian military intervention in Ukraine seemed far-fetched if not totally alarmist. But the arrival of Russian troops in Crimea over the weekend has shown that he is not averse to reckless adventures, even ones that offer little gain. In the coming days and weeks</p>
    
    <ol>
        <li>China says military will respond to provocations.</li>
        <li >This Man Has Served 20 <span class="second"> Years—and May Die—in </span> Prison for Marijuana.</li>
        <li>At White House, Israel's Netanyahu pushes back against Obama diplomacy.</li>
    </ol>
    </div>
    </body>
    </html>
    
    0 讨论(0)
  • 2020-11-29 04:45

    here is function return you array of sub-ranges

    function getSafeRanges(range) {
    
    var doc = document;
    
    var commonAncestorContainer = range.commonAncestorContainer;
    var startContainer = range.startContainer;
    var endContainer = range.endContainer;
    var startArray = new Array(0),
        startRange = new Array(0);
    var endArray = new Array(0),
        endRange = new Array(0);
    // @@@@@ If start container and end container is same
    if (startContainer == endContainer) {
        return [range];
    } else {
        for (var i = startContainer; i != commonAncestorContainer; i = i.parentNode) {
            startArray.push(i);
        }
        for (var i = endContainer; i != commonAncestorContainer; i = i.parentNode) {
            endArray.push(i);
        }
    }
    if (0 < startArray.length) {
        for (var i = 0; i < startArray.length; i++) {
            if (i) {
                var node = startArray[i - 1];
                while ((node = node.nextSibling) != null) {
                    startRange = startRange.concat(getRangeOfChildNodes(node));
                }
            } else {
                var xs = doc.createRange();
                var s = startArray[i];
                var offset = range.startOffset;
                var ea = (startArray[i].nodeType == Node.TEXT_NODE) ? startArray[i] : startArray[i].lastChild;
                xs.setStart(s, offset);
                xs.setEndAfter(ea);
                startRange.push(xs);
            }
        }
    }
    if (0 < endArray.length) {
        for (var i = 0; i < endArray.length; i++) {
            if (i) {
                var node = endArray[i - 1];
                while ((node = node.previousSibling) != null) {
                    endRange = endRange.concat(getRangeOfChildNodes(node));
                }
            } else {
                var xe = doc.createRange();
                var sb = (endArray[i].nodeType == Node.TEXT_NODE) ? endArray[i] : endArray[i].firstChild;
                var end = endArray[i];
                var offset = range.endOffset;
                xe.setStartBefore(sb);
                xe.setEnd(end, offset);
                endRange.unshift(xe);
            }
        }
    }
    var topStartNode = startArray[startArray.length - 1];
    var topEndNode = endArray[endArray.length - 1];
    var middleRange = getRangeOfMiddleElements(topStartNode, topEndNode);
    startRange = startRange.concat(middleRange);
    response = startRange.concat(endRange);
    return response;
    

    }

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