Como destacar texto usando javascript

98

Alguém pode me ajudar com uma função javascript que pode destacar o texto em uma página da web. E o requisito é - destacar apenas uma vez, não como destacar todas as ocorrências do texto como fazemos em caso de pesquisa.

Ankit
fonte
4
Se você postar o código da função, poderemos ajudar. Se você nos pedir para criar essa função para você ... isso é menos provável. Você tem que fazer algo por conta própria. Comece a fazer algo e volte quando ficar preso.
Felix Kling
7
Sim, eu li Como perguntar e fiz algo por conta própria, mas não consegui e foi por isso que perguntei. Trabalho com Android e tenho pouco conhecimento de javasript, por isso não consigo fazer sozinho. Anteriormente, eu estava usando um javascript diferente que funcionava, mas não sem certas limitações. Posso não ter usado as palavras certas ao fazer esta pergunta e sinto muito por isso, mas por favor, não pense de outra forma.
Ankit
1
Este plugin pode ser do seu interesse: github.com/julmot/jmHighlight . Ele pode destacar palavras-chave separadamente ou como um termo, pode destacar a correspondência com seu elemento personalizado e nome de classe e também pode pesquisar sinais diacríticos. No topo, ele permite que você filtre o contexto no qual procurar por correspondências.
cara,
1
Check-out seguindo a maneira regex ... stackoverflow.com/a/45519242/2792959
Eu preparei um artigo sobre isso aqui, exhesham.com/2017/11/20/…
Hesham Yassin

Respostas:

100

Você pode usar o efeito de realce jquery .

Mas se você estiver interessado em código javascript bruto, dê uma olhada no que eu obtive. Simplesmente copie e cole em um HTML, abra o arquivo e clique em "destacar" - isso deve destacar a palavra "raposa". Em termos de desempenho, acho que isso seria suficiente para texto pequeno e uma única repetição (como você especificou)

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>

Editar% s:

Usando replace

Vejo que essa resposta ganhou alguma popularidade, pensei em acrescentá-la. Você também pode usar facilmente substituir

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

Ou para várias ocorrências (não relevantes para a pergunta, mas foram feitas nos comentários), basta adicionar globala expressão regular de substituição.

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

Espero que isso ajude os comentaristas intrigados.

Substituindo o HTML em toda a página da web

para substituir o HTML por uma página da web inteira, você deve consultar innerHTMLdo corpo do documento.

document.body.innerHTML

cara mograbi
fonte
Muito obrigado pela sua resposta, mas você também pode me dizer como especificar a cor no próprio javascript
Ankit
Você pode substituir "<span class='highlight'>"por "<span style='color: " + color + ";'>", a cor deve ser algo comovar color = "#ff0000";
Yaniro
e se eu quiser destacar todas as ocorrências de uma palavra em toda a página? @guy mograbi
Baqer Naqvi
4
Usar um simples "substituir" é uma má ideia . Descrevi o porquê aqui: stackoverflow.com/a/32758672/3894981
cara
2
Esta não é uma boa ideia porque tentará destacar as tags / atributos / etc. de HTML. Por exemplo, o que aconteceria no caso de: <img src="fox.jpg" /> Você obteria um HTML inválido que se pareceria com: <img src="<span class='highlight'>fox</span>.jpg" /> Não é bom
dcporter7
46

As soluções oferecidas aqui são muito ruins.

  1. Você não pode usar regex, porque dessa forma, você pesquisa / destaca nas tags html.
  2. Você não pode usar regex, porque não funciona corretamente com UTF * (qualquer coisa com caracteres não latinos / ingleses).
  3. Você não pode simplesmente fazer um innerHTML.replace, porque isso não funciona quando os caracteres têm uma notação HTML especial, por exemplo, &amp;para &, &lt;para <, &gt;para>, &auml;para ä, &ouml;para ö &uuml;para ü &szlig;para ß, etc.

O que você precisa fazer:

Percorra o documento HTML, encontre todos os nós de texto, obtenha o textContent, obtenha a posição do texto destacado com indexOf(com um opcional toLowerCasese não for sensível a maiúsculas e minúsculas), anexe tudo antes de indexofcomo textNode, anexe o texto correspondente com uma extensão de destaque, e repita para o resto do textnode (a string realçada pode ocorrer várias vezes na textContentstring).

Aqui está o código para isso:

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

};

Então você pode usá-lo assim:

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

Aqui está um exemplo de documento HTML

<!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>

A propósito, se você pesquisar em um banco de dados com LIKE,
por exemplo WHERE textField LIKE CONCAT('%', @query, '%')[o que você não deveria fazer, você deve usar fulltext-search ou Lucene], então você pode escapar de todos os caracteres com \ e adicionar uma instrução SQL-escape, dessa forma você encontrará caracteres especiais que são expressões LIKE.

por exemplo

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

e o valor de @query não é '%completed%'senão'%\c\o\m\p\l\e\t\e\d%'

(testado, funciona com SQL-Server e PostgreSQL, e todos os outros sistemas RDBMS que suportam ESCAPE)


Uma versão datilografada 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
fonte
Ótima resposta. O método parece um exagero, mas conciso! Definitivamente terei interesse em fazer um teste de velocidade com esse método, pois no meu caso os resultados são carregados lentamente no DOM (já que PODEM haver milhares de resultados), curioso para saber se esse método adicionaria uma alta latência ao carregamento lento.
Pogrindis
5
Desculpe, mas nenhum de seus argumentos é verdadeiro. 1. Você absolutamente pode usar um RegExp, você apenas não deve pesquisar dentro do valor HTML, mas no valor de texto de um elemento. 2. Você pode absolutamente usar RegExp com caracteres diacríticos, como implementado no mark.js . 3. As notações HTML serão convertidas nos caracteres reais no DOM do navegador, portanto, você também os usará com certeza!
cara
1
@julmot; Para 1: o que significa que você precisa iterar por meio de cada elemento, que é exatamente o que eu faço. A menos que você não se preocupe em perder a formatação, nesse caso você pode pesquisar em document.body.innerText, que será bem lento. 3. Não no DOM, mas no innerText ou na propriedade textContent de um elemento de texto. O que novamente significa que você precisa iterar através dos elementos de texto; não pode ser feito com regEx AFAIK. 2: Não sei mark.js, mas eu evitaria tudo que faz um jQuery.each, porque é muito lento.
Stefan Steiger
1
@StefanSteiger 1. Então você deve corrigir sua decisão relacional, pois diz que não podemos pesquisar com um RegExp, o que não é verdade 2. Ele não usa jQuery.each. O que te faz pensar isso? 3. Isso não é verdade, pelo menos no Firefox. &auml;por exemplo, será convertido para o caractere real, mesmo quando estiver usando innerHTML.
cara
1
Olá @StefanSteiger. Na verdade, estou usando suas soluções. Este é perfeito. Mas há algum problema como If II have a P Em que há dois spans e um span tem dados como Diploma MSBTE e o segundo span tem dados de 2012. Agora, se a string que eu quero destacar é Diploma MSBTE 2012, toda essa string então eu verifiquei isso não funciona, se tudo que vai corresponder está presente em um intervalo, então funciona, mas se o conteúdo do texto estiver em tags diff, então Não funciona. Você pode falar algo sobre isso?
ganeshk
40

Por que usar uma função de realce criada por você mesmo é uma má ideia

O motivo pelo qual provavelmente é uma má ideia começar a construir sua própria função de realce do zero é porque você certamente encontrará problemas que outros já resolveram. Desafios:

  • Você precisaria remover nós de texto com elementos HTML para destacar suas correspondências sem destruir eventos DOM e acionar a regeneração DOM repetidamente (o que seria o caso com, por exemplo innerHTML)
  • Se você quiser remover os elementos destacados, terá que remover os elementos HTML com seu conteúdo e também combinar os nós de texto divididos para pesquisas futuras. Isso é necessário porque cada plug-in de marca-texto procura correspondências dentro de nós de texto e se suas palavras-chave forem divididas em vários nós de texto, elas não serão encontradas.
  • Você também precisaria construir testes para certificar-se de que seu plugin funcione em situações nas quais você não pensou. E estou falando sobre testes entre navegadores!

Parece complicado? Se você deseja alguns recursos como ignorar alguns elementos de realce, mapeamento de diacríticos, mapeamento de sinônimos, pesquisa dentro de iframes, pesquisa de palavras separadas, etc., isso se torna cada vez mais complicado.

Use um plugin existente

Ao usar um plugin existente e bem implementado, você não precisa se preocupar com as coisas nomeadas acima. O artigo 10 dos plug - ins de realce de texto da jQuery no Sitepoint compara os plug-ins de realce populares.

Dê uma olhada em mark.js

mark.js é um plugin escrito em JavaScript puro, mas também está disponível como plugin jQuery. Ele foi desenvolvido para oferecer mais oportunidades do que os outros plug-ins com opções para:

  • pesquise palavras-chave separadamente em vez do termo completo
  • diacríticos do mapa (por exemplo, se "justo" também deve corresponder a "justò")
  • ignorar correspondências dentro de elementos personalizados
  • usar elemento de destaque personalizado
  • usar classe de destaque personalizada
  • sinônimos personalizados de mapas
  • pesquise também dentro de iframes
  • receber termos não encontrados

DEMO

Alternativamente, você pode ver este violino .

Exemplo de uso :

// Highlight "keyword" in the specified context
$(".context").mark("keyword");

// Highlight the custom regular expression in the specified context
$(".context").markRegExp(/Lorem/gmi);

É gratuito e desenvolvido em código aberto no GitHub ( referência do projeto ).

cara
fonte
4
Destacar texto por si só não é uma razão boa o suficiente para eu incluir jQuery.
Roy
10
@Roy, levei isso a sério. Boas notícias, a partir da v6.0.0 mark.js renunciou à dependência jQuery e agora é opcional para usá-lo como plugin jQuery.
cara de
Tudo verdade, exceto: 1º ponto não é possível, porque você não pode obter manipuladores de eventos registrados, e mesmo se pudesse, você não poderia definir funções anônimas ... 2º: mark.js também não encontra texto entre duas tags, por exemplo, <span> s </span> ed não encontrará o sed ... 3º: sempre que aparecer um navegador (incluindo uma nova versão) que você ainda não tenha testado, ele pode quebrar. Isso sempre é verdade, não importa quantos testes você escreva. A 17kb, as marcas são muito grandes para o que fazem.
Stefan Steiger,
A que pontos você está se referindo @StefanSteiger? Não posso dizer nada ao primeiro ponto sem essa informação. No entanto, o segundo comentário está errado, mark.js pode encontrar correspondências entre tags, usando a acrossElementsopção. E para o terceiro comentário; mark.js não é grande em comparação com as funcionalidades que oferece. E não, é improvável que algo quebre no futuro, uma vez que mark.js foi testado, por exemplo, iniciando o Chrome 30 e em todas as versões mais novas com testes de unidade em vários navegadores e nunca houve problemas com as versões futuras.
cara,
@ gajo: Os três pontos após o primeiro parágrafo. Ah, ok, faltando essa opção na demonstração que eu olhei. Nesse caso, pode fazer algum sentido. Mesmo assim, acho que é muito 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
fonte
3
Mohit, bem-vindo ao SO. Alguma descrição do código seria bom!
Nippey
não deveria haver uma maneira de selecionar o texto sem criar outro nó?
Dave Gregory
@ user191433 a questão não é apenas sobre a seleção de texto, mas também sobre a aplicação de estilos. Para isso, você precisa de um nó.
Christophe
Lembrete / dica de que o JavaScript se span.style.backgroundColor = "yellow";traduz em CSS style="background-color: yellow;"- aquela diferença sutil entre o camelCase e a notação tracejada me atrapalhou no começo.
MarkHu
1
A resposta de PS Mohit em stackoverflow.com/questions/7991474/… é uma variante mais simplificada deste código. (por exemplo, omitindo as variáveis ​​de início e fim que são apenas diagnósticas / não funcionais aqui.)
MarkHu
7

Esta é minha solução regexp de JavaScript puro:

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
fonte
Isso funciona perfeitamente para mim quando o bloco de texto que estou tentando destacar contém tags HTML.
John Chapman de
Você também pode ajustar a função para aceitar várias palavras por meio do símbolo de pipe regexp, por exemploone|two|three
Klemen Tušar
Não substituirá o texto se o final do texto tiver um >caractere. Modifique o regex usando (?!([^<]+)?<)para que funcione.
Archie Reyes
Modificado conforme solicitado.
Klemen Tušar
Perfeito! Este é o melhor para mim
marco burrometo 10/01/19
5

Eu tenho o mesmo problema, um monte de texto chega por meio de uma solicitação xmlhttp. Este texto está formatado em html. Eu preciso destacar cada ocorrência.

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

O problema é que não preciso destacar o texto nas tags. Por exemplo, preciso destacar a raposa:

Agora posso substituí-lo por:

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 à sua pergunta: você pode deixar o g nas opções regexp e apenas a primeira ocorrência será substituída, mas esta ainda é a que está na propriedade img src e destrói a tag da imagem:

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

Foi assim que resolvi, mas queria saber se existe uma maneira melhor, algo que perdi nas expressões 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
fonte
Esta foi a única solução regex que funcionou para mim sem mexer com <img src="word">ou <a href="word">.
yvesmancera
1
Regra de ouro: nunca. Usar. Regular. Expressões. Para. Bagunça. Sobre. Com. XML.
ScottMcGready
5

Nenhuma das outras soluções realmente atendeu às minhas necessidades e, embora a solução de Stefan Steiger tenha funcionado como eu esperava, achei-a um pouco prolixa.

A seguir está minha tentativa:

/**
 * 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>

Eu também recomendaria usar algo como escape-string-regexp se suas palavras-chave puderem ter caracteres especiais que precisariam ser escapados em regexes:

const keywordRegex = RegExp(keywords.map(escapeRegexp).join('|')), flags);
elclanrs
fonte
Isso funcionou bem para mim, mas também precisa de uma maneira de "desmarcar" '
jwzumwalt
4

Exemplo simples de TypeScript

NOTA: Embora eu concorde com @Stefan em muitas coisas, eu só precisava de um simples destaque de correspondência:

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));
        }
    }
}

E então construir o 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
fonte
3

Desde HTML5 você pode usar o <mark></mark> tags para destacar o texto. Você pode usar javascript para envolver algum texto / palavra-chave entre essas tags. Aqui está um pequeno exemplo de como marcar e desmarcar texto.

JSFIDDLE DEMO

Kasper Taeymans
fonte
innerHTMLé perigoso. Isso excluirá eventos.
cara
2
Isso também não funciona corretamente porque, por exemplo, se você entrar no JSFIDDLE "Lorem", ele marca apenas a primeira instância dele.
agm1984
1
Bem, você só precisa substituir todas as ocorrências da palavra-chave. aqui está um exemplo com regex globalmente jsfiddle.net/de5q704L/73
kasper Taeymans
2

Avançando para 2019, a API da Web agora tem suporte nativo para destacar textos:

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

E você está pronto para ir! anchorNodeé o nó inicial da seleção, focusNodeé o nó final da seleção. E, se forem nós de texto, offseté o índice do caractere inicial e final nos respectivos nós. Aqui está a documentação

Eles até têm uma demonstração ao vivo

JGuo
fonte
oh, isso é brilhante. simplesmente use-o assim: selection.setBaseAndExtent (desejadoNode, 0, desejadoNode, 1); para destacar o único nó de que você precisa. e funciona com Gutenberg
tonyAndr
1

Eu também estava pensando nisso, você pode experimentar o que aprendi neste post.

Eu usei:

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>

você também pode tentar aqui: http://henriquedonati.com/projects/Extension/extension.html

xc

Henrique donati
fonte
0

Se você também deseja que ele seja destacado no carregamento da página, há uma nova maneira.

apenas adicione #:~:text=Highlight%20These

tente acessar este link

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

Jovylle Bermudez
fonte
-1

Usando o método surroundContents () no tipo Range . Seu único argumento é um elemento que envolverá esse intervalo.

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