2009-03-20 59 views
14

Tôi đang cố gắng lấy tất cả các nút DOM nằm trong một đối tượng phạm vi, cách tốt nhất để làm điều này là gì?Làm thế nào để có được các nút nằm bên trong một phạm vi với javascript?

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*/; 

Tôi đã suy nghĩ một cách trong vài giờ qua và đến với điều này:

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

Tuy nhiên khi nút cuối cùng là phụ huynh của nút start nó sẽ trả về tất cả mọi thứ trên trang. Tôi chắc chắn tôi đang nhìn thấy một cái gì đó hiển nhiên? Hoặc có thể đi về nó một cách hoàn toàn sai lầm.

MDC/DOM/range

+2

'var c = getSelection() getRangeAt (0) .cloneContents(). c.querySelectorAll ('*') ' – caub

Trả lời

11

Các getNextNode sẽ bỏ endNode mong muốn của bạn một cách đệ quy nếu nó một nút cha.

Thực hiện việc kiểm tra phá vỡ điều kiện bên trong của getNextNode thay vì:

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

và trong câu lệnh while:

while (startNode = getNextNode(startNode, false , endNode)); 
+0

Cảm ơn :) Có thể muốn chỉnh sửa bit thứ hai, mặc dù nó chỉ truyền trong hai tham số và thiếu khung kết thúc. – Annan

+3

không hoạt động cho các phạm vi trải rộng trên một vài đoạn văn: ( – Thariama

9

Dưới đây là một thực hiện tôi đã đưa ra để giải quyết này:

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; 
} 
+0

là một đoạn mã tuyệt vời. Trong khi đó, thanh toán của jabbari dưới đây sử dụng truy vấnSelectorAll là gọn gàng, vấn đề cơ bản với cách tiếp cận của tôi đối với tôi là nó nhân bản các nút. dom, trong khi bạn không và do đó cung cấp thao tác dom trực tiếp.Cảm ơn rất nhiều vì điều này. – Pancho

1

mã dưới đây giải quyết vấn đề của bạn

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

tôi đã thực hiện 2 bản vá bổ sung dựa trên câu trả lời của MikeB để cải thiện tính chính xác của các nút chọn.

Tôi đặc biệt thử nghiệm điều này trên tất cả các hoạt động đã chọn, ngoài lựa chọn phạm vi được thực hiện bằng cách kéo con trỏ dọc theo văn bản trải rộng trên nhiều phần tử.

Trong Firefox, nhấn chọn tất cả (CMD + A) trả về một phạm vi nơi bắt đầuContainer & endContainer là div có thể được chỉnh sửa, sự khác biệt nằm trong startOffset & endOffset trong đó nó tương ứng với chỉ mục của nút con đầu tiên và cuối cùng.

Trong Chrome, nhấn chọn tất cả (CMD + A) trả về phạm vi bắt đầuContainer là nút con đầu tiên của div có thể chỉnh sửa nội dung và endContainer là nút con cuối cùng của div có thể chỉnh sửa nội dung.

Các sửa đổi Tôi đã thêm công việc xung quanh sự khác biệt giữa hai điều này. Bạn có thể xem các nhận xét trong mã để giải thích thêm.

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, công việc tuyệt vời. Tôi đã sửa đổi bản gốc cộng với sửa đổi của Stefan trong những điều sau đây.

Ngoài ra, tôi đã xóa sự phụ thuộc vào Phạm vi, tính năng này chuyển đổi hàm thành thuật toán chung để đi bộ giữa hai nút. Thêm vào đó, tôi đã bọc mọi thứ vào một chức năng duy nhất.

Suy nghĩ về các giải pháp khác:

  • Không quan tâm đến dựa vào jquery
  • Sử dụng cloneNode thang máy các kết quả để một mảnh vỡ, trong đó ngăn chặn nhiều hoạt động người ta có thể muốn tiến hành trong lọc.
  • Sử dụng truy vấnSelectAll trên một đoạn được nhân bản vô ích vì nút bắt đầu hoặc nút kết thúc có thể nằm trong nút gói, do đó trình phân tích cú pháp có thể không có thẻ đóng?

Ví dụ:

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

Giả sử bắt đầu nút là "A" đoạn, và nút cuối cùng là "C" đoạn . Đoạn kết quả được nhân bản sẽ là:

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

và chúng tôi đang thiếu thẻ đóng? dẫn đến cấu trúc DOM sôi nổi?

Dù sao, đây là hàm, bao gồm tùy chọn bộ lọc, sẽ trả về TRUE hoặc FALSE để bao gồm/loại trừ khỏi kết quả.

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. hàm chỉ trả về startNode và endNode. các nút ở giữa không được đẩy vào mảng.

dường như vòng lặp while trả về null trên getNextNode() do đó khối đó không bao giờ được thực hiện.

0

đây là chức năng trả lại cho bạn hàng loạt các phụ dãy

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; 

}

Các vấn đề liên quan