How to highlight text using javascript

后端 未结 13 2256
名媛妹妹
名媛妹妹 2020-11-22 02:32

Can someone help me with a javascript function that can highlight text on a web page. And the requirement is to - highlight only once, not like highlight all occurrences of

相关标签:
13条回答
  • 2020-11-22 03:00

    Using the surroundContents() method on the Range type. Its only argument is an element which will wrap that Range.

    function styleSelected() {
      bg = document.createElement("span");
      bg.style.backgroundColor = "yellow";
      window.getSelection().getRangeAt(0).surroundContents(bg);
    }
    
    0 讨论(0)
  • 2020-11-22 03:01

    Here's my regexp pure JavaScript solution:

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

    Fast forward to 2019, Web API now has natively support for highlighting texts:

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

    And you are good to go! anchorNode is the selection starting node, focusNode is the selection ending node. And, if they are text nodes, offset is the index of the starting and ending character in the respective nodes. Here is the documentation

    They even have a live demo

    0 讨论(0)
  • 2020-11-22 03:08

    We if you also want it to be highlighted on page load, there is a new way.

    just add #:~:text=Highlight%20These

    try accessing this link

    https://stackoverflow.com/questions/38588721#:~:text=Highlight%20a%20text

    0 讨论(0)
  • 2020-11-22 03:10

    Simple TypeScript example

    NOTE: While I agree with @Stefan in many things, I only needed a simple match highlighting:

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

    And then constructing the actual result:

    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;
            }
        }
    }
    
    0 讨论(0)
  • 2020-11-22 03:13

    The solutions offered here are quite bad.

    1. You can't use regex, because that way, you search/highlight in the html tags.
    2. You can't use regex, because it doesn't work properly with UTF* (anything with non-latin/English characters).
    3. You can't just do an innerHTML.replace, because this doesn't work when the characters have a special HTML notation, e.g. &amp; for &, &lt; for <, &gt; for >, &auml; for ä, &ouml; for ö &uuml; for ü &szlig; for ß, etc.

    What you need to do:

    Loop through the HTML document, find all text nodes, get the textContent, get the position of the highlight-text with indexOf (with an optional toLowerCase if it should be case-insensitive), append everything before indexof as textNode, append the matched Text with a highlight span, and repeat for the rest of the textnode (the highlight string might occur multiple times in the textContent string).

    Here is the code for this:

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

    Then you can use it like this:

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

    Here's an example HTML document

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

    By the way, if you search in a database with LIKE,
    e.g. WHERE textField LIKE CONCAT('%', @query, '%') [which you shouldn't do, you should use fulltext-search or Lucene], then you can escape every character with \ and add an SQL-escape-statement, that way you'll find special characters that are LIKE-expressions.

    e.g.

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

    and the value of @query is not '%completed%' but '%\c\o\m\p\l\e\t\e\d%'

    (tested, works with SQL-Server and PostgreSQL, and every other RDBMS system that supports ESCAPE)


    A revised 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 
    

    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
    
    0 讨论(0)
提交回复
热议问题