129 votes

Comment mettre du texte en surbrillance à l'aide de javascript

Quelqu'un peut-il m'aider avec une fonction javascript qui peut mettre en évidence du texte sur une page web. L'exigence est de ne mettre en évidence qu'une seule fois, et non pas de mettre en évidence toutes les occurrences du texte comme nous le faisons dans le cas d'une recherche.

5 votes

Si vous postez le code de la fonction, nous pourrons vous aider. Si vous nous demandez de créer une telle fonction pour vous... c'est moins probable. Vous devez faire quelque chose par vous-même. Commencez à faire quelque chose et revenez quand vous êtes bloqué.

7 votes

Oui, j'ai lu How to Ask et j'ai fait quelque chose par moi-même, mais je suis resté bloqué et c'est pourquoi j'ai demandé. Je travaille sur Android et j'ai peu de connaissances en javasript, c'est pourquoi je ne suis pas en mesure de le faire moi-même. Auparavant, j'utilisais un autre javascript qui faisait le travail mais pas sans certaines limitations. Je n'ai peut-être pas utilisé les bons mots en posant cette question et j'en suis désolé, mais s'il vous plaît, ne pensez pas le contraire.

1 votes

Ce plugin peut vous intéresser : github.com/julmot/jmHighlight . Il peut mettre en évidence les mots clés séparément ou en tant que terme, peut mettre en évidence la correspondance avec votre élément personnalisé et votre nom de classe et peut également rechercher les diacritiques. En outre, il vous permet de filtrer le contexte dans lequel vous recherchez des correspondances.

135voto

guy mograbi Points 2418

Vous pouvez utiliser l'outil jquery effet de surbrillance .

Mais si vous êtes intéressé par le code javascript brut, jetez un coup d'oeil à ce que j'ai obtenu. Il suffit de copier-coller dans un HTML, d'ouvrir le fichier et de cliquer sur "mettre en évidence" - cela devrait mettre en évidence le mot "fox". En termes de performances, je pense que cela conviendrait pour un petit texte et une seule répétition (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>

Edits :

Utilisation de replace

Je vois que cette réponse a gagné en popularité, j'ai pensé que je pourrais la compléter. Vous pouvez aussi facilement utiliser replace

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

Ou pour les occurrences multiples (non pertinent pour la question, mais a été demandé dans les commentaires) vous ajoutez simplement global sur 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.

Remplacer le HTML de la page web entière

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

document.body.innerHTML

0 votes

Merci beaucoup pour votre réponse mais pouvez-vous également me dire comment spécifier la couleur en javascript lui-même.

0 votes

Vous pouvez remplacer le "<span class='highlight'>" avec "<span style='color: " + color + ";'>" la couleur devrait être quelque chose comme var color = "#ff0000";

0 votes

Que faire si je veux mettre en surbrillance toutes les occurrences d'un mot sur toute la page ?@guy mograbi

65voto

Quandary Points 12867

Les solutions proposées ici sont assez mauvaises.

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

Ce que vous devez faire :

Boucle à travers le document HTML, trouve tous les nœuds de texte, récupère l'adresse de l'utilisateur. textContent , obtenir la position du texte en surbrillance avec indexOf (avec une option toLowerCase s'il doit être insensible à la casse), ajouter tout ce qui se trouve avant indexof comme textNode ajoutez le texte correspondant à un espace de surbrillance, et répétez l'opération pour le reste du noeud de texte (la chaîne de surbrillance peut apparaître plusieurs fois dans la chaîne de texte). textContent string).

Voici le code pour cela :

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

};

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

D'ailleurs, si vous effectuez une recherche dans une base de données avec LIKE ,
par exemple WHERE textField LIKE CONCAT('%', @query, '%') [ce que vous ne devriez pas faire, vous devriez utiliser la recherche plein texte ou Lucene], alors vous pouvez échapper à chaque caractère avec \ et ajouter un SQL-escape-statement, de cette façon vous trouverez les caractères spéciaux qui sont des expressions LIKE.

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, et tout autre système RDBMS qui supporte ESCAPE)


Une version révisée du manuscrit :

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

0 votes

Grande réponse La méthode semble exagérée, mais concise ! Je serais certainement intéressé par un test de vitesse avec cette méthode, car dans mon cas, les résultats sont chargés paresseusement dans le DOM (comme il y a des résultats dans le DOM). CAN soit des milliers de résultats), je suis curieux de savoir si cette méthode ajouterait une latence élevée à la charge paresseuse.

11 votes

Désolé, mais aucun de vos arguments n'est vrai. 1. Vous pouvez absolument utiliser une RegExp, vous ne devez simplement pas chercher dans la valeur HTML mais dans la valeur texte d'un élément. 2. Vous pouvez absolument utiliser RegExp avec des caractères diacritiques, comme implémenté dans mark.js . 3. Les notations HTML seront converties en caractères réels dans le DOM du navigateur, vous devez donc absolument les utiliser !

1 votes

@julmot ; To 1 : Ce qui signifie que vous devez itérer à travers chaque élément, ce qui est précisément ce que je fais. Sauf si vous ne vous souciez pas de perdre le formatage, auquel cas vous pouvez rechercher dans document.body.innerText, ce qui sera assez lent. 3. Pas dans le DOM, mais dans la propriété innerText ou textContent d'un élément de texte. Ce qui signifie à nouveau que vous devez itérer à travers les éléments de texte ; ce qui ne peut pas être fait avec regEx AFAIK. 2 : Je ne connais pas mark.js, mais j'éviterais tout ce qui fait un jQuery.each, parce que c'est sacrément lent.

51voto

dude Points 2532

Pourquoi l'utilisation d'une fonction de surbrillance maison est une mauvaise idée.

La raison pour laquelle c'est probablement une mauvaise idée de commencer à construire votre propre fonction de mise en évidence à partir de zéro est que vous rencontrerez certainement des problèmes que d'autres ont déjà résolus. Défis :

  • Il vous faudrait supprimer les nœuds de texte avec des éléments HTML pour mettre en évidence vos correspondances sans détruire les événements DOM et déclencher la régénération du DOM à l'infini (ce qui serait le cas avec par exemple innerHTML )
  • Si vous voulez supprimer les éléments mis en évidence, vous devez supprimer les éléments HTML avec leur contenu et combiner les nœuds de texte divisés pour les recherches ultérieures. C'est nécessaire parce que chaque plugin de surlignage recherche les correspondances à l'intérieur des nœuds de texte et si vos mots clés sont divisés en plusieurs nœuds de texte, ils ne seront pas trouvés.
  • Vous devrez également élaborer des tests pour vous assurer que votre plugin fonctionne dans des situations auxquelles vous n'avez pas pensé. Et je parle de tests inter-navigateurs !

Cela vous semble compliqué ? Si vous souhaitez bénéficier de certaines fonctionnalités telles que l'exclusion de certains éléments de la mise en évidence, la correspondance des diacritiques, la correspondance des synonymes, la recherche dans les iframes, la recherche de mots séparés, etc.

Utiliser un plugin existant

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

Jetez un coup d'œil à mark.js

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

  • rechercher des mots-clés séparément au lieu du terme complet
  • correspondance des diacritiques (par exemple, si "justo" doit aussi correspondre à "justò")
  • ignorer les correspondances à l'intérieur des éléments personnalisés
  • utiliser un élément de mise en évidence personnalisé
  • utiliser une classe de mise en évidence personnalisée
  • synonymes de carte personnalisée
  • recherche également dans les iframes
  • recevoir des termes non trouvés

DEMO

Vous pouvez également 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 ).

8 votes

La mise en évidence du texte ne constitue pas à elle seule une raison suffisante pour que j'intègre jQuery.

13 votes

@Roy J'ai pris cela à cœur. Bonne nouvelle, à partir de la v6.0.0 mark.js a supprimé la dépendance à jQuery et permet désormais de l'utiliser en option comme plugin jQuery.

0 votes

Tout est vrai, sauf : le 1er point n'est pas possible, car on ne peut pas obtenir de gestionnaires d'événements enregistrés, et même si on le pouvait, on ne pourrait pas définir de fonctions anonymes... 2ème : mark.js ne trouve pas non plus le texte entre deux balises, par exemple <span>s</span>ed ne trouvera pas sed... 3ème : chaque fois qu'un navigateur (y compris une nouvelle version) arrive et que vous ne l'avez pas encore testé, il peut se casser. C'est toujours vrai, peu importe le nombre de tests que vous écrivez. A 17kb, marks est trop gros pour ce qu'il fait.

11voto

Mohit kumar Points 106
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);
}

3 votes

Mohit, bienvenue à SO. Une description du code serait la bienvenue !

0 votes

Il ne devrait pas y avoir un moyen de sélectionner du texte sans créer un autre nœud ?

0 votes

@user191433 la question ne concerne pas seulement la sélection du texte, mais aussi l'application des styles. Pour cela, vous avez besoin d'un nœud.

10voto

techouse Points 1214

Voici ma solution regexp pure JavaScript :

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

0 votes

Cela fonctionne parfaitement pour moi lorsque le bloc de texte que j'essaie de mettre en évidence contient des balises HTML.

0 votes

Vous pouvez également modifier la fonction pour qu'elle accepte plusieurs mots par le biais du symbole du tuyau regexp, par exemple one|two|three

0 votes

Il ne remplacera pas le texte si la fin du texte a un caractère > caractère. Modifiez la regex en utilisant (?!([^<]+)?<) pour qu'il fonctionne.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X