I have a mark button on UI, clicking which, any user selection is marked red. No problems here. I achieve this by document.execCommand("insertHTML")
But I have an additional requirement that if the new selection is created which is the intersection of old selections markings, old selection's red marking should disappear.
As an example:
In the following image: this and testing are marked. Now if I select his is tes from the start and click mark, old markings of this and testing should go away and only his is tes should be marked because there is an intersection.
code:
const button = document.getElementById("button");
button.addEventListener('click', ()=>{
const s = window.getSelection();
const selectionStr = s.toString();
document.execCommand("insertHTML", false, `<span class="bg-red">${selectionStr}<span>`);
})
.bg-red {
background: red;
}
<div contenteditable="true">
this is testing this is testing this is testing
</div>
<button id="button">mark</button>
You can find the startContainer
& endContainer
of the selected text with getRangeAt
, and compare them with contentContainer
.
And if they are not equal with contentContainer
, then remove the bg-red
class.
const button = document.getElementById("button");
button.addEventListener('click', ()=>{
let contentContainer = document.getElementById("contentContainer");
const selection = window.getSelection();
if (selection.rangeCount > 0){
let startContainer = selection.getRangeAt(0).startContainer.parentNode;
let endContainer = selection.getRangeAt(0).endContainer.parentNode;
if(startContainer != contentContainer)
startContainer.classList.remove('bg-red')
if(endContainer != contentContainer)
endContainer.classList.remove('bg-red')
}
const selectionStr = selection.toString();
document.execCommand("insertHTML", false, `<span class="bg-red">${selectionStr}<span>`);
})
.bg-red {
background: red;
}
<div id="contentContainer" contenteditable="true">
this is testing this is testing this is testing
</div>
<button id="button">mark</button>
If you don't need IE support, you could use selection.containsNode: https://developer.mozilla.org/en-US/docs/Web/API/Selection/containsNode
This allows you to flag the nodes that are contained in the selection. You need to set the partialContainment flag to true, so it'll detect nodes that are only partially selected.
So in a first time you flag the nodes that are contained by selection with a specific class name. Then you do your execCommand to apply your style. Then, you deleted the tags that were flagged previously by setting outerHTML on these nodes to innerHTML. You'll keep the style you just applied, but will remove previous ones. Like this:
const button = document.getElementById("button");
button.addEventListener('click', () => {
const s = window.getSelection();
const selectionStr = s.toString();
tagIntersection(s)
document.execCommand("insertHTML", false, `<span class="bg-red">${selectionStr}<span>`);
removeIntersection(s)
})
function tagIntersection(s) {
const redSpans = document.getElementsByClassName('bg-red')
for (let i = 0; i < redSpans.length; i++) {
if (s.containsNode(redSpans[i], true)) {
redSpans[i].classList.add('to-remove');
}
}
}
function removeIntersection(s) {
// using querySelector because getElements returns a live nodelist
// which is a problem when you manipulate the nodes
const toRemovespans = document.querySelectorAll('.to-remove')
for (let i = 0; i < toRemovespans.length; i++) {
toRemovespans[i].outerHTML = toRemovespans[i].innerHTML;
}
}
.bg-red {
background: red;
}
<div contenteditable="true" id="editableDiv">
this is testing this is testing this is testing
</div>
<button id="button">mark</button>
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