Get selected text and selected nodes on a page?

When choosing a block of text (possibly spanning many DOM nodes), is it possible to extract the selected text and nodes using Javascript?

Imagine this HTML code:

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

If the user triggered a mouseDown event starting with "World ..." and then mouseUp even immediately after "there!", I hope he returns:

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

I tried putting HTML in a text box, but that will only give me the selected text. I have not tried the <canvas> , but this may be another option.

If not JavaScript, is there a way that you can use the Firefox extension?

+7
javascript firefox xhtml firefox-addon selection
Dec 11 '08 at 21:56
source share
6 answers

You are on a bumpy ride, but it is possible. The main problem is that IE and W3C expose completely different interfaces for selection, so if you want to use the cross browser function, you basically need to write all this twice. In addition, some basic functions are missing on both interfaces.

The Mozilla Developer Connection contains a story about choosing a W3C . Microsoft has its own documentation system on MSDN . I recommend starting with a PPK introduction to ranges .

Here are some basic features that I think work:

 // 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; } 
+12
Dec 12 '08 at 23:19
source share

My Rangy library will get your part of the way by combining various APIs in IE <9 and all other major browsers and providing the getNodes() function for Range objects:

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

Getting the selected text is pretty easy in all browsers. In Rangy, it's just

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

Regarding character offsets, you can do something similar for any node node in the selection. Please note that this does not necessarily represent visible text in the document, because it does not take into account collapsed spaces, text is hidden through CSS, text outside the normal flow of documents through CSS, line breaks are implied by <br> and block elements, as well as others subtleties.

 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); 
+7
Apr 09 '12 at 15:55
source share

This returns the selected nodes, as I understand it: When I have

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

there are many nodes, and I select only a few, then I want only these nodes in the list.

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

The code should be available at: https://github.com/niccokunzmann/spiele-mit-kindern/blob/gh-pages/javascripts/feedback.js

I posted this answer here because I would like to find it here.

+3
Dec 02 '13 at 19:20
source share
0
Dec 11 '08 at 22:26
source share

There is a much shorter way if you just want a range.

 function getRange(){ return (navigator.appName=="Microsoft Internet Explorer") ? document.selection.createRange().parentElement() : (getSelection||document.getSelection)().getRangeAt(0).commonAncestorContainer } 
0
Jan 25 2018-12-15T00:
source share

All standards compatible with code that work in IE11 +.

Text string

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

start node (even if the text is selected in the reverse order):

 window.getSelection().anchorNode 

end of node (even if text is selected in reverse order):

 window.getSelection().focusNode 

Want to know more? Select the text and run the following JavaScript in the console:

 console.log(window.getSelection()); console.log(window.getSelection().getRangeAt(0)); 
0
Feb 14 '18 at 13:53
source share



All Articles