getSelection & surroundContents across multiple tags
I've got a script that changes the background colour of text that has been selected. However i'm encountering an issue when the text is selected across multiple elements/tags.
The code that i've got is:
var text = window.getSelection().getRangeAt(0);
var colour = document.createElement("hlight");
colour.style.backgroundColor = "Yellow";
text.surroundContents(colour);
And the error being output is:
Error: The boundary-points of a range does not meet specific requirements. =
NS_ERROR_DOM_RANGE_BAD_BOUNDARYPOINTS_ERR
Line: 7
I believe this is to do with the getRange() function though i'm not too sure how to proceed since I am a beginner at javascript.
Is there any other way I can replicate what I am trying to achieve?
Many thanks.
This question has been asked today: How can I highlight the text of the DOM Range object?
Here's my answer:
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);
}
}
Well I think the use of mark.js library is great in this case. The library's intention is to highlight all instances of a certain word in the HTML document, but it can be tweaked through the filter option function, and additional span attributes can be added through the each option function.
function markFunc(node, text, color) {
var instance = new Mark(node);
instance.mark(text, {
"element": "span",
"className": color,
"acrossElements": true,
"separateWordSearch": false,
"accuracy": "partially",
"diacritics": true,
"ignoreJoiners": true,
"each": function(element) {
element.setAttribute("id", "sohayb");
element.setAttribute("title", "sohayb_title");
},
"done":function(totalMarks) {
window.getSelection().empty();//This only in Chrome
console.log("total marks: " + totalMarks);
},
"filter": function(node, term, totalCounter, counter) {
var res = false;
if (counter == 0) {
res = selectionRange.isPointInRange(node, selectionRange.startOffset);
} else {
res = selectionRange.isPointInRange(node, 1);
}
console.log("Counter: " + counter + ", startOffset: " + selectionRange.startOffset);
return res;
}
});
};
Check this JSFiddle sample for completed code that highlights user's selection, even across multiple HTML elements.