I’m working on a small project that requires me to take a information from inside a div, wrap all instances of a certain word in a classed span tag, and then put everything back to the div again in the correct place.
My JS is ok in grabbing the text I need and splitting it onto an array, but am lost on where to go from here.
For example, let’s say the word is "needle". Here is my code:
function needleTest(){
var text_string = document.getElementById('haystack').innerHTML;
var text_array = text_string.split(' ');
for (let i = 0; i < text_array.length; i++) {
if(text_array[i]=='needle'){
return "<span class='needle'>" + text_array[i] + "</span>";
}else{
return text_array[i];
}
}
}
.needle{color:red}
<div id='haystack' contentEditable='true'>
There is a <strong>needle</strong> in this <a href='#'>haystack</a>
</div>
<button onclick='needleTest()'>
Click
</button>
What should happen is the script should grab the contents of the div, split it on the spaces, then loop through every element in the array.
It should completely ignore any hyperlinks (so elements starting <a) and ignore images (elements starting <img). For all other elements it should hunt for the word "needle" and, if found, wrap it in the class. ‘Needle’ may or may not have HTML tags around it. If it does, they should be preserved, and the new class added to to wrap everything else.
Finally, everything should then go back into the original div.
So, in this non working example, clicking the button should result in the following being put back into the div:
There is a <span class='needle'><strong>needle</strong></span> in this <a href='#'>haystack</a>
>Solution :
You can try recursive approach to traverse the DOM tree, identifying text nodes and wrapping the word (needle) in a span with the class (needle).
Demo:
function needleTest() {
//start processing the DOM from the 'haystack' div
processNode(document.getElementById('haystack'));
}
//recursive function to process each node in the DOM tree
function processNode(node) {
//check if the current node is a text node
if (node.nodeType === Node.TEXT_NODE) {
//split the text into words and create an array
const words = node.nodeValue.split(' ').map(word =>
//check if the word is 'needle', if yes, wrap it in a span with the class 'needle'
word.toLowerCase() === 'needle' ? `<span class='needle'>${word}</span>` : word
);
//create a new span element and set its innerHTML to the joined words
const spanWrapper = document.createElement('span');
spanWrapper.innerHTML = words.join(' ');
//replace the current text node with the new span element
node.replaceWith(spanWrapper);
} else if (node.nodeType === Node.ELEMENT_NODE && node.nodeName !== 'A' && node.nodeName !== 'IMG') {
//if the current node is an element (excluding 'A' and 'IMG' elements)
//recursively process each child node of the current element
node.childNodes.forEach(childNode => processNode(childNode));
}
}
.needle { color: red; }
<div id='haystack' contentEditable='true'>
There is a <strong>needle</strong> in this <a href='#'>haystack</a>
</div>
<button onclick='needleTest()'>Click</button>