I would like to replace all occurence with a html contenteditable div. I need to replace the occurence with a tag to customize this with a css. But when I try to replace with a span, MutationObserver generate a infinite loop...
document.designMode = "on";
var text = document.getElementById('text');
let observer = new MutationObserver(mutations =>
mutations.forEach(mutation => {
console.log(text.textContent);
// let a = "a";
// a.style.color = "red";
text.textContent = text.textContent.replace('/', '<span>/</span>');
observer.disconnect();
const range = document.createRange();
const textNode = text.firstChild;
range.setStart(textNode, text.textContent.length);
range.setEnd(textNode, text.textContent.length);
const sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
// observer.disconnect();
})
);
observer.observe(text, {
childList: true,
characterData: true,
subtree: true,
});
<p id="text" contenteditable="true">I'm a text will be change</p>
The infinite loop is caused by replacing typed /
characters with <span>/</span>
without turning off the observer first - each replacement triggers the observer again.
The solution for this problem is simple: turn the observer off before replacement and turn it on again afterwards:
"use strict";
document.designMode = "on";
var text = document.getElementById('text');
let observer = new MutationObserver(mutations =>
mutations.forEach(mutation => {
console.log(text.textContent);
observer.disconnect(); // turn observer off;
text.textContent = text.textContent.replace('/', '<span>/</span>');
const range = document.createRange();
const textNode = text.firstChild;
range.setStart(textNode, text.textContent.length);
range.setEnd(textNode, text.textContent.length);
const sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
observe(); // turn back on
})
);
const observe = ()=> {
observer.observe(text, {
childList: true,
characterData: true,
subtree: true,
});
};
observe();
span { background-color: yellow;}
<p id="text" contenteditable="true">I'm a text will be change</p>
But this only hightlights a potential new problem: the code replaces textContent
with HTML which, because it's inside a text node, is displayed in source form, and because <span>/</span>
is inside a text node, CSS to highlight SPAN elements in yellow doesn't work (it's not a SPAN element).
Assuming the span element should be rendered as HTML, the design problem now becomes how to replace newly typed forward slashes with a SPAN element in the DOM, not in a text node. The mutation.target
property of Mutation Records may be a good place to start by splitting it into three nodes: a textnode for text before the /
, an element node for <span>/</span>
and a final textnode for text following the /
if any, followed by repositioning the cursor after the span node if required.