In the following structure:
<div contenteditable="true">
<span class="some-class">Hello</span>
World <!-- no span here -->
<span class="some-other-class">and good bye!</span>
</div>
Is it possible to remove selected span elements, but not the text node, without interrupting the cursor position in the contenteditable
div
?
So even if the cursor is inside the span, keep the same text content, but just remove the span elements around it
For example if the styling of some-class
is red text color and I put the cursor somewhere inside the word "Hello", then I want the styling to be gone and remove the wrapping span
Here ya go!
<div contenteditable="true">
<span class="some-class" style="color: red">Hello</span>
<span class="some-class" style="color: blue">world</span>
<span class="some-other-class" style="color: orange">and good bye!</span>
<span style="color: green">test</span>
</div>
<script>
const div = document.querySelector('div');
const getSelection = () => {
let offset = 0;
const selection = window.getSelection();
const range = selection.getRangeAt(0);
let start = range.startOffset;
let end = range.endOffset;
if (selection.baseNode.parentNode.hasChildNodes()) {
for (const node of selection.baseNode.parentNode.childNodes) {
const cnode = node;
if (cnode.nodeType == document.TEXT_NODE && !(offset + cnode.length > start)) {
offset = offset + cnode.length;
}
if (cnode.nodeType == document.ELEMENT_NODE && !(offset + cnode.textContent.length > start)) {
offset = offset + cnode.textContent.length;
}
}
}
start = start + offset;
end = end + offset;
return { start, end };
};
const removeSpan = ({ target, currentTarget: { selectionStart } }) => {
// Get our selection points for within our span element
const { start, end } = getSelection();
// Only run this logic for span elements
if (target.tagName === 'SPAN') {
// Create new text node from our span's innerText
const text = document.createTextNode(target.innerText + ' ');
// Replace span with just the text
target.replaceWith(text);
// Create a range and set selection range back to what it was to not affect cursor
const range = document.createRange();
range.setStart(text, start);
range.setEnd(text, end);
const selection = window.getSelection();
selection.removeAllRanges();
selection.addRange(range);
}
};
div.addEventListener('click', removeSpan);
</script>