Javascript custom selection highlight

I am trying to find a way with javascript to highlight the text that the user selects when he presses some odd highlight button (as in <span style = "background-color: yellow"> selected text </span>). It should work only with WebKit or Firefox, but it seems to be almost impossible, since it should work in the following cases:

<p>this is text</p> <p>I eat food</p> 

When the user selects from the “text” through “I eat” in the browser (I can’t just put the failure there)

and this case:

 <span><span>this is text</span>middle text<span>this is text</span></span> 

When the user selects from "is text" to "this is" in the browser (even if you can wrap your highlighted spaces around each element in the selection, I would like you to try to highlight the highlighted middle text).

This problem does not seem to be solved anywhere, to be honest, I doubt it is possible.

It would be possible if you could get the Range that you get from the selection, as a string full of html that can be parsed and then replaced, but as far as I can tell, you can't get the raw html Range .. pity .

+14
javascript highlight
Nov 20 '08 at 10:06
source share
6 answers

This answer is probably a few years too late for you, but I ran into a similar problem and wanted to write it down here, since this is the first hit on google.

To repeat, the problem is that you just want to grab the Range object from your custom selection and surround it with a stylized div, for example:

 function highlightSelection() { var userSelection = window.getSelection().getRangeAt(0); highlightRange(userSelection); } function highlightRange(range) { var newNode = document.createElement("div"); newNode.setAttribute( "style", "background-color: yellow; display: inline;" ); range.surroundContents(newNode); } 

But as initial conditions of the parent, this is unsafe. It will work if the selection does not cross the borders of the elements, but it will cause a DOM error if the range created by the user selection is an unsafe range that crosses the borders of the HTML tags.




The solution is to create an array of smaller Range objects, none of which individually cross the element’s barrier, but collectively cover the range selected by the user. Each of these safe ranges can be distinguished as described above.

 function getSafeRanges(dangerous) { var a = dangerous.commonAncestorContainer; // Starts -- Work inward from the start, selecting the largest safe range var s = new Array(0), rs = new Array(0); if (dangerous.startContainer != a) for(var i = dangerous.startContainer; i != a; i = i.parentNode) s.push(i) ; if (0 < s.length) for(var i = 0; i < s.length; i++) { var xs = document.createRange(); if (i) { xs.setStartAfter(s[i-1]); xs.setEndAfter(s[i].lastChild); } else { xs.setStart(s[i], dangerous.startOffset); xs.setEndAfter( (s[i].nodeType == Node.TEXT_NODE) ? s[i] : s[i].lastChild ); } rs.push(xs); } // Ends -- basically the same code reversed var e = new Array(0), re = new Array(0); if (dangerous.endContainer != a) for(var i = dangerous.endContainer; i != a; i = i.parentNode) e.push(i) ; if (0 < e.length) for(var i = 0; i < e.length; i++) { var xe = document.createRange(); if (i) { xe.setStartBefore(e[i].firstChild); xe.setEndBefore(e[i-1]); } else { xe.setStartBefore( (e[i].nodeType == Node.TEXT_NODE) ? e[i] : e[i].firstChild ); xe.setEnd(e[i], dangerous.endOffset); } re.unshift(xe); } // Middle -- the uncaptured middle if ((0 < s.length) && (0 < e.length)) { var xm = document.createRange(); xm.setStartAfter(s[s.length - 1]); xm.setEndBefore(e[e.length - 1]); } else { return [dangerous]; } // Concat rs.push(xm); response = rs.concat(re); // Send to Console return response; } 

Then you can (appear) to highlight "User Choice" with this modified code:

 function highlightSelection() { var userSelection = window.getSelection().getRangeAt(0); var safeRanges = getSafeRanges(userSelection); for (var i = 0; i < safeRanges.length; i++) { highlightRange(safeRanges[i]); } } 



Note that you will probably need some CSS Fancier to make many disparate elements user friendly. Hope this ultimately helps someone else get tired on the Internet!

+31
Oct 10 '12 at 15:59
source share

Well, you can do this using DOM manipulation. This works in Firefox:

 var selection = window.getSelection(); var range = selection.getRangeAt(0); var newNode = document.createElement("span"); newNode.setAttribute("style", "background-color: pink;"); range.surroundContents(newNode); 

It seems to work in the current version of Safari. See https://developer.mozilla.org/en/DOM/range.surroundContents and http://www.w3.org/TR/2000/REC-DOM-Level-2-Traversal-Range-20001113/ranges. html

+8
Nov 25 '08 at 13:53
source share
 <html> <head> <script type="text/javascript"> function load(){ window.document.designMode = "On"; //run this in a button, will highlight selected text window.document.execCommand("hiliteColor", false, "#000"); } </script> </head> <body contentEditable="true" onload="load()"> this is text </body> </html> 
+4
Nov 20 '08 at 23:02
source share

This is my first post here, but looking through your answers will not be something like this work? I have a sample here: http://henriquedonati.com/projects/Extension/extension.html

 function highlightSelection() { var userSelection = window.getSelection(); for(var i = 0; i < userSelection.rangeCount; i++) { highlightRange(userSelection.getRangeAt(i)); } } function highlightRange(range) { var newNode = document.createElement("span"); newNode.setAttribute( "style", "background-color: yellow; display: inline;" ); range.surroundContents(newNode); } 
+4
Jul 31 '16 at 8:35
source share

Here is the complete code for highlighting text and underlining text

 <!DOCTYPE html> <html> <head> <style type="text/css"> .highlight { background-color: yellow; } #test-text::-moz-selection { /* Code for Firefox */ background: yellow; } #test-text::selection { background: yellow; } </style> </head> <body> <div id="div1" style="border: 1px solid #000;"> <div id="test-text"> <h1> Hello How are you </h1> <p > Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum. </p> </div> </div> <br /> </body> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script> <script type="text/javascript"> mouseXPosition = 0; $(document).ready(function () { $("#test-text").mousedown(function (e1) { mouseXPosition = e1.pageX;//register the mouse down position }); $("#test-text").mouseup(function (e2) { var highlighted = false; var selection = window.getSelection(); var selectedText = selection.toString(); var startPoint = window.getSelection().getRangeAt(0).startOffset; var endPoint = window.getSelection().getRangeAt(0).endOffset; var anchorTag = selection.anchorNode.parentNode; var focusTag = selection.focusNode.parentNode; if ((e2.pageX - mouseXPosition) < 0) { focusTag = selection.anchorNode.parentNode; anchorTag = selection.focusNode.parentNode; } if (selectedText.length === (endPoint - startPoint)) { highlighted = true; if (anchorTag.className !== "highlight") { highlightSelection(); } else { var afterText = selectedText + "<span class = 'highlight'>" + anchorTag.innerHTML.substr(endPoint) + "</span>"; anchorTag.innerHTML = anchorTag.innerHTML.substr(0, startPoint); anchorTag.insertAdjacentHTML('afterend', afterText); } }else{ if(anchorTag.className !== "highlight" && focusTag.className !== "highlight"){ highlightSelection(); highlighted = true; } } if (anchorTag.className === "highlight" && focusTag.className === 'highlight' && !highlighted) { highlighted = true; var afterHtml = anchorTag.innerHTML.substr(startPoint); var outerHtml = selectedText.substr(afterHtml.length, selectedText.length - endPoint - afterHtml.length); var anchorInnerhtml = anchorTag.innerHTML.substr(0, startPoint); var focusInnerHtml = focusTag.innerHTML.substr(endPoint); var focusBeforeHtml = focusTag.innerHTML.substr(0, endPoint); selection.deleteFromDocument(); anchorTag.innerHTML = anchorInnerhtml; focusTag.innerHTml = focusInnerHtml; var anchorafterHtml = afterHtml + outerHtml + focusBeforeHtml; anchorTag.insertAdjacentHTML('afterend', anchorafterHtml); } if (anchorTag.className === "highlight" && !highlighted) { highlighted = true; var Innerhtml = anchorTag.innerHTML.substr(0, startPoint); var afterHtml = anchorTag.innerHTML.substr(startPoint); var outerHtml = selectedText.substr(afterHtml.length, selectedText.length); selection.deleteFromDocument(); anchorTag.innerHTML = Innerhtml; anchorTag.insertAdjacentHTML('afterend', afterHtml + outerHtml); } if (focusTag.className === 'highlight' && !highlighted) { highlighted = true; var beforeHtml = focusTag.innerHTML.substr(0, endPoint); var outerHtml = selectedText.substr(0, selectedText.length - beforeHtml.length); selection.deleteFromDocument(); focusTag.innerHTml = focusTag.innerHTML.substr(endPoint); outerHtml += beforeHtml; focusTag.insertAdjacentHTML('beforebegin', outerHtml ); } if (!highlighted) { highlightSelection(); } $('.highlight').each(function(){ if($(this).html() == ''){ $(this).remove(); } }); selection.removeAllRanges(); }); }); function highlightSelection() { var selection; //Get the selected stuff if (window.getSelection) selection = window.getSelection(); else if (typeof document.selection != "undefined") selection = document.selection; //Get a the selected content, in a range object var range = selection.getRangeAt(0); //If the range spans some text, and inside a tag, set its css class. if (range && !selection.isCollapsed) { if (selection.anchorNode.parentNode == selection.focusNode.parentNode) { var span = document.createElement('span'); span.className = 'highlight'; span.textContent = selection.toString(); selection.deleteFromDocument(); range.insertNode(span); // range.surroundContents(span); } } } </script> </html> 

https://jsfiddle.net/Bilalchk123/1o4j0w2v/

+2
Aug 11 '17 at 7:29 on
source share

I had the same problem today by highlighting selected tags, starting with multiple tags. Decision:

  • Find a way to extract the selected part along with HTML tags .
  • Wrap the extracted part with a span and return it to the DOM .

Refer to the code below for details.

 function getRangeObject(selectionObject){ try{ if(selectionObject.getRangeAt) return selectionObject.getRangeAt(0); } catch(ex){ console.log(ex); } } document.onmousedown = function(e){ var text; if (window.getSelection) { /* get the Selection object */ userSelection = window.getSelection() /* get the innerText (without the tags) */ text = userSelection.toString(); /* Creating Range object based on the userSelection object */ var rangeObject = getRangeObject(userSelection); /* This extracts the contents from the DOM literally, inclusive of the tags. The content extracted also disappears from the DOM */ contents = rangeObject.extractContents(); var span = document.createElement("span"); span.className = "highlight"; span.appendChild(contents); /* Insert your new span element in the same position from where the selected text was extracted */ rangeObject.insertNode(span); } else if (document.selection && document.selection.type != "Control") { text = document.selection.createRange().text; } }; 
+1
Dec 31 '15 at 9:39
source share



All Articles