I'm trying to highlight the text of HTML elements without removing their inner HTML. Here is an example:
const buttonEl = `<button>
<span>
Icon
</span>
Text
</button>
`;
document.body.innerHTML = buttonEl;
const foundButtonEl = document.querySelector("button");
const elements = [];
elements.push(foundButtonEl);
function addHighlight(elements, text) {
const regex = new RegExp(text);
elements.forEach((element, index) => {
element.innerHTML = element.textContent.replace(
regex,
`<mark>$&</mark>`
);
});
}
addHighlight(elements, "T");
If you run this example, you'll notice that T
will be highlighted, but the <span>
tags will be removed.
How to highlight the text without affecting the inner HTML?
CodePudding user response:
You change the inner HTML of button. So one way would be to put a span around your Text and use an id and then use document.getElementById("text") I guess. See for example
const buttonEl = `<button>
<span>
Icon
</span>
<span id="text">
Text
</span>
</button>
`;
document.body.innerHTML = buttonEl;
const foundButtonEl = document.getElementById("text");
const elements = [];
elements.push(foundButtonEl);
function addHighlight(elements, text) {
const regex = new RegExp(text);
elements.forEach((element, index) => {
element.innerHTML = element.textContent.replace(
regex,
`<mark>$&</mark>`
);
});
}
addHighlight(elements, "T");
CodePudding user response:
The .textContent
property returns (or set) the text content of all children and since the <button>
element may contain further nested elements, its strict content might be mixed with promiscuous content coming from other elements like your span icon.
Here in this demo I added a layer that will convert the button content so that every single text node found will be removed and changed to a span.textnode
so that later when you'll need to style the text content adding the <mark>
it will be strightforward to change ONLY the contents of inner span.textnode
elements.
To better show the concept I also added the fontawesome asset and style the icon inside the button.
const buttons = document.querySelectorAll("button");
function addHighlight(buttons, text){
buttons.forEach(button => {
initButton(button);
highlightTextInButton(button, text);
});
}
function initButton(button){
//for each child node in button
for (var i = 0; i < button.childNodes.length; i ) {
const child = button.childNodes[i];
//if it's of type TEXT_NODE
if (child.nodeType === Node.TEXT_NODE) {
//creates a new span.textnode
const span = document.createElement('span');
span.classList.add('textnode');
//with this same content
span.textContent = child.nodeValue;
//removes the text node
child.remove();
//adds the new span.textnode
button.append(span);
}
}
}
function highlightTextInButton(button, text){
const regex = new RegExp(text);
button.querySelectorAll(':scope > .textnode')
.forEach(textnode=>{
const textContent = textnode.textContent;
textnode.innerHTML = textContent.replace(regex,`<mark>$&</mark>`);
});
}
addHighlight(buttons, "T");
.textnode{
}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.1/css/all.min.css" integrity="sha512-MV7K8 y gLIBoVD59lQIYicR65iaqukzvf/nwasF0nqhPay5w/9lJmVM2hMDcnK1OnMGCdVK iQrJ7lzPJQd1w==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<button>
<i ></i>
Text
</button>