web-dev-qa-db-de.com

So markieren Sie Text mit Javascript

Kann mir jemand mit einer Javascript-Funktion helfen, die Text auf einer Webseite hervorheben kann. Und die Anforderung ist, - nur einmal hervorzuheben, nicht wie bei der Suche alle Vorkommen des Textes hervorzuheben.

78
Ankit

Sie können die Abfrage Hervorhebungseffekt verwenden.

Wenn Sie sich für rohen Javascript-Code interessieren, schauen Sie sich an, was ich habe. Kopieren Sie einfach den Code in einen HTML-Code, öffnen Sie die Datei und klicken Sie auf "Hervorheben" - dies sollte das Wort "Fuchs" hervorheben. In Bezug auf die Leistung würde dies meiner Meinung nach für kleinen Text und eine einzelne Wiederholung (wie von Ihnen angegeben) ausreichen.

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>

Bearbeitungen:

replace verwenden

Ich habe gesehen, dass diese Antwort an Popularität gewonnen hat. Ich dachte, ich könnte sie ergänzen. Sie können auch leicht ersetzen

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

Oder Sie fügen bei mehreren Vorkommen (die für die Frage nicht relevant sind, aber in Kommentaren angegeben wurden) einfach global zum regulären Ersetzungsausdruck hinzu.

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

Hoffe das hilft den neugierigen Kommentatoren.

Ersetzen des HTML auf der gesamten Webseite

um den HTML-Code für eine gesamte Webseite zu ersetzen, sollten Sie sich auf innerHTML im Hauptteil des Dokuments beziehen.

document.body.innerHTML

80
guy mograbi

Die hier angebotenen Lösungen sind ziemlich schlecht.

  1. Sie können Regex nicht verwenden, da Sie auf diese Weise in den HTML-Tags suchen/hervorheben.
  2. Sie können Regex nicht verwenden, da es mit UTF * nicht ordnungsgemäß funktioniert (alles mit nicht-lateinischen/englischen Zeichen).
  3. Sie können nicht einfach eine innerHTML.replace durchführen, da dies nicht funktioniert, wenn die Zeichen eine spezielle HTML-Notation haben, z. &amp; Für &, &lt; Für <, &gt; Für>, &auml; Für ä, &ouml; Für ö &uuml; für ü &szlig; für ß usw.

Was Sie tun müssen:

Durchlaufen Sie das HTML-Dokument, finden Sie alle Textknoten, holen Sie sich das textContent, holen Sie sich die Position des Hervorhebungs-Texts mit indexOf (mit einem optionalen toLowerCase, falls dies der Fall sein sollte -unempfindlich), füge alles vor indexof als textNode an, füge den übereinstimmenden Text mit einem Hervorhebungsbereich an und wiederhole ihn für den Rest des Textknotens (die Hervorhebungszeichenfolge kann in textContent string).

Hier ist der Code dafür:

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

};

Dann kannst du es so benutzen:

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

Hier ist ein Beispiel für ein HTML-Dokument

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

Übrigens, wenn Sie mit LIKE in einer Datenbank suchen,
z.B. WHERE textField LIKE CONCAT('%', @query, '%') [was du nicht machen solltest, du solltest Volltextsuche oder Lucene benutzen], dann kannst du jedes Zeichen mit\maskieren und eine SQL-Escape-Anweisung hinzufügen, damit du Sonderzeichen findest das sind LIKE-Ausdrücke.

z.B.

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

und der Wert von @query ist nicht '%completed%', sondern '%\c\o\m\p\l\e\t\e\d%'

(getestet, funktioniert mit SQL-Server und PostgreSQL sowie jedem anderen RDBMS-System, das ESCAPE unterstützt)


Eine überarbeitete TypeScript-Version:

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 

Verwendung:

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

Warum die Verwendung einer selbst erstellten Hervorhebungsfunktion eine schlechte Idee ist

Der Grund, warum es wahrscheinlich eine schlechte Idee ist, Ihre eigene Hervorhebungsfunktion von Grund auf neu zu erstellen, ist, dass Sie mit Sicherheit auf Probleme stoßen, die andere bereits gelöst haben. Herausforderungen:

  • Sie müssten Textknoten mit HTML-Elementen entfernen, um Ihre Übereinstimmungen hervorzuheben, ohne DOM-Ereignisse zu zerstören und die DOM-Regeneration immer wieder auszulösen (was beispielsweise bei innerHTML der Fall wäre).
  • Wenn Sie markierte Elemente entfernen möchten, müssen Sie HTML-Elemente mit ihrem Inhalt entfernen und die geteilten Textknoten für die weitere Suche kombinieren. Dies ist erforderlich, da jedes Textmarker-Plugin in Textknoten nach Übereinstimmungen sucht und Ihre Keywords nicht gefunden werden, wenn sie in mehrere Textknoten aufgeteilt werden.
  • Sie müssten auch Tests erstellen, um sicherzustellen, dass Ihr Plugin in Situationen funktioniert, an die Sie nicht gedacht haben. Und ich spreche über Cross-Browser-Tests!

Hört sich kompliziert an? Wenn Sie einige Funktionen wie das Ignorieren einiger Elemente von Hervorhebungen, diakritischen Zuordnungen, Synonymzuordnungen, die Suche in Iframes, die Suche nach getrennten Wörtern usw. wünschen, wird dies immer komplizierter.

Verwenden Sie ein vorhandenes Plugin

Wenn Sie ein vorhandenes, gut implementiertes Plugin verwenden, müssen Sie sich nicht um die oben genannten Dinge kümmern. Der Artikel 10 jQuery Text Highlighter Plugins auf Sitepoint vergleicht beliebte Highlighter Plugins.

Schauen Sie sich mark.js an

mark.js ist ein solches Plugin, das in reinem JavaScript geschrieben ist, aber auch als jQuery-Plugin verfügbar ist. Es wurde entwickelt, um mehr Möglichkeiten als die anderen Plugins zu bieten, mit Optionen für:

  • suche nach Schlüsselwörtern separat anstelle des vollständigen Begriffs
  • kartendiakritika (Zum Beispiel, wenn "justo" auch mit "justò" übereinstimmen soll)
  • übereinstimmungen in benutzerdefinierten Elementen ignorieren
  • verwenden Sie ein benutzerdefiniertes Hervorhebungselement
  • verwenden Sie eine benutzerdefinierte Hervorhebungsklasse
  • benutzerdefinierte Synonyme zuordnen
  • suche auch in iframes
  • nicht gefundene Begriffe erhalten

DEMO

Alternativ können Sie diese Geige sehen.

Anwendungsbeispiel :

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

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

Es ist kostenlos und Open-Source auf GitHub entwickelt ( Projektreferenz ).

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

Ich habe das gleiche Problem, eine Reihe von Text kommt durch eine xmlhttp-Anfrage. Dieser Text ist HTML-formatiert. Ich muss jedes Vorkommen hervorheben.

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

Das Problem ist, dass ich keinen Text in Tags hervorheben muss. Zum Beispiel muss ich Fuchs markieren:

Jetzt kann ich es ersetzen durch:

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

Um Ihre Frage zu beantworten: Sie können die Optionen für g in regulären Ausdrücken weglassen und nur das erste Vorkommen wird ersetzt, aber dies ist immer noch das in der Eigenschaft img src und zerstört das Bild-Tag:

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

Auf diese Weise habe ich es gelöst, mich aber gefragt, ob es einen besseren Weg gibt, den ich in regulären Ausdrücken vermisst habe:

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

Einfaches TypeScript-Beispiel

HINWEIS: Obwohl ich in vielen Punkten mit @Stefan einverstanden bin, brauchte ich nur eine einfache Übereinstimmungsmarkierung:

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

Und dann das eigentliche Ergebnis konstruieren:

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

Hier ist meine reine JavaScript-Lösung für reguläre Ausdrücke:

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

Keine der anderen Lösungen entsprach wirklich meinen Anforderungen, und obwohl die Lösung von Stefan Steiger erwartungsgemäß funktionierte, fand ich sie etwas zu ausführlich.

Folgendes ist mein Versuch:

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

Ich würde auch empfehlen, etwas wie escape-string-regexp zu verwenden, wenn Ihre Schlüsselwörter Sonderzeichen enthalten können, die in regulären Ausdrücken maskiert werden müssten:

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

Seit HTML5 können Sie das <mark></mark> Tags zum Markieren von Text. Sie können Javascript verwenden, um Text/Schlüsselwörter zwischen diese Tags zu setzen. Hier finden Sie ein kleines Beispiel für das Markieren und Aufheben der Markierung von Text.

JSFIDDLE DEMO

1
kasper Taeymans

Ich habe mich auch gefragt, ob Sie versuchen können, was ich auf this post gelernt habe.

Ich benutzte:

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>

sie können es auch hier versuchen: http://henriquedonati.com/projects/Extension/extension.html

xc

1
Henrique Donati

Ich habe festgestellt, dass das markieren Plugin die beste Übereinstimmung ist. Mit diesem Plugin können Sie einen Teil des Inhalts markieren :

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

1
Igor G.