Вставить HTML в текст node с помощью JavaScript
У меня есть небольшой текст node:
var node
И я хочу обернуть span вокруг каждого появления "lol".
node.nodeValue = node.nodeValue.replace(/lol/, "<span>lol</span>")
Он выводит "<span>lol<span>"
, когда я хочу "lol"
как элемент span.
Ответы
Ответ 1
Вам может понадобиться node
для родительского node, таким образом вы можете просто использовать innerHTML:
node.innerHTML=node.childNodes[0].nodeValue.replace(/lol/, "<span>lol</span>");
Здесь node.childNodes[0]
относится к фактическому тексту node, а node
- его содержащий элемент.
Ответ 2
Ответ, представленный Андреасом Джосасом, довольно хорош. Однако код имел несколько ошибок, когда поисковый запрос появлялся несколько раз в том же тексте node. Вот решение с исправленными ошибками, и, кроме того, вставка учитывается в matchText для удобства использования и понимания. Теперь в обратном вызове создается только новый тег и возвращается обратно в matchText по возврату.
Обновлена функция matchText с исправлениями ошибок:
var matchText = function(node, regex, callback, excludeElements) {
excludeElements || (excludeElements = ['script', 'style', 'iframe', 'canvas']);
var child = node.firstChild;
while (child) {
switch (child.nodeType) {
case 1:
if (excludeElements.indexOf(child.tagName.toLowerCase()) > -1)
break;
matchText(child, regex, callback, excludeElements);
break;
case 3:
var bk = 0;
child.data.replace(regex, function(all) {
var args = [].slice.call(arguments),
offset = args[args.length - 2],
newTextNode = child.splitText(offset+bk), tag;
bk -= child.data.length + all.length;
newTextNode.data = newTextNode.data.substr(all.length);
tag = callback.apply(window, [child].concat(args));
child.parentNode.insertBefore(tag, newTextNode);
child = newTextNode;
});
regex.lastIndex = 0;
break;
}
child = child.nextSibling;
}
return node;
};
Использование:
matchText(document.getElementsByTagName("article")[0], new RegExp("\\b" + searchTerm + "\\b", "g"), function(node, match, offset) {
var span = document.createElement("span");
span.className = "search-term";
span.textContent = match;
return span;
});
Если вы хотите вставить теги привязки (ссылки) вместо тегов span, измените элемент create на "a" вместо "span", добавьте строку, чтобы добавить атрибут href в тег, и добавьте 'a' в список excludeElements, чтобы ссылки не были созданы внутри ссылок.
Ответ 3
В следующей статье приведен код для замены текста элементами HTML:
http://blog.alexanderdickson.com/javascript-replacing-text
Из статьи:
var matchText = function(node, regex, callback, excludeElements) {
excludeElements || (excludeElements = ['script', 'style', 'iframe', 'canvas']);
var child = node.firstChild;
do {
switch (child.nodeType) {
case 1:
if (excludeElements.indexOf(child.tagName.toLowerCase()) > -1) {
continue;
}
matchText(child, regex, callback, excludeElements);
break;
case 3:
child.data.replace(regex, function(all) {
var args = [].slice.call(arguments),
offset = args[args.length - 2],
newTextNode = child.splitText(offset);
newTextNode.data = newTextNode.data.substr(all.length);
callback.apply(window, [child].concat(args));
child = newTextNode;
});
break;
}
} while (child = child.nextSibling);
return node;
}
Использование:
matchText(document.getElementsByTagName("article")[0], new RegExp("\\b" + searchTerm + "\\b", "g"), function(node, match, offset) {
var span = document.createElement("span");
span.className = "search-term";
span.textContent = match;
node.parentNode.insertBefore(span, node.nextSibling);
});
И объяснение:
По сути, правильный способ сделать это...
- Итерировать по всем текстовым узлам.
- Найти подстроку в текстовых узлах.
- Разделите это по смещению.
- Вставьте элемент span между разбиением.
Ответ 4
Не сказать, что это лучший ответ, но я публикую то, что я сделал для полноты. В моем случае я уже просмотрел или определил смещения текста, который мне нужно выделить в конкретном #text node. Это также разъясняет этапы.
//node is a #text node, startIndex is the beginning location of the text to highlight, and endIndex is the index of the character just after the text to highlight
var parentNode = node.parentNode;
// break the node text into 3 parts: part1 - before the selected text, part2- the text to highlight, and part3 - the text after the highlight
var s = node.nodeValue;
// get the text before the highlight
var part1 = s.substring(0, startIndex);
// get the text that will be highlighted
var part2 = s.substring(startIndex, endIndex);
// get the part after the highlight
var part3 = s.substring(endIndex);
// replace the text node with the new nodes
var textNode = document.createTextNode(part1);
parentNode.replaceChild(textNode, node);
// create a span node and add it to the parent immediately after the first text node
var spanNode = document.createElement("span");
spanNode.className = "HighlightedText";
parentNode.insertBefore(spanNode, textNode.nextSibling);
// create a text node for the highlighted text and add it to the span node
textNode = document.createTextNode(part2);
spanNode.appendChild(textNode);
// create a text node for the text after the highlight and add it after the span node
textNode = document.createTextNode(part3);
parentNode.insertBefore(textNode, spanNode.nextSibling);
Ответ 5
Актуальный ответ для тех, кто находит этот вопрос сейчас, следующий:
function textNodeInnerHTML(textNode,innerHTML) {
var div = document.createElement('div');
textNode.parentNode.insertBefore(div,textNode);
div.insertAdjacentHTML('afterend',innerHTML);
div.remove();
textNode.remove();
}
Идея состоит в том, чтобы вставить вновь созданный HTML-элемент (скажем, var div = document.createElement('div');
) перед textNode
, используя:
textNode.parentNode.insertBefore(div,textNode);
а затем используйте:
div.insertAdjacentHTML(
'afterend',
textNode.data.replace(/lol/g,'<span style="color : red">lol</span>')
)
затем удалите textNode
и div
, используя:
textNode.remove();
div.remove();
insertAdjacentHTML
не уничтожает слушателей событий, как innerHTML
.
Если вы хотите найти все текстовые узлы, которые являются потомками elm
, используйте:
[...elm.querySelectorAll('*')]
.map(l => [...l.childNodes])
.flat()
.filter(l => l.nodeType === 3);