Cách tô sáng văn bản bằng javascript


98

Ai đó có thể giúp tôi với một hàm javascript có thể đánh dấu văn bản trên một trang web. Và yêu cầu là - chỉ đánh dấu một lần, không phải đánh dấu tất cả các lần xuất hiện của văn bản như chúng ta làm trong trường hợp tìm kiếm.


4
Nếu bạn đăng mã của chức năng, chúng tôi sẽ có thể trợ giúp. Nếu bạn yêu cầu chúng tôi tạo một chức năng như vậy cho bạn ... thì ít khả năng hơn. Bạn phải làm một cái gì đó của riêng bạn. Bắt đầu làm điều gì đó và quay lại khi bạn gặp khó khăn.
Felix Kling

7
YEs Tôi đã đọc How to Ask & Tôi đã tự làm điều gì đó nhưng tôi gặp khó khăn và đó là lý do tại sao tôi hỏi. Tôi làm việc trên Android và có ít kiến ​​thức về javasript, đó là lý do tại sao tôi không thể tự mình làm điều đó. Trước đó, tôi đã sử dụng một javascript khác đã thực hiện công việc nhưng không phải là không có giới hạn nhất định. Tôi có thể đã không sử dụng đúng từ khi hỏi câu hỏi này và tôi xin lỗi vì điều đó nhưng xin đừng nghĩ khác.
Ankit

1
Plugin này có thể bạn quan tâm: github.com/julmot/jmHighlight . Nó có thể đánh dấu các từ khóa riêng biệt hoặc dưới dạng một thuật ngữ, có thể làm nổi bật kết hợp với phần tử tùy chỉnh và tên lớp của bạn và cũng có thể tìm kiếm các dấu phụ. Ở trên cùng, nó cho phép bạn lọc ngữ cảnh để tìm kiếm các kết quả phù hợp.
dude

1
Thanh toán regex sau cách ... stackoverflow.com/a/45519242/2792959

Tôi đã chuẩn bị một bài báo về điều đó ở đây, exhesham.com/2017/11/20/…
Hesham Yassin

Câu trả lời:


100

Bạn có thể sử dụng hiệu ứng đánh dấu jquery .

Nhưng nếu bạn quan tâm đến mã javascript thô, hãy xem những gì tôi nhận được Chỉ cần sao chép dán vào HTML, mở tệp và nhấp vào "tô sáng" - điều này sẽ làm nổi bật từ "cáo". Hiệu suất khôn ngoan Tôi nghĩ điều này sẽ làm cho văn bản nhỏ và một lần lặp lại (như bạn đã chỉ định)

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>

Chỉnh sửa:

Sử dụng replace

Tôi thấy câu trả lời này đã trở nên phổ biến, tôi nghĩ tôi có thể thêm vào nó. Bạn cũng có thể dễ dàng sử dụng thay thế

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

Hoặc đối với nhiều lần xuất hiện (không liên quan đến câu hỏi, nhưng được hỏi trong nhận xét), bạn chỉ cần thêm globalvào biểu thức chính quy thay thế.

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

Hy vọng điều này sẽ giúp ích cho những người bình luận hấp dẫn.

Thay thế HTML cho toàn bộ trang web

để thay thế HTML cho toàn bộ trang web, bạn nên tham khảo phần nội dung innerHTMLcủa tài liệu.

document.body.innerHTML


Cảm ơn rất nhiều cho trả lời của bạn nhưng bạn cũng có thể cho tôi biết làm thế nào để xác định màu sắc trong javascript bản thân
Ankit

Bạn có thể thay thế "<span class='highlight'>"với "<span style='color: " + color + ";'>", màu sắc nên được một cái gì đó giống nhưvar color = "#ff0000";
Yaniro

những gì nếu tôi muốn làm nổi bật tất cả các lần xuất hiện của một từ trên toàn bộ trang @ Guy mograbi?
Baqer Naqvi

4
Sử dụng một "thay thế" đơn giản là một ý tưởng tồi . Tôi đã mô tả lý do tại sao ở đây: stackoverflow.com/a/32758672/3894981
anh bạn

2
Đây không phải là một ý tưởng tuyệt vời vì điều này sẽ cố gắng làm nổi bật các thẻ / thuộc tính HTML / v.v. Ví dụ, điều gì sẽ xảy ra trong trường hợp: <img src="fox.jpg" /> Bạn sẽ nhận được HTML không hợp lệ mà sẽ trông như thế: <img src="<span class='highlight'>fox</span>.jpg" /> Không tốt
dcporter7

46

Các giải pháp được đưa ra ở đây khá tệ.

  1. Bạn không thể sử dụng regex, bởi vì theo cách đó, bạn tìm kiếm / đánh dấu trong các thẻ html.
  2. Bạn không thể sử dụng regex vì nó không hoạt động đúng với UTF * (bất kỳ thứ gì có ký tự không phải latin / tiếng Anh).
  3. Bạn không thể chỉ thực hiện một innerHTML.replace, vì điều này không hiệu quả khi các ký tự có ký hiệu HTML đặc biệt, ví dụ: &amp;&, &lt;cho <, &gt;cho>, &auml;cho ä, &ouml;cho ö &uuml;cho ü &szlig;cho ß, v.v.

Bạn cần gì để làm:

Lặp qua tài liệu HTML, tìm tất cả các nút văn bản, lấy textContent, lấy vị trí của văn bản được đánh dấu bằng indexOf(với tùy chọn toLowerCasenếu nó phải là phân biệt chữ hoa chữ thường), nối mọi thứ trước indexofnhư textNode, nối Văn bản phù hợp với khoảng đánh dấu, và lặp lại cho phần còn lại của textnode (chuỗi đánh dấu có thể xuất hiện nhiều lần trong textContentchuỗi).

Đây là mã cho việc này:

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

};

Sau đó, bạn có thể sử dụng nó như thế này:

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

Đây là một tài liệu HTML mẫu

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

Nhân tiện, nếu bạn tìm kiếm trong cơ sở dữ liệu LIKE,
ví dụ WHERE textField LIKE CONCAT('%', @query, '%')[điều bạn không nên làm, bạn nên sử dụng fulltext-search hoặc Lucene], thì bạn có thể thoát mọi ký tự bằng \ và thêm câu lệnh SQL-Escape, theo cách đó bạn sẽ tìm thấy các ký tự đặc biệt là biểu thức LIKE.

ví dụ

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

và giá trị của @query không phải là '%completed%'nhưng'%\c\o\m\p\l\e\t\e\d%'

(đã thử nghiệm, hoạt động với SQL-Server và PostgreSQL và mọi hệ thống RDBMS khác hỗ trợ ESCAPE)


Một phiên bản đánh máy đã sửa đổi:

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 

Sử dụng:

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

Câu trả lời tuyệt vời .. Phương pháp có vẻ như quá mức cần thiết, nhưng ngắn gọn! Chắc chắn sẽ quan tâm đến việc thực hiện kiểm tra tốc độ với phương pháp đó như trong trường hợp của tôi, kết quả được tải chậm vào DOM (vì CÓ THỂ có hàng nghìn kết quả), tò mò liệu phương pháp này có thêm độ trễ cao cho tải chậm hay không.
Pogrindis

5
Xin lỗi, nhưng không có lập luận nào của bạn là đúng. 1. Bạn hoàn toàn có thể sử dụng RegExp, bạn chỉ không nên tìm kiếm bên trong giá trị HTML mà là giá trị văn bản của một phần tử. 2. Bạn hoàn toàn có thể sử dụng RegExp với các nhân vật dấu phụ, như thực hiện trong mark.js . 3. Các ký hiệu HTML sẽ được chuyển đổi thành các ký tự thực trong DOM của trình duyệt, vì vậy bạn cũng hoàn toàn sử dụng chúng!
anh chàng

1
@julmot; Điều 1: Có nghĩa là bạn cần phải lặp lại mọi phần tử, đó chính xác là những gì tôi làm. Trừ khi bạn không quan tâm đến việc mất định dạng, trong trường hợp đó, bạn có thể tìm kiếm trong document.body.innerText, điều này sẽ khá chậm. 3. Không phải trong DOM, mà là trong innerText hoặc thuộc tính textContent của một phần tử văn bản. Điều này một lần nữa có nghĩa là bạn cần phải lặp qua các phần tử văn bản; không thể thực hiện được với regEx AFAIK. 2: Không biết mark.js, nhưng tôi sẽ tránh mọi thứ thực hiện jQuery.each, vì nó quá chậm.
Stefan Steiger

1
@StefanSteiger 1. Sau đó, bạn nên sửa lỗi tương đối về quyết định của mình, vì nó nói rằng chúng tôi không thể tìm kiếm bằng RegExp, điều này không đúng 2. Nó không sử dụng jQuery.each. Điều gì làm cho bạn nghĩ? 3. Điều này không đúng, ít nhất là trong Firefox.&auml;Ví dụ: sẽ được chuyển đổi thành ký tự thực, ngay cả khi sử dụng innerHTML.
dude

1
Xin chào @StefanSteiger Trên thực tế, tôi đang sử dụng các giải pháp của bạn. Cái này là hoàn hảo. Nhưng có một số vấn đề như, Nếu II có P Trong đó có hai nhịp và một nhịp có dữ liệu như Diploma MSBTE và nhịp thứ hai có dữ liệu 2012. Bây giờ Nếu chuỗi mà tôi muốn đánh dấu là Diploma MSBTE 2012, toàn bộ chuỗi này sau đó tôi đã kiểm tra điều này không hoạt động, Nếu mọi thứ sắp khớp đều xuất hiện trong một khoảng thì nó hoạt động, nhưng nếu nội dung văn bản nằm trong các thẻ khác nhau thì Nó không hoạt động. Bạn có thể vui lòng cho biết điều gì đó về điều này?
ganeshk

40

Tại sao sử dụng chức năng đánh dấu tự tạo là một ý tưởng tồi

Lý do tại sao có lẽ là một ý tưởng tồi nếu bắt đầu xây dựng chức năng đánh dấu của riêng bạn từ đầu là vì bạn chắc chắn sẽ gặp phải những vấn đề mà người khác đã giải quyết. Những thách thức:

  • Bạn sẽ cần phải xóa các nút văn bản có các phần tử HTML để làm nổi bật các kết quả phù hợp của mình mà không phá hủy các sự kiện DOM và kích hoạt tái tạo DOM lặp đi lặp lại (ví dụ như trường hợp này innerHTML)
  • Nếu bạn muốn xóa các phần tử được đánh dấu, bạn sẽ phải xóa các phần tử HTML cùng với nội dung của chúng và cũng phải kết hợp các nút văn bản đã tách để tìm kiếm thêm. Điều này là cần thiết vì mọi plugin đánh dấu đều tìm kiếm bên trong các nút văn bản để tìm các kết quả phù hợp và nếu từ khóa của bạn sẽ được chia thành nhiều nút văn bản thì chúng sẽ không được tìm thấy.
  • Bạn cũng sẽ cần xây dựng các bài kiểm tra để đảm bảo plugin của bạn hoạt động trong các tình huống mà bạn chưa nghĩ đến. Và tôi đang nói về các thử nghiệm trên nhiều trình duyệt!

Nghe có vẻ phức tạp? Nếu bạn muốn một số tính năng như bỏ qua một số yếu tố khỏi đánh dấu, ánh xạ dấu phụ, ánh xạ từ đồng nghĩa, tìm kiếm bên trong iframe, tìm kiếm từ được phân tách, v.v. thì điều này càng trở nên phức tạp hơn.

Sử dụng một plugin hiện có

Khi sử dụng một plugin hiện có, được triển khai tốt, bạn không phải lo lắng về những thứ đã nêu ở trên. Bài viết 10 plugin đánh dấu văn bản jQuery trên Sitepoint sẽ so sánh các plugin highlighter phổ biến.

Hãy xem mark.js

mark.js là một plugin được viết bằng JavaScript thuần túy, nhưng cũng có sẵn dưới dạng plugin jQuery. Nó được phát triển để cung cấp nhiều cơ hội hơn các plugin khác với các tùy chọn:

  • tìm kiếm các từ khóa riêng lẻ thay vì cụm từ hoàn chỉnh
  • dấu phụ trên bản đồ (Ví dụ: nếu "justo" cũng phải khớp với "justò")
  • bỏ qua các kết quả phù hợp bên trong các phần tử tùy chỉnh
  • sử dụng phần tử tô sáng tùy chỉnh
  • sử dụng lớp đánh dấu tùy chỉnh
  • từ đồng nghĩa tùy chỉnh bản đồ
  • cũng tìm kiếm bên trong iframe
  • nhận các điều khoản không tìm thấy

BẢN GIỚI THIỆU

Ngoài ra, bạn có thể xem trò chơi này .

Ví dụ sử dụng :

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

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

Nó miễn phí và được phát triển mã nguồn mở trên GitHub ( tham khảo dự án ).


4
Chỉ đánh dấu văn bản không phải là lý do đủ tốt để tôi đưa vào jQuery.
Roy

10
@Roy Tôi đã ghi nhớ điều này. Tin tốt là kể từ phiên bản v6.0.0 mark.js đã loại bỏ sự phụ thuộc của jQuery và giờ đây có thể tùy chọn sử dụng nó như một plugin jQuery.
dude

Tất cả đều đúng, ngoại trừ: điểm thứ nhất là không thể, vì bạn không thể có được các trình xử lý sự kiện đã đăng ký và ngay cả khi có thể, bạn cũng không thể đặt các hàm ẩn danh ... Thứ 2: mark.js cũng không tìm thấy văn bản giữa hai thẻ, ví dụ: <span> s </span> ed sẽ không tìm thấy sed ... 3rd: bất cứ khi nào một trình duyệt (bao gồm cả phiên bản mới) xuất hiện mà bạn chưa thử nghiệm, nó có thể bị hỏng. Điều đó luôn đúng, bất kể bạn viết bao nhiêu bài kiểm tra. Ở 17kb, điểm quá lớn so với những gì nó làm.
Stefan Steiger

Bạn đang đề cập đến @StefanSteiger những điểm nào? Không thể nói điều gì đó đến điểm đầu tiên mà không có thông tin đó. Tuy nhiên, nhận xét thứ hai là sai, mark.js có thể tìm thấy các điểm trùng khớp giữa các thẻ bằng cách sử dụng acrossElementstùy chọn. Và đến nhận xét thứ ba; mark.js không lớn so với các chức năng mà nó cung cấp. Và không, không chắc có điều gì đó sẽ xảy ra trong tương lai, vì mark.js đã được thử nghiệm, ví dụ như khởi động Chrome 30 và trong tất cả các phiên bản mới hơn với thử nghiệm đơn vị trình duyệt chéo và không bao giờ có bất kỳ sự cố nào với các phiên bản sắp tới.
dude

@dude: Ba điểm sau đoạn đầu tiên. À, được rồi, thiếu tùy chọn đó trong bản demo mà tôi đã xem. Trong trường hợp đó, nó có thể có ý nghĩa. Nhưng tôi vẫn thấy nó quá lớn.
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);
}

3
Mohit, chào mừng đến với SO. Một số mô tả về mã sẽ rất hay!
Nippey 26/10/12

Không nên có cách nào để chọn văn bản mà không cần tạo một nút khác?
Dave Gregory

@ user191433 câu hỏi không chỉ là chọn văn bản mà còn là áp dụng các kiểu. Đối với điều đó, bạn cần một nút.
Christophe

Nhắc nhở / mẹo rằng JavaScript span.style.backgroundColor = "yellow";chuyển sang CSS style="background-color: yellow;"- đó là sự khác biệt nhỏ giữa camelCase và ký hiệu gạch ngang khiến tôi vấp phải lúc đầu.
MarkHu

1
Câu trả lời của PS Mohit tại stackoverflow.com/questions/7991474/… là một biến thể hợp lý hơn của mã này. (ví dụ: bỏ qua các biến bắt đầu và kết thúc chỉ mang tính chất chẩn đoán / không có chức năng ở đây.)
MarkHu

7

Đây là giải pháp JavaScript thuần regexp của tôi:

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

Điều này hoàn toàn phù hợp với tôi khi khối văn bản tôi đang cố đánh dấu chứa các thẻ HTML.
John Chapman

Bạn cũng có thể tinh chỉnh các chức năng để chấp nhận nhiều lời qua biểu tượng regexp đường ống, ví dụone|two|three
Klemen Tusar

Nó sẽ không thay thế văn bản nếu cuối văn bản có một >ký tự. Sửa đổi regex bằng cách sử dụng (?!([^<]+)?<)để nó hoạt động.
Archie Reyes

Đã sửa đổi theo yêu cầu.
Klemen Tušar

Hoàn hảo! Đây là tốt nhất cho tôi
marco burrometo

5

Tôi có cùng một vấn đề, một loạt văn bản đến thông qua một yêu cầu xmlhttp. Văn bản này được định dạng html. Tôi cần phải làm nổi bật mỗi lần xuất hiện.

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

Vấn đề là tôi không cần đánh dấu văn bản trong thẻ. Ví dụ, tôi cần tô sáng con cáo:

Bây giờ tôi có thể thay thế nó bằng:

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

Để trả lời câu hỏi của bạn: bạn có thể bỏ đi các tùy chọn g trong regexp và chỉ lần xuất hiện đầu tiên sẽ được thay thế nhưng đây vẫn là lần xuất hiện trong thuộc tính img src và hủy thẻ hình ảnh:

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

Đây là cách tôi đã giải quyết nó nhưng tôi đang tự hỏi liệu có cách nào tốt hơn không, điều gì đó tôi đã bỏ qua trong biểu thức chính quy:

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

Đây là giải pháp regex duy nhất phù hợp với tôi mà không gây rắc rối với <img src="word">hoặc <a href="word">.
yvesmancera

1
Quy tắc vàng: Không bao giờ. Sử dụng. Đều đặn. Biểu thức. Đến. Lộn xộn. Trong khoảng. Với. XML.
ScottMcGready

5

Không có giải pháp nào khác thực sự phù hợp với nhu cầu của tôi và mặc dù giải pháp của Stefan Steiger hoạt động như tôi mong đợi nhưng tôi thấy nó hơi dài dòng.

Sau đây là nỗ lực của tôi:

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

Tôi cũng khuyên bạn nên sử dụng một cái gì đó như Escape-string-regexp nếu từ khóa của bạn có thể có các ký tự đặc biệt cần được thoát trong regexes:

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

Điều này làm việc tốt đối với tôi nhưng nó cũng cần một cách để "bỏ đánh dấu" '
jwzumwalt

4

Ví dụ về TypeScript đơn giản

LƯU Ý: Mặc dù tôi đồng ý với @Stefan về nhiều thứ, nhưng tôi chỉ cần đánh dấu kết hợp đơn giản :

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

Và sau đó xây dựng kết quả thực tế:

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

3

Vì HTML5, bạn có thể sử dụng <mark></mark> thẻ để đánh dấu văn bản. Bạn có thể sử dụng javascript để bọc một số văn bản / từ khóa giữa các thẻ này. Đây là một ví dụ nhỏ về cách đánh dấu và bỏ đánh dấu văn bản.

JSFIDDLE DEMO


innerHTMLlà nguy hiểm. Nó sẽ xóa các sự kiện.
dude

2
Điều này cũng không hoạt động bình thường vì, ví dụ: nếu bạn nhập vào JSFIDDLE "Lorem", nó chỉ đánh dấu phiên bản đầu tiên của nó.
agm1984

1
Chào bạn, bạn chỉ cần thay thế tất cả các lần xuất hiện của từ khóa. đây là một ví dụ với regex global jsfiddle.net/de5q704L/73
kasper Taeymans 12/09/17

2

Tua nhanh đến năm 2019, Web API hiện đã hỗ trợ nguyên bản để đánh dấu văn bản:

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

Và bạn tốt để đi! anchorNodelà nút bắt đầu lựa chọn, focusNodelà nút kết thúc lựa chọn. Và, nếu chúng là các nút văn bản, offsetlà chỉ số của ký tự bắt đầu và kết thúc trong các nút tương ứng. Đây là tài liệu

Họ thậm chí còn có một bản demo trực tiếp


oh điều này là tuyệt vời. chỉ cần sử dụng nó như sau: select.setBaseAndExtent (mong muốnNode, 0, mong muốnNode, 1); để làm nổi bật nút duy nhất bạn cần. và nó hoạt động với Gutenberg
tonyAndr

1

Tôi cũng tự hỏi điều đó, bạn có thể thử những gì tôi học được về điều này bài đăng .

Tôi đã sử dụng:

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>

bạn cũng có thể thử nó tại đây: http://henriquedonati.com/projects/Extension/extension.html

xc


0

Chúng tôi nếu bạn cũng muốn nó được đánh dấu khi tải trang, có một cách mới.

chỉ cần thêm #:~:text=Highlight%20These

thử truy cập liên kết này

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


-1

Sử dụng phương thức surroundContents () trên loại Phạm vi . Đối số duy nhất của nó là một phần tử sẽ bao bọc Phạm vi đó.

function styleSelected() {
  bg = document.createElement("span");
  bg.style.backgroundColor = "yellow";
  window.getSelection().getRangeAt(0).surroundContents(bg);
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.