Resalta una palabra con jQuery
Básicamente necesito resaltar una palabra en particular en un bloque de texto. Por ejemplo, imagina que quiero resaltar la palabra "dolor" en este texto:
<p>
Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
</p>
<p>
Quisque bibendum sem ut lacus. Integer dolor ullamcorper libero.
Aliquam rhoncus eros at augue. Suspendisse vitae mauris.
</p>
¿Cómo convierto lo anterior a algo como esto?
<p>
Lorem ipsum <span class="myClass">dolor</span> sit amet, consectetuer adipiscing elit.
</p>
<p>
Quisque bibendum sem ut lacus. Integer <span class="myClass">dolor</span> ullamcorper
libero. Aliquam rhoncus eros at augue. Suspendisse vitae mauris.
</p>
¿Es esto posible con jQuery?
Editar : Como señaló Sebastian , esto es bastante posible sin jQuery, pero esperaba que hubiera un método especial de jQuery que le permitiera hacer selectores en el texto mismo. Ya estoy usando mucho jQuery en este sitio, por lo que mantener todo envuelto en jQuery haría las cosas quizás un poco más ordenadas.
Pruebe resaltar: texto JavaScript resaltando el complemento jQuery .
Advertencia: el código fuente disponible en esta página contiene un script de minería de criptomonedas; utilice el siguiente código o elimine el script de minería del script descargado del sitio web.
/*
highlight v4
Highlights arbitrary terms.
<http://johannburkard.de/blog/programming/javascript/highlight-javascript-text-higlighting-jquery-plugin.html>
MIT license.
Johann Burkard
<http://johannburkard.de>
<mailto:[email protected]>
*/
jQuery.fn.highlight = function(pat) {
function innerHighlight(node, pat) {
var skip = 0;
if (node.nodeType == 3) {
var pos = node.data.toUpperCase().indexOf(pat);
if (pos >= 0) {
var spannode = document.createElement('span');
spannode.className = 'highlight';
var middlebit = node.splitText(pos);
var endbit = middlebit.splitText(pat.length);
var middleclone = middlebit.cloneNode(true);
spannode.appendChild(middleclone);
middlebit.parentNode.replaceChild(spannode, middlebit);
skip = 1;
}
}
else if (node.nodeType == 1 && node.childNodes && !/(script|style)/i.test(node.tagName)) {
for (var i = 0; i < node.childNodes.length; ++i) {
i += innerHighlight(node.childNodes[i], pat);
}
}
return skip;
}
return this.length && pat && pat.length ? this.each(function() {
innerHighlight(this, pat.toUpperCase());
}) : this;
};
jQuery.fn.removeHighlight = function() {
return this.find("span.highlight").each(function() {
this.parentNode.firstChild.nodeName;
with (this.parentNode) {
replaceChild(this.firstChild, this);
normalize();
}
}).end();
};
Pruebe también la versión "actualizada" del script original .
/*
* jQuery Highlight plugin
*
* Based on highlight v3 by Johann Burkard
* http://johannburkard.de/blog/programming/javascript/highlight-javascript-text-higlighting-jquery-plugin.html
*
* Code a little bit refactored and cleaned (in my humble opinion).
* Most important changes:
* - has an option to highlight only entire words (wordsOnly - false by default),
* - has an option to be case sensitive (caseSensitive - false by default)
* - highlight element tag and class names can be specified in options
*
* Usage:
* // wrap every occurrance of text 'lorem' in content
* // with <span class='highlight'> (default options)
* $('#content').highlight('lorem');
*
* // search for and highlight more terms at once
* // so you can save some time on traversing DOM
* $('#content').highlight(['lorem', 'ipsum']);
* $('#content').highlight('lorem ipsum');
*
* // search only for entire word 'lorem'
* $('#content').highlight('lorem', { wordsOnly: true });
*
* // don't ignore case during search of term 'lorem'
* $('#content').highlight('lorem', { caseSensitive: true });
*
* // wrap every occurrance of term 'ipsum' in content
* // with <em class='important'>
* $('#content').highlight('ipsum', { element: 'em', className: 'important' });
*
* // remove default highlight
* $('#content').unhighlight();
*
* // remove custom highlight
* $('#content').unhighlight({ element: 'em', className: 'important' });
*
*
* Copyright (c) 2009 Bartek Szopka
*
* Licensed under MIT license.
*
*/
jQuery.extend({
highlight: function (node, re, nodeName, className) {
if (node.nodeType === 3) {
var match = node.data.match(re);
if (match) {
var highlight = document.createElement(nodeName || 'span');
highlight.className = className || 'highlight';
var wordNode = node.splitText(match.index);
wordNode.splitText(match[0].length);
var wordClone = wordNode.cloneNode(true);
highlight.appendChild(wordClone);
wordNode.parentNode.replaceChild(highlight, wordNode);
return 1; //skip added node in parent
}
} else if ((node.nodeType === 1 && node.childNodes) && // only element nodes that have children
!/(script|style)/i.test(node.tagName) && // ignore script and style nodes
!(node.tagName === nodeName.toUpperCase() && node.className === className)) { // skip if already highlighted
for (var i = 0; i < node.childNodes.length; i++) {
i += jQuery.highlight(node.childNodes[i], re, nodeName, className);
}
}
return 0;
}
});
jQuery.fn.unhighlight = function (options) {
var settings = { className: 'highlight', element: 'span' };
jQuery.extend(settings, options);
return this.find(settings.element + "." + settings.className).each(function () {
var parent = this.parentNode;
parent.replaceChild(this.firstChild, this);
parent.normalize();
}).end();
};
jQuery.fn.highlight = function (words, options) {
var settings = { className: 'highlight', element: 'span', caseSensitive: false, wordsOnly: false };
jQuery.extend(settings, options);
if (words.constructor === String) {
words = [words];
}
words = jQuery.grep(words, function(word, i){
return word != '';
});
words = jQuery.map(words, function(word, i) {
return word.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
});
if (words.length == 0) { return this; };
var flag = settings.caseSensitive ? "" : "i";
var pattern = "(" + words.join("|") + ")";
if (settings.wordsOnly) {
pattern = "\\b" + pattern + "\\b";
}
var re = new RegExp(pattern, flag);
return this.each(function () {
jQuery.highlight(this, re, settings.element, settings.className);
});
};
function hiliter(word, element) {
var rgxp = new RegExp(word, 'g');
var repl = '<span class="myClass">' + word + '</span>';
element.innerHTML = element.innerHTML.replace(rgxp, repl);
}
hiliter('dolor');
Por qué utilizar una función de resaltado hecha por uno mismo es una mala idea
La razón por la que probablemente sea una mala idea empezar a crear su propia función de resaltado desde cero es porque seguramente se encontrará con problemas que otros ya han resuelto. Desafíos:
- Necesitaría eliminar nodos de texto con elementos HTML para resaltar sus coincidencias sin destruir los eventos DOM y activar la regeneración DOM una y otra vez (como sería el caso, por ejemplo
innerHTML
) - Si desea eliminar los elementos resaltados, deberá eliminar los elementos HTML con su contenido y también deberá combinar los nodos de texto divididos para realizar más búsquedas. Esto es necesario porque cada complemento de resaltado busca coincidencias dentro de los nodos de texto y, si sus palabras clave se dividen en varios nodos de texto, no se encontrarán.
- También necesitarás crear pruebas para asegurarte de que tu complemento funcione en situaciones en las que no has pensado. ¡Y estoy hablando de pruebas entre navegadores!
¿Suena complicado? Si desea algunas funciones como ignorar algunos elementos del resaltado, mapeo de signos diacríticos, mapeo de sinónimos, búsqueda dentro de iframes, búsqueda de palabras separadas, etc., esto se vuelve cada vez más complicado.
Utilice un complemento existente
Cuando utiliza un complemento existente y bien implementado, no tiene que preocuparse por las cosas mencionadas anteriormente. El artículo 10 complementos de resaltado de texto de jQuery en Sitepoint compara los complementos de resaltado populares. Esto incluye complementos de respuestas de esta pregunta.
Eche un vistazo a mark.js
mark.js es un complemento escrito en JavaScript puro, pero también está disponible como complemento jQuery. Fue desarrollado para ofrecer más oportunidades que otros complementos con opciones para:
- busque palabras clave por separado en lugar del término completo
- signos diacríticos del mapa (por ejemplo, si "justo" también debe coincidir con "justò")
- ignorar coincidencias dentro de elementos personalizados
- utilizar elemento de resaltado personalizado
- usar clase de resaltado personalizada
- mapa sinónimos personalizados
- buscar también dentro de iframes
- recibir términos no encontrados
MANIFESTACIÓN
Alternativamente puedes ver este violín .
Ejemplo de uso :
// Highlight "keyword" in the specified context
$(".context").mark("keyword");
// Highlight the custom regular expression in the specified context
$(".context").markRegExp(/Lorem/gmi);
Es gratuito y está desarrollado de código abierto en GitHub ( referencia del proyecto ).
Aquí hay una variación que ignora y preserva el caso:
jQuery.fn.highlight = function (str, className) {
var regex = new RegExp("\\b"+str+"\\b", "gi");
return this.each(function () {
this.innerHTML = this.innerHTML.replace(regex, function(matched) {return "<span class=\"" + className + "\">" + matched + "</span>";});
});
};