Search code examples
javascripttypescriptdominnerhtml

How to avoid affecting HTML tags in childNodes during innerHTML string.replace()?


I am aiming to replace each word in a document P tags with a span element. While the below code does the trick well for the first case, the following ones overlap, and start to destroy the HTML structure of our P tag completely. See image for unwanted output

Wondering if my approach is a wrong one, or would need some sorcery to only effect the innerHTML between the tags?

const runBionic = (node: ChildNode, paragraph: HTMLElement):HTMLElement => {
if (node.textContent === null) return paragraph;
const originalWords:string[] = node.textContent.split(" ");
const filteredWords: string[] = originalWords.filter(word => word!=="" && word !== "\n");
console.log("Filtered words: ", filteredWords);
//replace each word with a span element
filteredWords.forEach(word => {
    const modifiedWord = `<span style='font-weight: bold;'>${word}</span>`;
    console.log("REPLACING ", word, "WITH ", modifiedWord);
    paragraph.innerHTML = paragraph.innerHTML.replaceAll(word,modifiedWord);
});
return paragraph;};

Aiming to eventually build a chrome extension that highlights the first 1/2 of the characters of any word on any page. This will help dyslexic people read the web faster. Attaching a link to the whole repo on github for context.


Solution

  • Okay, so I ended up with replacing the whole TextNode with a new span element instead. This way I can prepare the whole element and append it to its parent. Works perfectly, no regex needed....

    const replaceNode = (node: ChildNode)=>{
    if (node.textContent === null) return node;
    const newChild = document.createElement("span");
    const words: string[] = node.textContent.split(" ").filter(word => word!=="" && word !== "\n");
    const HTMLstring: string = spannify(words);
    newChild.innerHTML = HTMLstring;
    return newChild;};
    
    const spannify = (words : string[]) : string=>{
    let HTMLstring = "";
    words.forEach(word =>{
        const span = `<span> ${word} </span>`;
        HTMLstring += span;
    });
    return HTMLstring;};