Выделять только полные слова
Есть функция которая находит на странице слова которые есть в массиве и заключает их в тег.
Она работает хорошо, но проблема в том что она заключает в тег даже часть слова, например, если в массиве есть:
"кот", "собака"
а текст такой:
... тот который ...
то получится так:
... тот <span class="highlight">кот</span>орый ...
Нужно чтобы функция выделяла только полные слова.
Буду очень благодарен за помощь!
var words = ["кот", "текст", "пример"];
highlight(document.body, words);
function highlight(elem, keywords, caseSensitive = false, cls = 'highlight') {
const flags = caseSensitive ? 'gi' : 'g';
keywords.sort((a, b) => b.length - a.length);
Array.from(elem.childNodes).forEach(child => {
const keywordRegex = RegExp(keywords.join('|'), flags);
if (child.nodeType !== 3) { // not a text node
highlight(child, keywords, caseSensitive, cls);
} else if (keywordRegex.test(child.textContent)) {
const frag = document.createDocumentFragment();
let lastIdx = 0;
child.textContent.replace(keywordRegex, (match, idx) => {
const part = document.createTextNode(child.textContent.slice(lastIdx, idx));
const highlighted = document.createElement('span');
highlighted.textContent = match;
highlighted.classList.add(cls);
frag.appendChild(part);
frag.appendChild(highlighted);
lastIdx = idx + match.length;
});
const end = document.createTextNode(child.textContent.slice(lastIdx));
frag.appendChild(end);
child.parentNode.replaceChild(frag, child);
}
});
}
.highlight {
background: red;
color: #fff;
}
<body>
пример текста который стоит тут
</body>
Ответы (2 шт):
Автор решения: Pavel Nazarian
→ Ссылка
const words = ["кот", "текст", "пример"];
highlight(document.body, words);
function highlight(elem, keywords, cls = 'highlight') {
Array.from(elem.childNodes).forEach(child => {
if (child.nodeType == 3) {
let html = [];
let frag = document.createDocumentFragment();
child.textContent.trim().split(" ").forEach(word => {
if (keywords.some(el => { return el == word })) {
const highlighted = document.createElement('span');
highlighted.innerHTML = ` <span class='${cls}'>${word}</span> `
frag.appendChild(highlighted)
} else {
const part = document.createTextNode(` ${word} `);
frag.appendChild(part)
}
});
child.parentNode.replaceChild(frag, child);
}
})
}
.highlight {
background: red;
color: #fff;
}
<body>
пример текста который стоит тут, а кот гуляет
</body>
Автор решения: yar85
→ Ссылка
С исправленной регуляркой, и упрощенным созданием фрагмента документа (неявным - через присваивание outerHTML):
var words = ["кот", "текст", "пример"];
highlight(document.body, words);
function highlight(elem, keywords, caseSensitive = false, cls = 'highlight') {
const flags = caseSensitive ? 'giu' : 'gu';
const keywordRegex = RegExp(`(\\P{L})(${keywords.join('|')})(\\P{L})`, flags);
keywords.sort((a, b) => b.length - a.length);
Array.from(elem.childNodes).forEach(child => {
if (child.nodeType !== 3) { // not a text node
highlight(child, keywords, caseSensitive, cls);
} else {
const replacement = document.createElement('span');
child.parentNode.replaceChild(replacement, child);
replacement.outerHTML = child.textContent.replace(keywordRegex,
`$1<span class="${cls}">$2</span>$3`
);
}
});
}
.highlight {
background: red;
color: #fff;
}
<body>
пример текста который стоит тут как кот
</body>
Создание экземпляра RegExp поднял, т.к. нет практического смысла на каждой итерации его пересоздавать.