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!
underemployedJD Oct 10 '12 at 15:59 2012-10-10 15:59
source share