web-dev-qa-db-fra.com

Comment surligner du texte en utilisant javascript

Quelqu'un peut-il m'aider avec une fonction javascript qui peut mettre en évidence du texte sur une page Web. Et l'exigence est de - mettre en surbrillance une seule fois, et non comme toutes les occurrences du texte, comme nous le faisons en cas de recherche.

78
Ankit

Vous pouvez utiliser le jquery effet de surbrillance .

Mais si le code JavaScript brut vous intéresse, jetez un coup d'œil à ce que j'ai obtenu. Copiez-le simplement, collez-le dans un fichier HTML, ouvrez le fichier et cliquez sur "mettre en évidence". Le mot "renard" doit alors être mis en évidence. Performance sage, je pense que cela ferait pour petit texte et une répétition unique (comme vous l'avez spécifié)

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>

Modifications:

Utiliser replace

Je vois que cette réponse a gagné en popularité, je pensais pouvoir l’ajouter. Vous pouvez également utiliser facilement remplacer

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

Ou pour plusieurs occurrences (non pertinentes pour la question, mais qui ont été posées dans des commentaires), vous ajoutez simplement global à l'expression régulière de remplacement.

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

J'espère que cela aidera les commentateurs intrigués.

Remplacement du code HTML dans la page Web entière

pour remplacer le code HTML d'une page Web entière, vous devez vous référer à innerHTML du corps du document.

document.body.innerHTML

80
guy mograbi

Les solutions proposées ici sont plutôt mauvaises.

  1. Vous ne pouvez pas utiliser regex, car de cette façon, vous recherchez/mettez en surbrillance dans les balises HTML.
  2. Vous ne pouvez pas utiliser regex, car cela ne fonctionne pas correctement avec UTF * (tout ce qui contient des caractères non latins/anglais).
  3. Vous ne pouvez pas simplement faire un innerHTML.replace, car cela ne fonctionne pas lorsque les caractères ont une notation HTML spéciale, par exemple. &amp; pour &, &lt; pour <, &gt; pour>, &auml; pour ä, &ouml; pour ö &uuml; pour ü &szlig; pour ß, etc.

Ce que vous devez faire:

Parcourez le document HTML, recherchez tous les nœuds de texte, obtenez le textContent, obtenez la position du texte surligné avec indexOf (avec un _ optionnel toLowerCase s'il doit être insensible à la casse) , ajoutez tout ce qui précède indexof comme textNode, ajoutez le texte correspondant avec une plage de surbrillance, puis répétez l'opération pour le reste du nœud de texte (la chaîne de surbrillance peut apparaître plusieurs fois dans la chaîne textContent) .

Voici le code pour ceci:

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

};

Ensuite, vous pouvez l'utiliser comme ceci:

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

Voici un exemple de document 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>

Par ailleurs, si vous recherchez dans une base de données avec LIKE,
par exemple. WHERE textField LIKE CONCAT('%', @query, '%') [que vous ne devriez pas faire, vous devriez utiliser fulltext-search ou Lucene], alors vous pouvez échapper à chaque caractère avec\et ajouter une instruction SQL-escape, qui vous permettra de trouver des caractères spéciaux qui sont LIKE-expressions.

par exemple.

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

et la valeur de @query n'est pas '%completed%' mais '%\c\o\m\p\l\e\t\e\d%'

(testé, fonctionne avec SQL-Server et PostgreSQL, ainsi que tous les autres systèmes SGBDR prenant en charge ESCAPE)


Une version révisée de TypeScript:

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 

Usage:

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
37
Stefan Steiger

Pourquoi utiliser une fonction de surbrillance personnalisée est une mauvaise idée

C'est probablement une mauvaise idée de créer votre propre fonction de surbrillance à partir de zéro, c'est parce que vous rencontrerez certainement des problèmes que d'autres ont déjà résolus. Défis:

  • Vous devez supprimer les nœuds de texte avec des éléments HTML pour mettre en surbrillance vos correspondances sans détruire les événements DOM ni déclencher la régénération DOM encore et encore (ce qui serait le cas de innerHTML, par exemple).
  • Si vous souhaitez supprimer les éléments en surbrillance, vous devez supprimer les éléments HTML avec leur contenu et également combiner les nœuds de texte fractionnés pour des recherches ultérieures. Cela est nécessaire car chaque plug-in Highlighter recherche des correspondances dans les nœuds de texte et si vos mots-clés sont scindés en plusieurs nœuds de texte, ils ne seront pas trouvés.
  • Vous devrez également créer des tests pour vous assurer que votre plug-in fonctionne dans des situations auxquelles vous n'avez pas pensé. Et je parle de tests multi-navigateurs!

Cela semble compliqué? Si vous voulez des fonctionnalités comme ignorer certains éléments de la surbrillance, mapper les signes diacritiques, mapper les synonymes, rechercher dans des iframes, rechercher des mots séparés, etc., cela devient de plus en plus compliqué.

Utiliser un plugin existant

Lorsque vous utilisez un plugin existant, bien implémenté, vous n'avez pas à vous soucier des choses nommées ci-dessus. L'article 10 plugins de surligneur de texte jQuery sur Sitepoint compare les plugins les plus populaires surligneur.

Regardez mark.js

mark.js est un tel plugin écrit en JavaScript pur, mais également disponible en tant que plugin jQuery. Il a été développé pour offrir plus d'opportunités que les autres plugins avec des options pour:

  • rechercher des mots-clés séparément au lieu du terme complet
  • map diacritics (Par exemple, si "justo" doit également correspondre à "justò")
  • ignorer les correspondances à l'intérieur des éléments personnalisés
  • utiliser un élément de surbrillance personnalisé
  • utiliser une classe de surbrillance personnalisée
  • mapper des synonymes personnalisés
  • chercher aussi à l'intérieur des iframes
  • recevoir des termes non trouvés

DÉMO

Sinon, vous pouvez voir ce violon .

Exemple d'utilisation :

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

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

Il est gratuit et développé en open-source sur GitHub ( référence du projet ).

29
dude
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);
}
9
Mohit kumar

J'ai le même problème, un tas de texte entre par une requête xmlhttp. Ce texte est en format HTML. Je dois souligner chaque occurrence.

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

Le problème est que je n'ai pas besoin de surligner du texte dans les balises. Par exemple, je dois mettre en évidence le renard:

Maintenant je peux le remplacer par:

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

Pour répondre à votre question: vous pouvez omettre les options de g dans regexp et seule la première occurrence sera remplacée, mais il s’agit toujours de celui dans la propriété img src et détruit la balise image:

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

C'est la façon dont j'ai résolu le problème, mais je me demandais s'il y avait une meilleure façon, quelque chose que j'ai manqué dans les expressions régulières:

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>");
});
4
HMR

Exemple de TypeScript simple

NOTE: Bien que je sois d’accord avec @Stefan sur beaucoup de choses, j’avais seulement besoin d’un simple surlignage:

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

Et puis en construisant le résultat réel:

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;
        }
    }
}
4
Slavo Vojacek

Voici ma solution JavaScript pur regexp:

function highlight(text) {
    document.body.innerHTML = document.body.innerHTML.replace(
        new RegExp(text + '(?!([^<]+)?<)', 'gi'),
        '<b style="background-color:#ff0;font-size:100%">$&</b>'
    );
}
4
techouse

Aucune des autres solutions ne correspond vraiment à mes besoins, et bien que la solution de Stefan Steiger ait fonctionné comme prévu, je l'ai trouvée un peu trop verbeuse.

Voici ma tentative:

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

Je recommanderais également d'utiliser quelque chose comme escape-string-regexp si vos mots clés peuvent comporter des caractères spéciaux qui doivent être masqués dans les expressions rationnelles:

const keywordRegex = RegExp(keywords.map(escapeRegexp).join('|')), flags);
2
elclanrs

Depuis HTML5, vous pouvez utiliser les balises <mark></mark> pour mettre du texte en surbrillance. Vous pouvez utiliser javascript pour insérer du texte/mot clé entre ces balises. Voici un petit exemple montrant comment marquer et désélectionner du texte.

JSFIDDLE DEMO

1
kasper Taeymans

Je me demandais cela aussi, vous pouvez essayer ce que j'ai appris sur this post.

J'ai utilisé:

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>

vous pouvez aussi l'essayer ici: http://henriquedonati.com/projects/Extension/extension.html

xc

1
Henrique Donati

J'ai trouvé le plugin mettre en évidence comme étant la meilleure correspondance; avec cela, vous pouvez mettre en évidence une partie du contenu :

$ ('li'). surligner ('bla');

1
Igor G.