I am trying to create a highlighter tool which works like this:
This way only one range of text can be highlighted at a time.
Problem:
I am having a hard time understanding the Range, Selection and Node API's
At the moment I can't add the highlighted text back into the fresh html... I am only appending it to the document.body.
What I have so far:
https://jsfiddle.net/4mb39jd6/
(function(){
var highlighter = {
/**
*
*/
init: function(){
this.cacheDOM();
this.bindEvents();
},
/**
*
*/
cacheDOM: function(){
this.$html = $('.content').html();
this.$content = $('.content');
this.$highlighter = $('.highlighter');
},
/**
*
*/
bindEvents: function(){
this.$highlighter.on('mousedown', this.highlightSelection.bind(this));
},
/**
*
*/
highlightSelection: function(e){
var selection = window.getSelection(); // get selection
var text = selection.toString(); // get selected text
var newNode = document.createElement('span'); // create node
newNode.style.backgroundColor = $(e.target).css('backgroundColor'); // set node properties
newNode.appendChild(document.createTextNode(text)); // append selection text to node
var range = selection.getRangeAt(0); // 2 - get the selected range
range.deleteContents(); // delete the contents
range.insertNode(newNode); // insert the new node with the replacement text
documentFragment = range.cloneContents(); // clone the node
this.$content.html(this.$html); // refresh the content
document.body.appendChild(documentFragment); // add the new highlighted text
},
};
highlighter.init();
})();
Q:
How do I add my highlighted node... which looks like this <span style="background-color: rgb(255, 255, 131);">some random text</span> back into a fresh html document so that it is in the same position.
If the goal is to have only one highlight at a time, I'd go for a less complicated approach that:
To do so, mark your highlight <span>s with an attribute or class (or better yet, store a reference):
newNode.classList.add("js-highlight");
Add a method to remove such an element:
clearHighlight: function() {
var selection = document.querySelector(".js-highlight");
if (selection) {
selection.parentElement.replaceChild(
document.createTextNode(selection.innerText),
selection
);
}
}
Then, before replacing your range with the highlight element, call clearHighlight.
Example: https://jsfiddle.net/2tqdLfb1/
An alternative:
I also tried another approach that respected your "cached HTML" logic, but found it to be overcomplicated. The basics of that approach:
parentElement of the selectioninnerText in to 1, 2 or 3 textNodes based on the selection start and end index<span>An example that shows how you could store the query path for your range ancestor:
function getPathUpToSelector(selector, element, path) {
var parent = element.parentElement;
if (parent && !element.matches(selector)) {
var index = Array.from(parent.children).indexOf(element) + 1;
path.push("*:nth-child(" + index + ")");
return getPathUpToSelector(selector, parent, path);
}
return [selector].concat(path).join(" > ");
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With