Trouble with getSelection() [duplicate] - javascript

I'm trying to find a way with javascript to highlight the text the user selects when they click some odd highlight button (as in <span style="background-color:yellow">highlighted text</span>). It only has to work with either WebKit or Firefox, but it seems to be well nigh impossible because it has to work in the following cases:
<p>this is text</p>
<p>I eat food</p>
When the user selects from "is text" through "I eat" in the browser (can't just put a span 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 though you can wrap your highlight spans around each element in the selection, I'd like to see you try to get that middle text highlighted).
This problem doesn't seem to be solved anywhere, frankly I doubt it's possible.
It would be possible if you could get the Range that you get from the selection as a string complete with html which could be parsed and then replaced, but as far as I can tell you can't get the raw html of a Range.. pity.

This answer is probably a few years too late for you, but I faced a similar problem and wanted to document it here, since it is the first hit on google.
To reiterate, the problem is that you would like to just capture the Range object from the User Selection and surround it with a styled div, like so:
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 Original Parent states, this is unsafe. It will work if the selection does not cross element boundaries, but it will throw a DOM eror if the Range created by the User Selection is an unsafe range which crosses the boundaries of HTML tags.
The solution is to produce an array of smaller Range objects, none of which individually crosses an element barrier, but which collectively cover the Range selected by the user. Each of these safe Ranges can be highlighted as 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;
}
It is then possible to (appear to) highlight the User Selection, 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'' probably need some fancier CSS to make the many disparate elements a user could look nice together. I hope that eventually this helps some other weary soul on the internet!

Well, you can do it 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);
Seems to work in the current version of Safari as well. 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

This is my first time posting here, but looking through your answers, wouldn't 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);
}

Here is a complete code to highlight and dehighlight the 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's 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/

function load(){
window.document.designMode = "On";
//run this in a button, will highlight selected text
window.document.execCommand("hiliteColor", false, "#768");
}
<html>
<head>
</head>
<body contentEditable="true" onload="load()">
this is text
</body>
</html>

I just finished releasing a package that is a typescript port of texthighlighter (a deprecated library). Just converting it to typescript has caught a few bugs and made it easier to work on in the future. Checkout https://www.npmjs.com/package/#funktechno/texthighlighter. This has no dependencies and allows for highlighting user selection, merging highlights, removinging highlights, serializing and deserializing (applying from data) highlights.
Note you will need to use the javascript mouseup event to properly trigger it.
import { doHighlight, deserializeHighlights, serializeHighlights, removeHighlights, optionsImpl } from "#/../node_modules/#funktechno/texthighlighter/lib/index";
const domEle = document.getElementById("sandbox");
const options: optionsImpl = {};
if (this.color) options.color = this.color;
if (domEle) doHighlight(domEle, true, options);
this is how I triggered it in a vue ts project
<div
id="sandbox"
#mouseup="runHighlight($event)"
>text to highlight</div>

I was having the same problem today, highlighting the selected tags ranging over multiple tags.
The solution:
Find a way to extract the selected portion along with the HTML tags.
Wrap the extracted portion with a span element and put it back in the DOM .
Refer the code below , for further clarification.
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;
}
};

since HTML use <mark> element as highlighted text, maybe it's easy to use this node, instead of using your own css, much more clean code:
function highlightRange(range) {
var newNode = document.createElement('mark');
range.surroundContents(newNode);
}
// original select range function
function highlight() {
var userSelection = window.getSelection();
for(var i = 0; i < userSelection.rangeCount; i++) {
highlightRange(userSelection.getRangeAt(i));
}
}

Related

Firefox triple click selection returns incorrect start and end offsets

I have this piece of code which is supposed to return the start and end offsets of the user selection:
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript">
function getSelRange() {
var selObj = window.getSelection();
var range = selObj.getRangeAt(0);
alert(range.startOffset+"-"+range.endOffset);
}
</script>
</head>
<body>
<button onclick="getSelRange()">Get the selected text!</button>
<p>Select some text!</p>
</body>
</html>
When I select some text from the p by dragging, it alerts the numbers correctly. However, when I select the entire text in the p by triple clicking on it, it alerts 0-1. Looks like in Firefox only, triple click doesn't return the selection range correctly.
How do I get the correct start and end points on triple click as well?
Firefox is returning the correct range. The problem is that your assumption that a range must have its start and end boundaries relative to a text node is incorrect.
What is happening is that Firefox is reporting the range as starting before the zeroth child node of the <p> element and ending after the first child of the <p> element. This is perfectly valid.
You could do something like the following to adjust such a range to make its boundaries lie inside the text node within the <p> element:
Demo: http://jsfiddle.net/DbmjH/2/
Code:
function adjustRange(range) {
range = range.cloneRange();
if (range.startContainer.nodeType != 3) {
var nodeAfterStart = range.startContainer.childNodes[range.startOffset];
if (nodeAfterStart && nodeAfterStart.nodeType == 3) {
range.setStart(nodeAfterStart, 0);
}
}
if (range.endContainer.nodeType != 3 && range.endOffset >= 1) {
var nodeBeforeEnd = range.endContainer.childNodes[range.endOffset - 1];
if (nodeBeforeEnd && nodeBeforeEnd.nodeType == 3) {
range.setEnd(nodeBeforeEnd, nodeBeforeEnd.data.length);
}
}
return range;
}
Check this fiddle ( Updated )
I have made it in a way that it will work on triple click when paragraph contains multiple lines.
<script>
function getSelRange() {
var selObj = window.getSelection();
var range = selObj.getRangeAt(0);
var r = document.getElementById('txt').innerHTML.split('<br>');
if (r[(range.endOffset-1)/2] == selObj) {
alert(0+"-"+r[(range.endOffset-1)/2].length);
} else if (range.startOffset >= range.endOffset) {
alert(range.startOffset + "-" + r[(range.endOffset-1)/2].length);
} else {
alert(range.startOffset + "-" + range.endOffset);
}
}
</script>
New Added Fiddle
<script>
function getSelRange() {
var selObj = window.getSelection();
var range = selObj.getRangeAt(0);
var r=document.getElementById('txt').innerHTML.split('<br>');
var selLines = selObj.toString().split('\n');
var Str = document.getElementById('txt').innerHTML;
Str=Str.replace(/<br>/g,"xzznlzzx");
var pr=selObj.toString().replace(/\r?\n/g,"xzznlzzx");
var rStr=Str.substring(0,Str.indexOf(pr));
var rSplit=rStr.split('xzznlzzx');
var prSplit=pr.split('xzznlzzx');
var countStart=0;
var countEnd=0;
var i=0;
for(;i<(rSplit.length-1);i++)
{
countStart=countStart+r[i].length;
}
for(j=0;j<(prSplit.length-1);i++,j++)
{
countEnd=countEnd+r[i].length;
}
countEnd=countEnd+countStart;
if(r[(range.endOffset-1)/2]==selObj)
{
alert((0+countStart)+"-"+(r[(range.endOffset-1)/2].length+countEnd));
}
else{
if(r[i].length<selObj.toString().length)
{
var indx = selObj.toString().indexOf(r[i]);
}
else{
var indx = r[i].indexOf(selObj.toString());
var vals=selObj.toString().length;
var res = r[i].substring(indx+vals,indx+vals+1);
if(res==""){indx=1}
else{indx=-1}
}
if(indx!=-1)
{
alert((range.startOffset+countStart)+"-"+(r[i].length+countEnd));
}
else{
alert((range.startOffset+countStart)+"-"+(range.endOffset+countEnd));
}
}
}
</script>
Note : for the above fiddles to work the string within <p> tag must be in a single line otherwise it will add the extra spaces between words to the range.

IE8 does not return selected text using mouse pointer

I'm using this code to select text from the window document. This code is working fine in all browsers: it returns the selected text, but in IE8 it does not give the selected text. Instead it gives the whole HTML of the selected line. Can anybody give me solution for this?
Example:
<B><U><SPAN style="LINE-HEIGHT: 150%; FONT-FAMILY: 'Arial',
'sans-serif'; FONT-SIZE: 12pt">Summary</SPAN></U></B>
I want only Summary so all major browser return this except IE8.
<script type="text/javascript">
function getSelectionText(id) {
var html = "";
if (typeof window.getSelection != "undefined") {
var sel = window.getSelection();
if (sel.rangeCount) {
var container = document.createElement("div");
for (var i = 0, len = sel.rangeCount; i < len; ++i) {
container.appendChild(sel.getRangeAt(i).cloneContents());
}
html = container.innerHTML;
}
} else if (typeof document.selection != "undefined") {
if (document.selection.type == "Text") {
html = document.selection.createRange().htmlText;
}
}
document.getElementById(id).value = html;
//document.getElementById(id).value = html;
}
</script>
You should look into rangy - A cross-browser JavaScript range and selection library. In their own words:
It provides a simple standards-based API for performing common DOM Range and Selection tasks in all major browsers, abstracting away the wildly different implementations of this functionality between Internet Explorer up to and including version 8 and DOM-compliant browsers.
Change
html = container.innerHTML;
into
html = container.innerText;
The issue is with the following line in IE8
html = document.selection.createRange().htmlText;
Change it to this
html = document.selection.createRange().text;
Below is a nice simple function which seems to work well for me
function getSelectedText() {
var txt = '';
if (window.getSelection) {
txt = window.getSelection();
}
else if (document.getSelection) {
txt = document.getSelection();
}
else if (document.selection) {
txt = document.selection.createRange().text;
}
else return;
return txt;
}
Hope this helps

How to be sure selected text doesn't contain a specific tag/class

I have the following function to add a span tag with selected text.
function Add() {
var selObj = window.getSelection();
var selRange = selObj.getRangeAt(0);
var newElement = document.createElement("span");
newElement.setAttribute("class", "cls1");
var documentFragment = selRange.extractContents();
newElement.appendChild(documentFragment);
selRange.insertNode(newElement);
selObj.removeAllRanges();
}
It works fine. But I want to prevent adding a new span inside another one.
Add Span
<div id="content">
Lorem ipsum dolor sit amet, <span class="cls1">consectetur adipisicing</span> elit,
</div>
If the user selects "adipisicing elit" I don't want to create a new span as "adipisicing" is inside another one. So how can I know whether the selected text includes any part of other span?
Thanks.
Here's one approach, that tests if the current selection is directly part of a <span class="cls1">. You can add it after the first line of your function, since it uses selObj as a starting point.
if ("cls1"!==selObj.anchorNode.parentNode.className) {
If it has to be a span:
if ("span"!==selObj.anchorNode.parentNode.tagName.toLowerCase()) {
To make sure BOTH ends of the selection are outside a span, use focusNode as well:
if ("span"!==selObj.anchorNode.parentNode.tagName.toLowerCase() &&
("span"!==selObj.focusNode.parentNode.tagName.toLowerCase()) {
Combining everything:
if ("cls1"!==selObj.anchorNode.parentNode.className &&
"span"!==selObj.anchorNode.parentNode.tagName.toLowerCase() &&
"cls1"!==selObj.focusNode.parentNode.className &&
"span"!==selObj.focusNode.parentNode.tagName.toLowerCase()) {
http://jsfiddle.net/mblase75/Zg5LW/
https://developer.mozilla.org/en-US/docs/DOM/Selection/anchorNode
https://developer.mozilla.org/en-US/docs/DOM/Selection/focusNode
At last I figured out how to achieve this. To sum up I need to check :
1. Whether selection is inside a specific class ("cls1") element.
2. Whether selection contains one or more specific class ("cls1") element.
3. Whether selection contains some part of a specific class ("cls1") element.
Thanks to #Blazemonger's code I could achieve 1. and 3. items:
if ("cls1" == selObj.anchorNode.parentNode.className || "cls1" == selObj.focusNode.parentNode.className)
{ return true; }
And two I added the following part which checks for 2. and 3. items:
var docFrag = selRange.cloneContents();
var childNodes = docFrag.childNodes;
for (var i = 0; i < childNodes.length; i++) {
var nodee = childNodes[i];
if ($(nodee).hasClass("cls1") || $(nodee).find(".cls1").length > 0)
{ return true; }
}
So I wrote a checking function combining the two:
function ContainsDiv(selRange)
{
var docFrag = selRange.cloneContents();
var childNodes = docFrag.childNodes;
for (var i = 0; i < childNodes.length; i++) {
var nodee = childNodes[i];
if ($(nodee).hasClass("cls1") || $(nodee).find(".cls1").length > 0)
{ return true; }
if ("cls1" == selObj.anchorNode.parentNode.className || "cls1" == selObj.focusNode.parentNode.className)
{ return true; }
}
And I used this function inside Add() :
function Add() {
var selObj = window.getSelection();
var selRng = selObj.getRangeAt(0);
if (ContainsDiv(selRng)) {
return;
}
var newElement = document.createElement("span");
newElement.setAttribute("class", "cls1");
var documentFragment = selRng.extractContents();
newElement.appendChild(documentFragment);
selRng.insertNode(newElement);
selObj.removeAllRanges();
}
var parentNode = selObj.parentNode;
if (parentNode.nodeName == "SPAN")
{
//don't add
}

apply Highlight HTML text using javascript as in Microsoft word

I have the following code for highlighting html text using javascript:
var counter = 1;
function BindEventForHighlight() {
$('#ParaFontSize').bind("mouseup", HighlightText);
$('#ParaFontSize').unbind("mouseup", RemoveHighlight);
}
function UnBindHighlightEvent() {
$('#ParaFontSize').bind("mouseup", RemoveHighlight);
$('#ParaFontSize').unbind("mouseup", HighlightText);
}
function HighlightText() {
var text = window.getSelection();
var start = text.anchorOffset;
var end = text.focusOffset - text.anchorOffset;
range = window.getSelection().getRangeAt(0);
range1 = window.getSelection().toString();
var selectionContents = range.extractContents();
var span = document.createElement("span");
span.appendChild(selectionContents);
span.setAttribute("id", counter);
span.setAttribute("class", "highlight");
range.insertNode(span);
counter++;
}
function RemoveAllHighlights() {
var selection = document.getElementById('ParaFontSize');
var spans = selection.getElementsByTagName("span");
for (var i = 0; i < spans.length; i++) {
spans[i].className = "";
}
}
function RemoveHighlight() {
var selection = window.getSelection();
if (selection.toString() !== "" &&
selection.anchorNode.parentNode.nodeName === "SPAN") {
selection.anchorNode.parentNode.className = "";
}
else {
return false;
}
}
The HighlightText function uses range object to highlight text. and i want to save start and end of selected text into Data base, and when user get back to the same page he will see the highlighted text from previous time. also i want other scenarios as follow:
when the user highlight some text, he will be able to unhighlight all or part of this highlighted text and keep the remaining text highlighted.
the user able to clear all highlighted text in the page.
let's say i want this highlighter functions as the highlighter in MS word.
Thanks in advance
Really appreciated.

getSelection without alt attribute and scripts in it?

I'm using window.getSelection () to get the selected text.
But, if i select an image too, it returns also altof an image.
EXAMPLE:
<img src="someSrc.jpg" alt="image_alt" /> My text here ...
if i select an image too, it returns
image_alt My text here ...
But i need only
My text here ...
Is there any way to get only text, without alt?
Thanks much
Try this:
window.getTextSelection = function() {
//First get HTML Fragment of selection
var html = window.getSelection().getRangeAt(0).cloneContents();
//Return only the text
return html.textContent||html.innerText;
}
In some cases you can simply disable the user selection via CSS:
May you also can achieve this by disabling user-select for images:
img {
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-o-user-select: none;
user-select: none;
}
The easiest way would be to use the toString() method of selection's Range(s), which is what window.getSelection().toString() is specified to do in the current version of WHATWG's new Range spec (although this is contrary to what some browsers do and may or may not change). The following will work with multiple selected ranges (which Mozilla supports) and also in IE < 9.
jsFiddle: http://jsfiddle.net/timdown/HkP2S/
Code:
function getSelectionRangeText() {
var selText = "";
if (window.getSelection) {
var sel = window.getSelection(), rangeCount = sel.rangeCount;
if (rangeCount) {
for (var i = 0, rangeTexts = []; i < rangeCount; ++i) {
rangeTexts.push("" + sel.getRangeAt(i));
}
selText = rangeTexts.join("");
}
} else if (document.selection && document.selection.type == "Text") {
selText = document.selection.createRange().text;
}
return selText;
}
UPDATE
This solution includes text inside <script> and <style> elements. To remove this, you could use cloneContents() on the selection ranges and traverse the DOM of the resulting document fragments, collecting text only from text nodes not contained within <script> and <style> elements. You could also expand on this to remove text that is inside elements with CSS display: none.
jsFiddle: http://jsfiddle.net/timdown/HkP2S/2/
Code:
function getSelectionRangeText() {
var selText = "", selTextParts = [];
function getNodeText(node) {
if (node.nodeType == 3) {
selTextParts.push(node.data);
} else if (node.hasChildNodes()
&& !(node.nodeType == 1 && /^(script|style)$/i.test(node.tagName))) {
for (var child = node.firstChild; !!child; child = child.nextSibling) {
getNodeText(child);
}
}
}
if (window.getSelection) {
var sel = window.getSelection(), rangeCount = sel.rangeCount;
if (rangeCount) {
for (var i = 0; i < rangeCount; ++i) {
getNodeText(sel.getRangeAt(i).cloneContents());
}
selText = selTextParts.join("");
}
} else if (document.selection && document.selection.type == "Text") {
selText = document.selection.createRange().text;
}
return selText;
}

Categories

Resources