How to implement dynamic underscore in JS, for Drupal solutions
Introduction
In this article, we will discuss how to implement dynamic underlining in JavaScript for Drupal solutions. Through this method, we can highlight specific words or phrases within a paragraph element (p). This technique can be useful in cases such as highlighting search terms, emphasizing keywords in a text, or highlighting certain words based on specific conditions.
The idea of using this is in cases where custom modules are not very flexible, we can add this little JS, and apply our solution based on our specific needs.
Example Code
JS:
// Click event for elements with the 'high-word' class
container.addEventListener("click", (event) => {
if (event.target.classList.contains("high-word")) {
removeHighWord();
}
});
// Escape special characters in a string for use in a regular expression
const escapeRegExp = (string) => {
return string.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&");
};
// Remove HTML tags from a string
const removeAllTags = (string) => {
return string.replace(/<\/?[^>]+(>|$)/g, "");
};
// Check if the matched text is inside an HTML tag
const isInsideTag = (text, index) => {
const openingTag = text.slice(0, index).lastIndexOf("<");
const closingTag = text.slice(0, index).lastIndexOf(">");
return openingTag > closingTag;
};
// Highlight a word or phrase within a paragraph element (p)
const highlightWordInP = (p, word) => {
if (p) {
const escapedWord = escapeRegExp(word);
const cleanWord = removeAllTags(escapedWord);
const wordParts = cleanWord.split(/\s+/);
const regexPattern = wordParts
.map((part) => `(${part})`)
.join("(?:<[^>]*>|&[^;]*;|\\W)*");
const regex = new RegExp(regexPattern, "gi");
let text = p.innerHTML;
let highlightedText = text.replace(regex, (match, ...args) => {
const index = args[args.length - 2];
if (isInsideTag(text, index)) {
// Match is inside a tag, don't highlight it
return match;
} else {
return match
.split(/\s+/)
.map((part) => `<span class='high-word'>${part}</span>`)
.join(" ");
}
});
p.innerHTML = highlightedText;
}
};
// This function removes the <span> element but keeps its content
const removeHighWord = () => {
const highlightedElements =
document.getElementsByClassName("high-word");
[...highlightedElements].forEach((el) => {
const parent = el.parentNode;
const textNode = document.createTextNode(el.textContent);
parent.replaceChild(textNode, el);
});
};
Css:
.high-word {
background-color: rgba(0, 255, 0, 0.5); cursor: pointer;
}
Explanation
The code introduces a function called `highlightWordInP`, which accepts two arguments: a paragraph element `p` (but you can also pass it a div that has a set of text tags) and a `word` to be highlighted. The function does the following:
- Escapes special characters in the word using the `escapeRegExp` function.
- Removes HTML tags from the word using the `removeAllTags` function.
- Splits the word into parts and creates a regular expression that matches the word, ignoring HTML tags, HTML entities, and non-alphabetic characters between the word parts.
- Replaces matches of the word in the paragraph's innerHTML with a `<span>` element containing a specific CSS class for underlining.
Use Cases
You can use this dynamic underlining technique in various scenarios, such as:
- Highlighting search terms within search results.
- Emphasizing important keywords within a text.
- Creating a visual cue for specific words or phrases based on user interaction or other conditions.
To use the highlightWordInP
The difference, and for which I like this code, is that it recognizes between labels.
All the solutions that I found, to apply to a custom element of a form or a preview, do not adapt to what I needed here, since it was something very custom.
By using a class, we can control underlining, bold among other values.
The most common problem that happened was:
<p><b>This text</b> has different tags</p>
The conventional underscores were lost or they did it wrong when underlining this content, since when searching for matches it generated problems because the contents were between tags, for example when passing a text like "This text has different labels", it did not find the matches Due to the different tags, it worked as long as the contents were in the same tag, not separated between paragraphs or other possible elements, which the method shown in this article solves. function, simply pass the paragraph element and the word to be highlighted as arguments. The function will then apply the highlighting as specified.