Cómo resaltar texto usando javascript

98

¿Alguien puede ayudarme con una función de JavaScript que pueda resaltar texto en una página web? Y el requisito es resaltar solo una vez, no resaltar todas las apariciones del texto como lo hacemos en caso de búsqueda.

Ankit
fuente
4
Si publica el código de la función, podríamos ayudarlo. Si nos pide que creemos una función de este tipo para usted ... es menos probable. Tienes que hacer algo por tu cuenta. Empiece a hacer algo y vuelva cuando se quede atascado.
Felix Kling
7
Sí. Leí Cómo preguntar y hice algo por mi cuenta, pero me quedé atascado y por eso pregunté. Trabajo en Android y tengo pocos conocimientos de javasript, por eso no puedo hacerlo por mi cuenta. Anteriormente, estaba usando un javascript diferente que hizo el trabajo, pero no sin ciertas limitaciones. Es posible que no haya usado las palabras correctas al hacer esta pregunta y lo siento, pero no piense en otra cosa.
Ankit
1
Este complemento puede ser de su interés: github.com/julmot/jmHighlight . Puede resaltar palabras clave por separado o como un término, puede resaltar la coincidencia con su elemento personalizado y nombre de clase y también puede buscar signos diacríticos. En la parte superior, le permite filtrar el contexto en el que buscar coincidencias.
amigo
1
He preparado un artículo sobre eso aquí, exhesham.com/2017/11/20/…
Hesham Yassin

Respuestas:

100

Puedes usar el efecto de resaltado de jquery .

Pero si está interesado en el código javascript sin procesar, eche un vistazo a lo que obtuve. Simplemente copie y pegue en un HTML, abra el archivo y haga clic en "resaltar"; esto debería resaltar la palabra "zorro". En cuanto al rendimiento, creo que esto funcionaría para texto pequeño y una sola repetición (como usted especificó)

function highlight(text) {
  var inputText = document.getElementById("inputText");
  var innerHTML = inputText.innerHTML;
  var index = innerHTML.indexOf(text);
  if (index >= 0) { 
   innerHTML = innerHTML.substring(0,index) + "<span class='highlight'>" + innerHTML.substring(index,index+text.length) + "</span>" + innerHTML.substring(index + text.length);
   inputText.innerHTML = innerHTML;
  }
}
.highlight {
  background-color: yellow;
}
<button onclick="highlight('fox')">Highlight</button>

<div id="inputText">
  The fox went over the fence
</div>

Ediciones:

Utilizando replace

Veo que esta respuesta ganó algo de popularidad, pensé que podría agregarla. También puede usar reemplazar fácilmente

"the fox jumped over the fence".replace(/fox/,"<span>fox</span>");

O para múltiples ocurrencias (no es relevante para la pregunta, pero se hizo en los comentarios) simplemente agregue globalla expresión regular de reemplazo.

"the fox jumped over the other fox".replace(/fox/g,"<span>fox</span>");

Espero que esto ayude a los comentaristas intrigados.

Reemplazo del HTML a toda la página web

para reemplazar el HTML de una página web completa, debe consultar innerHTMLel cuerpo del documento.

document.body.innerHTML

chico mograbi
fuente
Muchas gracias por su respuesta, pero ¿puede también decirme cómo especificar el color en el javascript
Ankit
Puede reemplazar el "<span class='highlight'>"con "<span style='color: " + color + ";'>", el color debería ser algo comovar color = "#ff0000";
Yaniro
¿Qué pasa si quiero resaltar todas las apariciones de una palabra en toda la página? @guy mograbi
Baqer Naqvi
4
Usar un simple "reemplazo" es una mala idea . He descrito por qué aquí: stackoverflow.com/a/32758672/3894981
amigo
2
Esta no es una gran idea porque intentará resaltar etiquetas / atributos HTML / etc. Por ejemplo, qué sucedería en el caso de: <img src="fox.jpg" /><img src="<span class='highlight'>fox</span>.jpg" />
Obtendría
46

Las soluciones que se ofrecen aquí son bastante malas.

  1. No puede usar regex, porque de esa manera, busca / resalta en las etiquetas html.
  2. No puede usar expresiones regulares, porque no funciona correctamente con UTF * (cualquier cosa con caracteres no latinos / ingleses).
  3. No puede simplemente hacer un innerHTML.replace, porque esto no funciona cuando los caracteres tienen una notación HTML especial, por ejemplo, &amp;para &, &lt;para <, &gt;para>, &auml;para ä, &ouml;para ö &uuml;para ü &szlig;para ß, etc.

Qué necesitas hacer:

Recorra el documento HTML, busque todos los nodos de texto, obtenga textContent, obtenga la posición del texto resaltado con indexOf(con un opcional toLowerCasesi no debe distinguir entre mayúsculas y minúsculas), agregue todo lo anterior indexofcomo textNode, agregue el texto coincidente con un intervalo resaltado, y repita para el resto del nodo de texto (la cadena resaltada puede ocurrir varias veces en eltextContent cadena).

Aquí está el código para esto:

var InstantSearch = {

    "highlight": function (container, highlightText)
    {
        var internalHighlighter = function (options)
        {

            var id = {
                container: "container",
                tokens: "tokens",
                all: "all",
                token: "token",
                className: "className",
                sensitiveSearch: "sensitiveSearch"
            },
            tokens = options[id.tokens],
            allClassName = options[id.all][id.className],
            allSensitiveSearch = options[id.all][id.sensitiveSearch];


            function checkAndReplace(node, tokenArr, classNameAll, sensitiveSearchAll)
            {
                var nodeVal = node.nodeValue, parentNode = node.parentNode,
                    i, j, curToken, myToken, myClassName, mySensitiveSearch,
                    finalClassName, finalSensitiveSearch,
                    foundIndex, begin, matched, end,
                    textNode, span, isFirst;

                for (i = 0, j = tokenArr.length; i < j; i++)
                {
                    curToken = tokenArr[i];
                    myToken = curToken[id.token];
                    myClassName = curToken[id.className];
                    mySensitiveSearch = curToken[id.sensitiveSearch];

                    finalClassName = (classNameAll ? myClassName + " " + classNameAll : myClassName);

                    finalSensitiveSearch = (typeof sensitiveSearchAll !== "undefined" ? sensitiveSearchAll : mySensitiveSearch);

                    isFirst = true;
                    while (true)
                    {
                        if (finalSensitiveSearch)
                            foundIndex = nodeVal.indexOf(myToken);
                        else
                            foundIndex = nodeVal.toLowerCase().indexOf(myToken.toLowerCase());

                        if (foundIndex < 0)
                        {
                            if (isFirst)
                                break;

                            if (nodeVal)
                            {
                                textNode = document.createTextNode(nodeVal);
                                parentNode.insertBefore(textNode, node);
                            } // End if (nodeVal)

                            parentNode.removeChild(node);
                            break;
                        } // End if (foundIndex < 0)

                        isFirst = false;


                        begin = nodeVal.substring(0, foundIndex);
                        matched = nodeVal.substr(foundIndex, myToken.length);

                        if (begin)
                        {
                            textNode = document.createTextNode(begin);
                            parentNode.insertBefore(textNode, node);
                        } // End if (begin)

                        span = document.createElement("span");
                        span.className += finalClassName;
                        span.appendChild(document.createTextNode(matched));
                        parentNode.insertBefore(span, node);

                        nodeVal = nodeVal.substring(foundIndex + myToken.length);
                    } // Whend

                } // Next i 
            }; // End Function checkAndReplace 

            function iterator(p)
            {
                if (p === null) return;

                var children = Array.prototype.slice.call(p.childNodes), i, cur;

                if (children.length)
                {
                    for (i = 0; i < children.length; i++)
                    {
                        cur = children[i];
                        if (cur.nodeType === 3)
                        {
                            checkAndReplace(cur, tokens, allClassName, allSensitiveSearch);
                        }
                        else if (cur.nodeType === 1)
                        {
                            iterator(cur);
                        }
                    }
                }
            }; // End Function iterator

            iterator(options[id.container]);
        } // End Function highlighter
        ;


        internalHighlighter(
            {
                container: container
                , all:
                    {
                        className: "highlighter"
                    }
                , tokens: [
                    {
                        token: highlightText
                        , className: "highlight"
                        , sensitiveSearch: false
                    }
                ]
            }
        ); // End Call internalHighlighter 

    } // End Function highlight

};

Entonces puedes usarlo así:

function TestTextHighlighting(highlightText)
{
    var container = document.getElementById("testDocument");
    InstantSearch.highlight(container, highlightText);
}

Aquí hay un documento HTML de ejemplo

<!DOCTYPE html>
<html>
    <head>
        <title>Example of Text Highlight</title>
        <style type="text/css" media="screen">
            .highlight{ background: #D3E18A;}
            .light{ background-color: yellow;}
        </style>
    </head>
    <body>
        <div id="testDocument">
            This is a test
            <span> This is another test</span>
            äöüÄÖÜäöüÄÖÜ
            <span>Test123&auml;&ouml;&uuml;&Auml;&Ouml;&Uuml;</span>
        </div>
    </body>
</html>

Por cierto, si busca en una base de datos con LIKE,
p . Ej.WHERE textField LIKE CONCAT('%', @query, '%') [lo que no debe hacer, debe usar la búsqueda de texto completo o Lucene], entonces puede escapar de cada carácter con \ y agregar una declaración de escape SQL, de esa manera encontrará caracteres especiales que son expresiones LIKE.

p.ej

WHERE textField LIKE CONCAT('%', @query, '%') ESCAPE '\'

y el valor de @query no es '%completed%' sino'%\c\o\m\p\l\e\t\e\d%'

(probado, funciona con SQL-Server y PostgreSQL, y cualquier otro sistema RDBMS que admita ESCAPE)


Una versión mecanografiada revisada:

namespace SearchTools 
{


    export interface IToken
    {
        token: string;
        className: string;
        sensitiveSearch: boolean;
    }


    export class InstantSearch 
    {

        protected m_container: Node;
        protected m_defaultClassName: string;
        protected m_defaultCaseSensitivity: boolean;
        protected m_highlightTokens: IToken[];


        constructor(container: Node, tokens: IToken[], defaultClassName?: string, defaultCaseSensitivity?: boolean)
        {
            this.iterator = this.iterator.bind(this);
            this.checkAndReplace = this.checkAndReplace.bind(this);
            this.highlight = this.highlight.bind(this);
            this.highlightNode = this.highlightNode.bind(this);    

            this.m_container = container;
            this.m_defaultClassName = defaultClassName || "highlight";
            this.m_defaultCaseSensitivity = defaultCaseSensitivity || false;
            this.m_highlightTokens = tokens || [{
                token: "test",
                className: this.m_defaultClassName,
                sensitiveSearch: this.m_defaultCaseSensitivity
            }];
        }


        protected checkAndReplace(node: Node)
        {
            let nodeVal: string = node.nodeValue;
            let parentNode: Node = node.parentNode;
            let textNode: Text = null;

            for (let i = 0, j = this.m_highlightTokens.length; i < j; i++)
            {
                let curToken: IToken = this.m_highlightTokens[i];
                let textToHighlight: string = curToken.token;
                let highlightClassName: string = curToken.className || this.m_defaultClassName;
                let caseSensitive: boolean = curToken.sensitiveSearch || this.m_defaultCaseSensitivity;

                let isFirst: boolean = true;
                while (true)
                {
                    let foundIndex: number = caseSensitive ?
                        nodeVal.indexOf(textToHighlight)
                        : nodeVal.toLowerCase().indexOf(textToHighlight.toLowerCase());

                    if (foundIndex < 0)
                    {
                        if (isFirst)
                            break;

                        if (nodeVal)
                        {
                            textNode = document.createTextNode(nodeVal);
                            parentNode.insertBefore(textNode, node);
                        } // End if (nodeVal)

                        parentNode.removeChild(node);
                        break;
                    } // End if (foundIndex < 0)

                    isFirst = false;


                    let begin: string = nodeVal.substring(0, foundIndex);
                    let matched: string = nodeVal.substr(foundIndex, textToHighlight.length);

                    if (begin)
                    {
                        textNode = document.createTextNode(begin);
                        parentNode.insertBefore(textNode, node);
                    } // End if (begin)

                    let span: HTMLSpanElement = document.createElement("span");

                    if (!span.classList.contains(highlightClassName))
                        span.classList.add(highlightClassName);

                    span.appendChild(document.createTextNode(matched));
                    parentNode.insertBefore(span, node);

                    nodeVal = nodeVal.substring(foundIndex + textToHighlight.length);
                } // Whend

            } // Next i 

        } // End Sub checkAndReplace 


        protected iterator(p: Node)
        {
            if (p == null)
                return;

            let children: Node[] = Array.prototype.slice.call(p.childNodes);

            if (children.length)
            {
                for (let i = 0; i < children.length; i++)
                {
                    let cur: Node = children[i];

                    // https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType
                    if (cur.nodeType === Node.TEXT_NODE) 
                    {
                        this.checkAndReplace(cur);
                    }
                    else if (cur.nodeType === Node.ELEMENT_NODE) 
                    {
                        this.iterator(cur);
                    }
                } // Next i 

            } // End if (children.length) 

        } // End Sub iterator


        public highlightNode(n:Node)
        {
            this.iterator(n);
        } // End Sub highlight 


        public highlight()
        {
            this.iterator(this.m_container);
        } // End Sub highlight 


    } // End Class InstantSearch 


} // End Namespace SearchTools 

Uso:

let searchText = document.getElementById("txtSearchText");
let searchContainer = document.body; // document.getElementById("someTable");
let highlighter = new SearchTools.InstantSearch(searchContainer, [
    {
        token: "this is the text to highlight" // searchText.value,
        className: "highlight", // this is the individual highlight class
        sensitiveSearch: false
    }
]);


// highlighter.highlight(); // this would highlight in the entire table
// foreach tr - for each td2 
highlighter.highlightNode(td2); // this highlights in the second column of table
Stefan Steiger
fuente
Gran respuesta ... El método parece excesivo, ¡pero conciso! Definitivamente estará interesado en hacer una prueba de velocidad con ese método, ya que en mi caso los resultados se cargan lentamente en el DOM (ya que PUEDE haber miles de resultados), curioso si este método agregaría una alta latencia a la carga diferida.
Pogrindis
5
Lo siento, pero ninguno de tus argumentos es cierto. 1. Absolutamente puede usar una expresión regular, simplemente no debe buscar dentro del valor HTML sino el valor de texto de un elemento. 2. Puede utilizar absolutamente RegExp con caracteres diacríticos, como se aplica en mark.js . 3. Las notaciones HTML se convertirán a los caracteres reales en el DOM del navegador, ¡así que también las usa absolutamente!
dude
1
@julmot; Para 1: lo que significa que debe iterar a través de cada elemento, que es precisamente lo que hago. A menos que no le importe perder el formato, en cuyo caso puede buscar en document.body.innerText, que será bastante lento. 3. No en el DOM, sino en la propiedad innerText o textContent de un elemento de texto. Lo que nuevamente significa que necesita iterar a través de los elementos de texto; no se puede hacer con regEx AFAIK. 2: No sé mark.js, pero evitaría todo lo que hace un jQuery.each, porque es muy lento.
Stefan Steiger
1
@StefanSteiger 1. Entonces debería corregir su relación de decisión, ya que dice que no podemos buscar con una expresión regular en absoluto, lo cual no es cierto 2. No usa jQuery.each. ¿Qué te hace pensar que? 3. Esto no es cierto, al menos en Firefox. &auml;por ejemplo, se convertirá al carácter real, incluso cuando se utilice innerHTML.
dude
1
Hola @StefanSteiger En realidad, estoy usando tus soluciones. Este es perfecto. Pero hay algún problema como, Si tengo una P En la que hay dos tramos y uno tiene datos como Diploma MSBTE y el segundo tramo tiene datos de 2012. Ahora, si la cadena que quiero resaltar es Diploma MSBTE 2012, toda esta cadena luego verifiqué que esto no funciona, si todo lo que va a coincidir está presente en un lapso, entonces funciona, pero si el contenido del texto está en etiquetas diff, entonces No funciona. ¿Puedes contarnos algo sobre esto?
ganeshk
41

Por qué usar una función de resaltado hecha por uno mismo es una mala idea

La razón por la que probablemente sea una mala idea comenzar a construir su propia función de resaltado desde cero es porque seguramente se encontrará con problemas que otros ya han resuelto. Desafíos:

  • Debería eliminar los nodos de texto con elementos HTML para resaltar sus coincidencias sin destruir los eventos DOM y desencadenar la regeneración DOM una y otra vez (que sería el caso, por ejemplo, con 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 resaltador busca coincidencias dentro de los nodos de texto y, si sus palabras clave se dividirán en varios nodos de texto, no se encontrarán.
  • También necesitaría crear pruebas para asegurarse de que su complemento funcione en situaciones en las que no ha pensado. ¡Y estoy hablando de pruebas entre navegadores!

¿Suena complicado? Si desea algunas funciones como ignorar algunos elementos de resaltado, mapeo de 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.

Usar un complemento existente

Al utilizar un complemento existente y bien implementado, no tiene que preocuparse por las cosas mencionadas anteriormente. Los complementos de resaltador de texto de jQuery del artículo 10 en Sitepoint compara los complementos de resaltador populares.

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 los otros complementos con opciones para:

  • buscar palabras clave por separado en lugar del término completo
  • 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
  • sinónimos personalizados de mapas
  • buscar también dentro de iframes
  • recibir términos no encontrados

MANIFESTACIÓN

Alternativamente, puede 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 gratis y está desarrollado de código abierto en GitHub ( referencia del proyecto ).

tipo
fuente
4
Resaltar el texto por sí solo no es una razón suficiente para incluir jQuery.
Roy
10
@Roy Me lo he tomado en serio. Buenas noticias, a partir de v6.0.0 mark.js renunció a la dependencia de jQuery y ahora lo hace opcionalmente para usarlo como complemento de jQuery.
amigo
Todo cierto, excepto: el primer punto no es posible, porque no puede obtener controladores de eventos registrados, e incluso si pudiera, no podría establecer funciones anónimas ... segundo: mark.js tampoco encuentra texto entre dos etiquetas, por ejemplo <span> s </span> ed no encontrará sed ... 3 °: cada vez que aparece un navegador (incluida la nueva versión) que aún no lo ha probado, puede fallar. Eso siempre es cierto, no importa cuántas pruebas escriba. Con 17 kb, las marcas son demasiado grandes para lo que hace.
Stefan Steiger
¿A qué te refieres @StefanSteiger? No puedo decir nada al primer punto sin esa información. Sin embargo, el segundo comentario es incorrecto, mark.js puede encontrar coincidencias entre etiquetas, usando la acrossElementsopción. Y al tercer comentario; mark.js no es grande en comparación con las funcionalidades que ofrece. Y no, es poco probable que algo se rompa en el futuro, ya que mark.js se probó, por ejemplo, al iniciar Chrome 30 y en todas las versiones más nuevas con pruebas unitarias entre navegadores y nunca hubo problemas con las próximas versiones.
dude
@dude: Los tres puntos después del primer párrafo. Ah, está bien, faltaba esa opción en la demostración que miré. En ese caso, podría tener algún sentido. Pero aún así, lo encuentro demasiado grande.
Stefan Steiger
10
function stylizeHighlightedString() {

    var text = window.getSelection();

    // For diagnostics
    var start = text.anchorOffset;
    var end = text.focusOffset - text.anchorOffset;

    range = window.getSelection().getRangeAt(0);

    var selectionContents = range.extractContents();
    var span = document.createElement("span");

    span.appendChild(selectionContents);

    span.style.backgroundColor = "yellow";
    span.style.color = "black";

    range.insertNode(span);
}
Mohit Kumar
fuente
3
Mohit, bienvenido a SO. ¡Una descripción del código estaría bien!
Nippey
¿No debería haber una forma de seleccionar texto sin crear otro nodo?
Dave Gregory
@ user191433 la pregunta no se trata solo de seleccionar texto, sino también de aplicar estilos. Para eso necesitas un nodo.
Christophe
Recordatorio / sugerencia de que JavaScript se span.style.backgroundColor = "yellow";traduce en CSS: style="background-color: yellow;"esa sutil diferencia entre camelCase y la notación discontinua me hizo tropezar al principio.
MarkHu
1
La respuesta de PS Mohit en stackoverflow.com/questions/7991474/… es una variante más simplificada de este código. (por ejemplo, omitiendo las variables de inicio y finalización que son únicamente de diagnóstico / no funcionales aquí).
MarkHu
7

Aquí está mi solución de JavaScript puro regexp:

function highlight(text) {
    document.body.innerHTML = document.body.innerHTML.replace(
        new RegExp(text + '(?!([^<]+)?<)', 'gi'),
        '<b style="background-color:#ff0;font-size:100%">$&</b>'
    );
}
Klemen Tušar
fuente
Esto funciona perfectamente para mí cuando el bloque de texto que intento resaltar contiene etiquetas HTML.
John Chapman
También puede modificar la función para aceptar varias palabras a través del símbolo de tubería de one|two|three
expresión regular
No reemplazará el texto si el final del texto tiene un >carácter. Modifique la expresión regular usando (?!([^<]+)?<)para que funcione.
Archie Reyes
Modificado según lo solicitado.
Klemen Tušar
¡Perfecto! Esto es lo mejor para mí
marco burrometo
5

Tengo el mismo problema, entra un montón de texto a través de una solicitud xmlhttp. Este texto tiene formato html. Necesito resaltar cada ocurrencia.

str='<img src="brown fox.jpg" title="The brown fox" />'
    +'<p>some text containing fox.</p>'

El problema es que no necesito resaltar el texto en las etiquetas. Por ejemplo, necesito resaltar fox:

Ahora puedo reemplazarlo con:

var word="fox";
word="(\\b"+ 
    word.replace(/([{}()[\]\\.?*+^$|=!:~-])/g, "\\$1")
        + "\\b)";
var r = new RegExp(word,"igm");
str.replace(r,"<span class='hl'>$1</span>")

Para responder a su pregunta: puede omitir la g en las opciones de expresiones regulares y solo se reemplazará la primera aparición, pero esta sigue siendo la de la propiedad img src y destruye la etiqueta de la imagen:

<img src="brown <span class='hl'>fox</span>.jpg" title="The brown <span 
class='hl'>fox</span> />

Esta es la forma en que lo resolví, pero me preguntaba si hay una manera mejor, algo que me he perdido en las expresiones regulares:

str='<img src="brown fox.jpg" title="The brown fox" />'
    +'<p>some text containing fox.</p>'
var word="fox";
word="(\\b"+ 
    word.replace(/([{}()[\]\\.?*+^$|=!:~-])/g, "\\$1")
    + "\\b)";
var r = new RegExp(word,"igm");
str.replace(/(>[^<]+<)/igm,function(a){
    return a.replace(r,"<span class='hl'>$1</span>");
});
HMR
fuente
Esta fue la única solución de expresiones regulares que funcionó para mí sin interferir con <img src="word">o <a href="word">.
yvesmancera
1
Regla de oro: nunca. Utilizar. Regular. Expresiones. A. Lío. Acerca de. Con. XML.
ScottMcGready
5

Ninguna de las otras soluciones realmente se ajusta a mis necesidades, y aunque la solución de Stefan Steiger funcionó como esperaba, la encontré un poco demasiado detallada.

Lo que sigue es mi intento:

/**
 * Highlight keywords inside a DOM element
 * @param {string} elem Element to search for keywords in
 * @param {string[]} keywords Keywords to highlight
 * @param {boolean} caseSensitive Differenciate between capital and lowercase letters
 * @param {string} cls Class to apply to the highlighted keyword
 */
function highlight(elem, keywords, caseSensitive = false, cls = 'highlight') {
  const flags = caseSensitive ? 'gi' : 'g';
  // Sort longer matches first to avoid
  // highlighting keywords within keywords.
  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 all keywords found in the page
highlight(document.body, ['lorem', 'amet', 'autem']);
.highlight {
  background: lightpink;
}
<p>Hello world lorem ipsum dolor sit amet, consectetur adipisicing elit. Est vel accusantium totam, ipsum delectus et dignissimos mollitia!</p>
<p>
  Lorem ipsum dolor sit amet, consectetur adipisicing elit. Numquam, corporis.
  <small>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Accusantium autem voluptas perferendis dolores ducimus velit error voluptatem, qui rerum modi?</small>
</p>

También recomendaría usar algo como escape-string-regexp si sus palabras clave pueden tener caracteres especiales que deberían escaparse en las expresiones regulares:

const keywordRegex = RegExp(keywords.map(escapeRegexp).join('|')), flags);
elclanrs
fuente
Esto funcionó bien para mí, pero también necesita una forma de "desmarcar" '
jwzumwalt
4

Ejemplo simple de TypeScript

NOTA: Si bien estoy de acuerdo con @Stefan en muchas cosas, solo necesitaba un simple resaltado de coincidencia:

module myApp.Search {
    'use strict';

    export class Utils {
        private static regexFlags = 'gi';
        private static wrapper = 'mark';

        private static wrap(match: string): string {
            return '<' + Utils.wrapper + '>' + match + '</' + Utils.wrapper + '>';
        }

        static highlightSearchTerm(term: string, searchResult: string): string {
            let regex = new RegExp(term, Utils.regexFlags);

            return searchResult.replace(regex, match => Utils.wrap(match));
        }
    }
}

Y luego construyendo el resultado real:

module myApp.Search {
    'use strict';

    export class SearchResult {
        id: string;
        title: string;

        constructor(result, term?: string) {
            this.id = result.id;
            this.title = term ? Utils.highlightSearchTerm(term, result.title) : result.title;
        }
    }
}
Slavo Vojacek
fuente
3

Desde HTML5 puede utilizar el <mark></mark> etiquetas para resaltar texto. Puede usar javascript para envolver algún texto / palabra clave entre estas etiquetas. A continuación, se muestra un pequeño ejemplo de cómo marcar y desmarcar texto.

DEMO DE JSFIDDLE

kasper taeymans
fuente
innerHTMLes peligroso. Eliminará eventos.
dude
2
Esto tampoco funciona correctamente porque, por ejemplo, si ingresa en JSFIDDLE "Lorem", solo marca la primera instancia.
agm1984
1
Bueno, solo necesita reemplazar todas las apariciones de la palabra clave. aquí hay un ejemplo con expresiones
kasper Taeymans
2

Avance rápido hasta 2019, Web API ahora tiene soporte nativo para resaltar textos:

const selection = document.getSelection();
selection.setBaseAndExtent(anchorNode, anchorOffset, focusNode, focusOffset);

¡Y estás listo! anchorNodees el nodo inicial de la selección, focusNodees el nodo final de la selección. Y, si son nodos de texto, offsetes el índice del carácter inicial y final en los nodos respectivos. Aquí está la documentación

Incluso tienen una demostración en vivo

JGuo
fuente
oh esto es brillante. simplemente utilícelo así: selection.setBaseAndExtent (deseadoNodo, 0, DeseadoNodo, 1); para resaltar el único nodo que necesita. y funciona con Gutenberg
tonyAndr
1

También me preguntaba eso, podrías probar lo que aprendí sobre esto. publicación.

Solía:

function highlightSelection() {
			var userSelection = window.getSelection();
			for(var i = 0; i < userSelection.rangeCount; i++) {
				highlightRange(userSelection.getRangeAt(i));
			}
			
		}
			
			function highlightRange(range) {
			    var newNode = document.createElement("span");
			    newNode.setAttribute(
			       "style",
			       "background-color: yellow; display: inline;"
			    );
			    range.surroundContents(newNode);
			}
<html>
	<body contextmenu="mymenu">

		<menu type="context" id="mymenu">
			<menuitem label="Highlight Yellow" onclick="highlightSelection()" icon="/images/comment_icon.gif"></menuitem>
		</menu>
		<p>this is text, select and right click to high light me! if you can`t see the option, please use this<button onclick="highlightSelection()">button </button><p>

también puedes probarlo aquí: http://henriquedonati.com/projects/Extension/extension.html

xc

Henrique Donati
fuente
0

Si también desea que se destaque en la carga de la página, hay una nueva forma.

solo agrega #:~:text=Highlight%20These

intente acceder a este enlace

/programming/38588721#:~:text=Highlight%20a%20text

Jovylle Bermúdez
fuente
-1

Usando el método surroundContents () en el tipo de rango . Su único argumento es un elemento que envolverá ese rango.

function styleSelected() {
  bg = document.createElement("span");
  bg.style.backgroundColor = "yellow";
  window.getSelection().getRangeAt(0).surroundContents(bg);
}
arhoskins
fuente