2008-12-11 31 views
7

Khi chọn khối văn bản (có thể trải rộng trên nhiều nút DOM), bạn có thể trích xuất văn bản và nút đã chọn bằng Javascript không?Nhận văn bản đã chọn và các nút đã chọn trên một trang?

Imagine mã HTML này:

<h1>Hello World</h1><p>Hi <b>there!</b></p> 

Nếu người dùng đã khởi xướng một sự kiện mouseDown bắt đầu từ "World ..." và sau đó một mouseUp thậm chí ngay sau khi "đó!", Tôi hy vọng nó sẽ trở lại :

Text : { selectedText: "WorldHi there!" }, 
Nodes: [ 
    { node: "h1", offset: 6, length: 5 }, 
    { node: "p", offset: 0, length: 16 }, 
    { node: "p > b", offset: 0, length: 6 } 
] 

Tôi đã thử đưa HTML vào văn bản nhưng điều đó sẽ chỉ đưa cho tôi văn bản đã chọn. Tôi đã không thử các yếu tố <canvas> nhưng đó có thể là một lựa chọn khác.

Nếu không phải JavaScript, có cách nào có thể sử dụng tiện ích mở rộng của Firefox không?

Trả lời

12

Bạn đang ở trong một chuyến đi gập ghềnh, nhưng điều này là hoàn toàn có thể. Vấn đề chính là IE và W3C trưng ra các giao diện hoàn toàn khác nhau cho các lựa chọn vì vậy nếu bạn muốn chức năng trình duyệt chéo thì về cơ bản bạn phải viết toàn bộ điều hai lần. Ngoài ra, một số chức năng cơ bản bị thiếu từ cả hai giao diện.

Kết nối nhà phát triển Mozilla có câu chuyện trên W3C selections. Microsoft có hệ thống của họ documented on MSDN. Tôi khuyên bạn nên bắt đầu tại introduction to ranges của PPK.

Dưới đây là một số chức năng cơ bản mà tôi tin rằng công việc:

// selection objects will differ between browsers 
function getSelection() { 
    return (msie) 
    ? document.selection 
    : (window.getSelection || document.getSelection)(); 
} 

// range objects will differ between browsers 
function getRange() { 
    return (msie) 
     ? getSelection().createRange() 
     : getSelection().getRangeAt(0) 
} 

// abstract getting a parent container from a range 
function parentContainer (range) { 
    return (msie) 
     ? range.parentElement() 
     : range.commonAncestorContainer; 
} 
+0

bạn chuyển cho parentContainer như thế nào? Tôi không làm cho nó hoạt động (phương pháp parentContainer) –

+0

Rất tiếc. Không phải rất rõ ràng nhưng đó phải là một phạm vi, tôi đã cố định tên biến. Tôi đã nghĩ rằng nó có thể được sử dụng như thế này: var container = parentContainer (getRange()); Điều này không có nghĩa là nó sẽ hoạt động 100%. Mã này được dự định là một ví dụ về loại công việc cần thiết để thực hiện việc này và có thể được tìm thấy. Bạn sẽ muốn hiểu các API bạn đang xử lý (xem liên kết). – Borgar

+0

'parentContainer()' là vô ích: hai nhánh không được đảm bảo trả về cùng một điều vì phương thức 'parentElement()' của IE 'TextRange' sẽ luôn trả về một phần tử trong khi' commonAncestorContainer' có thể là một nút văn bản. Ngoài ra, không cần phải có bất kỳ trình duyệt sniffing (như ngụ ý bằng cách sử dụng 'msie'): bạn có thể dễ dàng phát hiện các đối tượng và phương pháp bạn cần. –

0

Có một cách ngắn hơn nhiều nếu bạn chỉ muốn phạm vi.

function getRange(){ 
    return (navigator.appName=="Microsoft Internet Explorer") 
     ? document.selection.createRange().parentElement() 
     : (getSelection||document.getSelection)().getRangeAt(0).commonAncestorContainer 
} 
+1

Đây không phải là lý tưởng. Đầu tiên, trình duyệt sniff là vô ích, vì IE 9 và sau đó hỗ trợ các API 'Selection' và' Range' chuẩn và bạn cũng dễ dàng phát hiện các tính năng bạn cần trực tiếp. Thứ hai, hai nhánh không được đảm bảo trả về cùng một thứ: phương thức 'parentElement()' của IE 'TextRange' sẽ luôn trả về một phần tử trong khi' commonAncestorContainer' có thể là một nút văn bản. Thứ ba, việc đặt tên là lẻ: những gì hàm trả về là một nút, không phải là một phạm vi. –

+0

Ngắn hơn không nhất thiết phải tốt hơn. – displayname

7

thư viện Rangy tôi sẽ nhận được một phần của đường đó bằng thống nhất các API khác nhau trong IE < 9 và tất cả các trình duyệt chính khác, và bằng cách cung cấp một chức năng getNodes() trên các đối tượng Phạm vi của nó:

function getSelectedNodes() { 
    var selectedNodes = []; 
    var sel = rangy.getSelection(); 
    for (var i = 0; i < sel.rangeCount; ++i) { 
     selectedNodes = selectedNodes.concat(sel.getRangeAt(i).getNodes()); 
    } 
    return selectedNodes; 
} 

Tải văn bản đã chọn khá dễ dàng trong tất cả các trình duyệt. Trong Rangy nó chỉ

var selectedText = rangy.getSelection().toString(); 

Without Rangy:

function getSelectedText() { 
    var sel, text = ""; 
    if (window.getSelection) { 
     text = "" + window.getSelection(); 
    } else if ((sel = document.selection) && sel.type == "Text") { 
     text = sel.createRange().text; 
    } 
    return text; 
} 

Đối với offsets nhân vật, bạn có thể làm một cái gì đó như thế này cho bất kỳ nút node trong việc lựa chọn. Lưu ý điều này không nhất thiết thể hiện văn bản có thể nhìn thấy trong tài liệu vì không có tài khoản của các khoảng trống bị thu hẹp, văn bản ẩn qua CSS, văn bản được đặt bên ngoài luồng tài liệu thông thường qua CSS, ngắt dòng ngụ ý bởi <br> và các phần tử khối, cộng với sự tinh tế khác.

var sel = rangy.getSelection(); 
var selRange = sel.getRangeAt(0); 
var rangePrecedingNode = rangy.createRange(); 
rangePrecedingNode.setStart(selRange.startContainer, selRange.startOffset); 
rangePrecedingNode.setEndBefore(node); 
var startIndex = rangePrecedingNode.toString().length; 
rangePrecedingNode.setEndAfter(node); 
var endIndex = rangePrecedingNode.toString().length; 
alert(startIndex + ", " + endIndex); 
3

này trả về các nút lựa chọn như tôi hiểu nó: Khi tôi có

<p> ... </p><p> ... </p><p> ... </p><p> ... </p><p> ... </p>... 
<p> ... </p><p> ... </p><p> ... </p><p> ... </p><p> ... </p> 

rất nhiều nút và tôi chỉ chọn một vài sau đó tôi muốn chỉ những nút là trong danh sách.

function getSelectedNodes() { 
    // from https://developer.mozilla.org/en-US/docs/Web/API/Selection 
    var selection = window.getSelection(); 
    if (selection.isCollapsed) { 
    return []; 
    }; 
    var node1 = selection.anchorNode; 
    var node2 = selection.focusNode; 
    var selectionAncestor = get_common_ancestor(node1, node2); 
    if (selectionAncestor == null) { 
    return []; 
    } 
    return getNodesBetween(selectionAncestor, node1, node2); 
} 

function get_common_ancestor(a, b) 
{ 
    // from http://stackoverflow.com/questions/3960843/how-to-find-the-nearest-common-ancestors-of-two-or-more-nodes 
    $parentsa = $(a).parents(); 
    $parentsb = $(b).parents(); 

    var found = null; 

    $parentsa.each(function() { 
     var thisa = this; 

     $parentsb.each(function() { 
      if (thisa == this) 
      { 
       found = this; 
       return false; 
      } 
     }); 

     if (found) return false; 
    }); 

    return found; 
} 

function isDescendant(parent, child) { 
    // from http://stackoverflow.com/questions/2234979/how-to-check-in-javascript-if-one-element-is-a-child-of-another 
    var node = child; 
    while (node != null) { 
     if (node == parent) { 
      return true; 
     } 
     node = node.parentNode; 
    } 
    return false; 
} 

function getNodesBetween(rootNode, node1, node2) { 
    var resultNodes = []; 
    var isBetweenNodes = false; 
    for (var i = 0; i < rootNode.childNodes.length; i+= 1) { 
    if (isDescendant(rootNode.childNodes[i], node1) || isDescendant(rootNode.childNodes[i], node2)) { 
     if (resultNodes.length == 0) { 
     isBetweenNodes = true; 
     } else { 
     isBetweenNodes = false; 
     } 
     resultNodes.push(rootNode.childNodes[i]); 
    } else if (resultNodes.length == 0) { 
    } else if (isBetweenNodes) { 
     resultNodes.push(rootNode.childNodes[i]); 
    } else { 
     return resultNodes; 
    } 
    }; 
if (resultNodes.length == 0) { 
    return [rootNode]; 
    } else if (isDescendant(resultNodes[resultNodes.length - 1], node1) || isDescendant(resultNodes[resultNodes.length - 1], node2)) { 
    return resultNodes; 
    } else { 
    // same child node for both should never happen 
    return [resultNodes[0]]; 
    } 
} 

Mã nên có sẵn tại địa chỉ: https://github.com/niccokunzmann/spiele-mit-kindern/blob/gh-pages/javascripts/feedback.js

tôi đăng câu trả lời này ở đây vì tôi đã có thể thích để tìm thấy nó ở đây.

0

Tất cả mã tuân thủ tiêu chuẩn hoạt động trong IE11 +.

chữ Chuỗi

window.getSelection().getRangeAt(0).toString() 

Các bắt đầu nút (ngay cả khi văn bản được chọn ngược):

window.getSelection().anchorNode 

Nút cuối (ngay cả khi văn bản được chọn ngược):

window.getSelection().focusNode 

Bạn có muốn biết thêm không? Chọn một số văn bản và chạy JavaScript sau trong bảng điều khiển:

console.log(window.getSelection()); 
console.log(window.getSelection().getRangeAt(0)); 
Các vấn đề liên quan