2009-03-20 4 views
14

범위 객체 내에있는 모든 DOM 노드를 가져 오려고하는데이 작업을 수행하는 가장 좋은 방법은 무엇입니까?자바 스크립트로 범위 안에있는 노드를 얻는 방법은 무엇입니까?

var selection = window.getSelection(); //what the user has selected 
var range = selection.getRangeAt(0); //the first range of the selection 
var startNode = range.startContainer; 
var endNode = range.endContainer; 
var allNodes = /*insert magic*/; 

나는 지난 몇 시간 동안 수있는 방법을 생각이 해낸되어 왔습니다 : 결국 노드가 시작 노드의 부모 인 경우

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

var getNodesInRange = function(range){ 
    var startNode = range.startContainer.childNodes[range.startOffset] 
      || range.startContainer;//it's a text node 
    var endNode = range.endContainer.childNodes[range.endOffset] 
      || range.endContainer; 

    if (startNode == endNode && startNode.childNodes.length === 0) { 
     return [startNode]; 
    }; 

    var nodes = []; 
    do { 
     nodes.push(startNode); 
    } 
    while ((startNode = getNextNode(startNode)) 
      && (startNode != endNode)); 
    return nodes; 
}; 

그러나이 모든 것을 반환 그 페이지. 나는 명백한 무엇인가를 멀리 바라보고있다라고 확신한다? 아니면 완전히 잘못된 방향으로 나아갈 수도 있습니다.

MDC/DOM/range

+2

'var에 대해 getSelection C =() getRangeAt (0) .cloneContents().; c.querySelectorAll ('*')' – caub

답변

11

getNextNode 원하는 말단 노드 재귀의 경우 부모 노드를 건너 뜁니다.

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); 
}; 

및 while 문에 :

대신 getNextNode의 내부 조건부 브레이크 검사를 수행

function getNextNode(node) 
{ 
    if (node.firstChild) 
     return node.firstChild; 
    while (node) 
    { 
     if (node.nextSibling) 
      return node.nextSibling; 
     node = node.parentNode; 
    } 
} 

function getNodesInRange(range) 
{ 
    var start = range.startContainer; 
    var end = range.endContainer; 
    var commonAncestor = range.commonAncestorContainer; 
    var nodes = []; 
    var node; 

    // walk parent nodes from start to common ancestor 
    for (node = start.parentNode; node; node = node.parentNode) 
    { 
     nodes.push(node); 
     if (node == commonAncestor) 
      break; 
    } 
    nodes.reverse(); 

    // walk children and siblings from start until end is found 
    for (node = start; node; node = getNextNode(node)) 
    { 
     nodes.push(node); 
     if (node == end) 
      break; 
    } 

    return nodes; 
} 
: 여기
while (startNode = getNextNode(startNode, false , endNode)); 
+0

고마워요 :) 두 번째 비트를 편집하고 싶을 수도 있습니다. 단 두 개의 매개 변수를 전달하고 끝 브래킷이 없습니다. – Annan

+3

여러 단락에 걸친 범위에 대해서는 작동하지 않습니다. ( – Thariama

9

는이 문제를 해결하기 위해 내가 함께했다 구현입니다
+0

코드의 대단한 부분 payam jabbari는 querySelectorAll의 사용이 깔끔하지 만, 저의 접근법에 대한 근본적인 문제는 노드를 복제한다는 것입니다. 돔, 반면에 직접 돔 조작을 제공하지 않습니다.이 대단히 감사합니다. – Pancho

1

내가 선택한 노드의 정확성을 향상시키기 위해 MikeB의 답변에 따라이 추가 수정을했다

<!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> 
1

문제 해결.

필자는 특히 여러 요소에 걸쳐 텍스트를 따라 커서를 드래그하여 범위 선택을 제외한 모든 선택 작업을 테스트합니다. 모든 (CMD + A)을 선택 타격 파이어 폭스에서

, 그것은 차이가 각각 첫 번째와 마지막 자식 노드의 인덱스 년대 startOffset & endOffset의에, & endContainer가의 contentEditable 사업부입니다 startContainer의 범위를 반환합니다.

Chrome에서 select all (CMD + A)를 누르면 startContainer가 contenteditable div의 첫 번째 자식 노드이고 endContainer가 contenteditable div의 마지막 자식 노드 인 범위가 반환됩니다.

내가 수정 한 내용은이 둘의 불일치를 해결합니다. 자세한 설명은 코드에서 주석을 볼 수 있습니다.

function getNextNode(node) { 
    if (node.firstChild) 
     return node.firstChild; 

    while (node) { 
     if (node.nextSibling) return node.nextSibling; 
     node = node.parentNode; 
    } 
} 

function getNodesInRange(range) { 

    // MOD #1 
    // When the startContainer/endContainer is an element, its 
    // startOffset/endOffset basically points to the nth child node 
    // where the range starts/ends. 
    var start = range.startContainer.childNodes[range.startOffset] || range.startContainer; 
    var end = range.endContainer.childNodes[range.endOffset] || range.endContainer; 
    var commonAncestor = range.commonAncestorContainer; 
    var nodes = []; 
    var node; 

    // walk parent nodes from start to common ancestor 
    for (node = start.parentNode; node; node = node.parentNode) 
    { 
     nodes.push(node); 
     if (node == commonAncestor) 
      break; 
    } 
    nodes.reverse(); 

    // walk children and siblings from start until end is found 
    for (node = start; node; node = getNextNode(node)) 
    { 
     // MOD #2 
     // getNextNode might go outside of the range 
     // For a quick fix, I'm using jQuery's closest to determine 
     // when it goes out of range and exit the loop. 
     if (!$(node.parentNode).closest(commonAncestor)[0]) break; 

     nodes.push(node); 
     if (node == end) 
      break; 
    } 

    return nodes; 
}; 
0

Annon, 훌륭한 작품. 나는 원래 플러스 스테판의 수정 내용을 다음과 수정했습니다.

또한 Range에 의존하지 않았습니다.이 알고리즘은 함수를 일반 알고리즘으로 변환하여 두 노드 사이를 이동합니다. 또한 모든 것을 하나의 함수로 묶었습니다.다른 솔루션에

생각 : cloneNode를 사용하여 JQuery와

  • 에 의존에

    • 관심되지 않음 하나가 필터링하는 동안 수행 할 수 있습니다 많은 작업을 방지하는 조각에 결과를 담고있다.
    • 복제 노드에서 시작 또는 끝 노드가 랩핑 노드 내에있을 수 있으므로 복제 된 조각에 querySelectAll을 사용하면 파서에 닫는 태그가 없을 수 있습니다.

    예 :

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

    노드가 "A"인 단락 시작 가정 및 엔드 노드가 "C"절 이다. 그 결과 복제 된 조각은 다음과 같습니다 :

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

    닫는 태그가 없습니다. DOM 구조가 펑키 한가?

    어쨌든 여기에는 필터 옵션이 포함되어있어 결과를 포함/제외시키기 위해 TRUE 또는 FALSE를 반환해야합니다.

    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

    bob. 이 함수는 startNode 및 endNode 만 반환합니다. 사이에있는 노드는 배열로 푸시되지 않습니다.

    while 루프는 getNextNode()에서 null을 반환하므로 블록이 실행되지 않습니다. 여기

    0

    기능이 하위 범위의 배열을 반환하면이다

    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; 
    

    }

    관련 문제