handling intersection between selection markings
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>
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>