How to Highlight the Text of the Dom Range Object

How can I highlight the text of the DOM Range object?

I would suggest using document's or the TextRange's execCommand method, which is built for just such a purpose, but is usually used in editable documents. Here's the answer I gave to a similar question:

The following should do what you want. In non-IE browsers it turns on designMode, applies a background colour and then switches designMode off again.

UPDATE

Fixed to work in IE 9.

UPDATE 12 September 2013

Here's a link detailing a method for removing highlights created by this method:

https://stackoverflow.com/a/8106283/96100

function makeEditableAndHighlight(colour) {
var range, sel = window.getSelection();
if (sel.rangeCount && sel.getRangeAt) {
range = sel.getRangeAt(0);
}
document.designMode = "on";
if (range) {
sel.removeAllRanges();
sel.addRange(range);
}
// Use HiliteColor since some browsers apply BackColor to the whole block
if (!document.execCommand("HiliteColor", false, colour)) {
document.execCommand("BackColor", false, colour);
}
document.designMode = "off";
}

function highlight(colour) {
var range;
if (window.getSelection) {
// IE9 and non-IE
try {
if (!document.execCommand("BackColor", false, colour)) {
makeEditableAndHighlight(colour);
}
} catch (ex) {
makeEditableAndHighlight(colour)
}
} else if (document.selection && document.selection.createRange) {
// IE <= 8 case
range = document.selection.createRange();
range.execCommand("BackColor", false, colour);
}
}

highlight the text of the DOM range element,

The execCommand method is a method of the document, not the Range. Also, hilitecolor only works in Firefox, so you should fall back to using backcolor in WebKit and Opera.

UPDATE

Fixed in IE 9.

function makeEditableAndHighlight(colour) {
var sel = window.getSelection();
var range = null;
if (sel.rangeCount && sel.getRangeAt) {
range = sel.getRangeAt(0);
}
document.designMode = "on";
if (range) {
sel.removeAllRanges();
sel.addRange(range);
}
// Use HiliteColor since some browsers apply BackColor to the whole block
if (!document.execCommand("HiliteColor", false, colour)) {
document.execCommand("BackColor", false, colour);
}
document.designMode = "off";
}

function highlight(colour) {
var range, sel;
if (window.getSelection) {
// IE9 and non-IE
try {
if (!document.execCommand("BackColor", false, colour)) {
makeEditableAndHighlight(colour);
}
} catch (ex) {
makeEditableAndHighlight(colour)
}
} else if (document.selection && document.selection.createRange) {
// IE <= 8 case
range = document.selection.createRange();
range.execCommand("BackColor", false, colour);
}
}

Highlight text range using JavaScript

Below is a function to set the selection to a pair of character offsets within a particular element. This is naive implementation: it does not take into account any text that may be made invisible (either by CSS or by being inside a <script> or <style> element, for example) and may have browser discrepancies (IE versus everything else) with line breaks, and takes no account of collapsed whitespace (such as 2 or more consecutive space characters collapsing to one visible space on the page). However, it does work for your example in all major browsers.

For the other part, the highlighting, I'd suggest using document.execCommand() for that. You can use my function below to set the selection and then call document.execCommand(). You'll need to make the document temporarily editable in non-IE browsers for the command to work. See my answer here for code: getSelection & surroundContents across multiple tags

Here's a jsFiddle example showing the whole thing, working in all major browsers: http://jsfiddle.net/8mdX4/1211/

And the selection setting code:

function getTextNodesIn(node) {
var textNodes = [];
if (node.nodeType == 3) {
textNodes.push(node);
} else {
var children = node.childNodes;
for (var i = 0, len = children.length; i < len; ++i) {
textNodes.push.apply(textNodes, getTextNodesIn(children[i]));
}
}
return textNodes;
}

function setSelectionRange(el, start, end) {
if (document.createRange && window.getSelection) {
var range = document.createRange();
range.selectNodeContents(el);
var textNodes = getTextNodesIn(el);
var foundStart = false;
var charCount = 0, endCharCount;

for (var i = 0, textNode; textNode = textNodes[i++]; ) {
endCharCount = charCount + textNode.length;
if (!foundStart && start >= charCount
&& (start < endCharCount ||
(start == endCharCount && i <= textNodes.length))) {
range.setStart(textNode, start - charCount);
foundStart = true;
}
if (foundStart && end <= endCharCount) {
range.setEnd(textNode, end - charCount);
break;
}
charCount = endCharCount;
}

var sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
} else if (document.selection && document.body.createTextRange) {
var textRange = document.body.createTextRange();
textRange.moveToElementText(el);
textRange.collapse(true);
textRange.moveEnd("character", end);
textRange.moveStart("character", start);
textRange.select();
}
}

Javascript Highlight Selected Range Button

The following should do what you want. In non-IE browsers it turns on designMode, applies a background colour and then switches designMode off again.

UPDATE

Fixed to work in IE 9.

function makeEditableAndHighlight(colour) {
sel = window.getSelection();
if (sel.rangeCount && sel.getRangeAt) {
range = sel.getRangeAt(0);
}
document.designMode = "on";
if (range) {
sel.removeAllRanges();
sel.addRange(range);
}
// Use HiliteColor since some browsers apply BackColor to the whole block
if (!document.execCommand("HiliteColor", false, colour)) {
document.execCommand("BackColor", false, colour);
}
document.designMode = "off";
}

function highlight(colour) {
var range, sel;
if (window.getSelection) {
// IE9 and non-IE
try {
if (!document.execCommand("BackColor", false, colour)) {
makeEditableAndHighlight(colour);
}
} catch (ex) {
makeEditableAndHighlight(colour)
}
} else if (document.selection && document.selection.createRange) {
// IE <= 8 case
range = document.selection.createRange();
range.execCommand("BackColor", false, colour);
}
}

How to highlight text using javascript

You can use the jquery highlight effect.

But if you are interested in raw javascript code, take a look at what I got
Simply copy paste into an HTML, open the file and click "highlight" - this should highlight the word "fox". Performance wise I think this would do for small text and a single repetition (like you specified)

function highlight(text) {
var inputText = document.getElementById("inputText");
var innerHTML = inputText.innerHTML;
var index = innerHTML.indexOf(text);
if (index >= 0) {
innerHTML = innerHTML.substring(0,index) + "<span class='highlight'>" + innerHTML.substring(index,index+text.length) + "</span>" + innerHTML.substring(index + text.length);
inputText.innerHTML = innerHTML;
}
}
.highlight {
background-color: yellow;
}
<button onclick="highlight('fox')">Highlight</button>

<div id="inputText">
The fox went over the fence
</div>

Highlight text in HTML by int offset (start-end) without knowing actual textNodes

You can use a TreeWalker to get all the textNodes from your main div and then go through their textContent's length to determine where your start and end points should be :

var mainDiv = document.getElementById('main');// create the treewalker which will accept all textNodesvar treeWalker = document.createTreeWalker(mainDiv,NodeFilter.SHOW_TEXT,null,false);// the array containing our textNodesvar textNodeList = [];while(treeWalker.nextNode()) textNodeList.push(treeWalker.currentNode);
function getRangeFromInt(start, end){ var indexSizeError = 'IndexSizeError: Index or size is negative or greater than the allowed amount'; if(start>end||start<0){console.warn(indexSizeError); return null;} var length = 0; var startNode, endNode, current=0, startPos, endPos;
while(length<=end){ // we'been too far ? return if(current>=textNodeList.length){console.warn(indexSizeError); return null;} // add the length of current node to our total length length+=textNodeList[current].textContent.length; // start is less than the actual total length ? if(start<length && !startNode){ // then our startNode is here startPos = start-(length-textNodeList[current].textContent.length); startNode = textNodeList[current]; } // same for the end if(end<length && !endNode){ endPos = end-(length-textNodeList[current].textContent.length); endNode = textNodeList[current]; }
current++; }
var range = document.createRange(); range.setStart(startNode, startPos); range.setEnd(endNode, endPos);
var selection = window.getSelection(); selection.removeAllRanges(); selection.addRange(range); return selection; }
var log = document.getElementById('log');document.querySelector('button').addEventListener('click', function(){ log.innerHTML = getRangeFromInt( this.previousElementSibling.previousElementSibling.value, this.previousElementSibling.value); },false);
p{font-size:.7em}#log{color: rgba(0,0,0,.7); border:1px solid; position: absolute; font-size:.5em;}
<input placeholder="start"/><input placeholder="end"/><button>getRange</button><div id="main"><p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin vel sodales odio. Nunc a nisi blandit, gravida augue quis, tristique ante. Aliquam in sem at tellus tincidunt ornare. Mauris ut scelerisque sapien. Pellentesque dignissim erat enim, vestibulum molestie diam ultrices eget. Nullam semper leo sit amet ante porttitor, vel tincidunt lorem </p><p>Nam pellentesque id nulla at venenatis. Integer arcu nisi, suscipit sit amet here neque ac, hendrerit finibus nisl. Morbi quis volutpat libero. Pellentesque in sapien eu magna finibus tempor. Aliquam posuere ornare dolor, vel maximus felis tincidunt vel. Curabitur ac risus ut augue </p><p>Pellentesque sollicitudin risus eu mi sollicitudin maximus eu at turpis. here Nunc iaculis tellus neque, in sollicitudin diam sollicitudin nec. Donec vitae urna nec nibh pharetra pulvinar. Proin eget dolor id quam porta </p><p>Suspendisse malesuada, elit a blandit efficitur, mi sem molestie orci, at vulputate here erat diam quis mi. Mauris feugiat faucibus semper. Nulla tempor et velit quis interdum. Proin tincidunt lacus ut lacus auctor scelerisque. Aliquam pharetra risus laoreet nulla commodo, at eleifend ipsum dapibus. Pellentesque dignissim congue diam, a fermentum diam </p></div><p id="log"></p>

Persisting the changes of range objects after selection in HTML

For each selection, you could serialize the selected range to character offsets and deserialize it again on reload using something like this:

Demo: http://jsfiddle.net/WeWy7/3/

Code:

var saveSelection, restoreSelection;

if (window.getSelection && document.createRange) {
saveSelection = function(containerEl) {
var range = window.getSelection().getRangeAt(0);
var preSelectionRange = range.cloneRange();
preSelectionRange.selectNodeContents(containerEl);
preSelectionRange.setEnd(range.startContainer, range.startOffset);
var start = preSelectionRange.toString().length;

return {
start: start,
end: start + range.toString().length
};
};

restoreSelection = function(containerEl, savedSel) {
var charIndex = 0, range = document.createRange();
range.setStart(containerEl, 0);
range.collapse(true);
var nodeStack = [containerEl], node, foundStart = false, stop = false;

while (!stop && (node = nodeStack.pop())) {
if (node.nodeType == 3) {
var nextCharIndex = charIndex + node.length;
if (!foundStart && savedSel.start >= charIndex && savedSel.start <= nextCharIndex) {
range.setStart(node, savedSel.start - charIndex);
foundStart = true;
}
if (foundStart && savedSel.end >= charIndex && savedSel.end <= nextCharIndex) {
range.setEnd(node, savedSel.end - charIndex);
stop = true;
}
charIndex = nextCharIndex;
} else {
var i = node.childNodes.length;
while (i--) {
nodeStack.push(node.childNodes[i]);
}
}
}

var sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
}
} else if (document.selection) {
saveSelection = function(containerEl) {
var selectedTextRange = document.selection.createRange();
var preSelectionTextRange = document.body.createTextRange();
preSelectionTextRange.moveToElementText(containerEl);
preSelectionTextRange.setEndPoint("EndToStart", selectedTextRange);
var start = preSelectionTextRange.text.length;

return {
start: start,
end: start + selectedTextRange.text.length
}
};

restoreSelection = function(containerEl, savedSel) {
var textRange = document.body.createTextRange();
textRange.moveToElementText(containerEl);
textRange.collapse(true);
textRange.moveEnd("character", savedSel.end);
textRange.moveStart("character", savedSel.start);
textRange.select();
};
}


Related Topics



Leave a reply



Submit