วิธีเน้นข้อความโดยใช้จาวาสคริปต์


103

ใครช่วยฉันด้วยฟังก์ชันจาวาสคริปต์ที่สามารถเน้นข้อความบนหน้าเว็บได้ และข้อกำหนดคือ - ไฮไลต์เพียงครั้งเดียวไม่เหมือนกับการเน้นข้อความที่เกิดขึ้นทั้งหมดเหมือนที่เราทำในกรณีของการค้นหา


4
หากคุณโพสต์รหัสของฟังก์ชันเราจะสามารถช่วยได้ หากคุณขอให้เราสร้างฟังก์ชันดังกล่าวให้คุณ ... ก็มีโอกาสน้อย คุณต้องทำอะไรด้วยตัวคุณเอง เริ่มทำบางสิ่งและกลับมาเมื่อคุณติดขัด
Felix Kling

7
ใช่ฉันได้อ่านวิธีการถามและฉันได้ทำบางสิ่งบางอย่างด้วยตัวเอง แต่ฉันติดขัดและนั่นคือเหตุผลที่ฉันถาม ฉันทำงานบน Android และมีความรู้เกี่ยวกับ javasript เพียงเล็กน้อยนั่นคือสาเหตุที่ฉันไม่สามารถทำได้ด้วยตัวเอง ก่อนหน้านี้ฉันใช้จาวาสคริปต์อื่นที่ใช้งานได้ แต่ไม่ใช่โดยไม่มีข้อ จำกัด บางประการ ฉันอาจไม่ได้ใช้คำที่ถูกต้องในขณะที่ถามคำถามนี้และฉันขอโทษสำหรับสิ่งนั้น แต่โปรดอย่าคิดอย่างอื่น
Ankit

1
ปลั๊กอินนี้อาจเป็นที่สนใจสำหรับคุณ: github.com/julmot/jmHighlight สามารถเน้นคำหลักแยกกันหรือเป็นคำสามารถเน้นการจับคู่กับองค์ประกอบที่กำหนดเองและชื่อคลาสของคุณและยังสามารถค้นหาการกำกับเสียง ด้านบนช่วยให้คุณกรองบริบทที่จะค้นหารายการที่ตรงกัน
เพื่อน

1
ชำระเงินด้วยวิธี regex ... stackoverflow.com/a/45519242/2792959

ฉันเตรียมบทความเกี่ยวกับเรื่องนี้ที่นี่exhesham.com/2017/11/20/…
Hesham Yassin

คำตอบ:


109

คุณสามารถใช้ jQuery ผลไฮไลท์

แต่ถ้าคุณสนใจโค้ดจาวาสคริปต์ดิบลองดูสิ่งที่ฉันได้รับเพียงคัดลอกวางลงใน HTML เปิดไฟล์แล้วคลิก "ไฮไลต์" - สิ่งนี้ควรเน้นคำว่า "สุนัขจิ้งจอก" ประสิทธิภาพที่ชาญฉลาดฉันคิดว่าสิ่งนี้จะทำกับข้อความขนาดเล็กและการทำซ้ำเพียงครั้งเดียว (เช่นที่คุณระบุ)

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>

การแก้ไข:

การใช้ replace

ฉันเห็นว่าคำตอบนี้ได้รับความนิยมฉันคิดว่าฉันอาจเพิ่มคำตอบนี้ คุณยังสามารถใช้การแทนที่ได้อย่างง่ายดาย

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

หรือสำหรับเหตุการณ์ที่เกิดขึ้นหลายครั้ง (ไม่เกี่ยวข้องกับคำถาม แต่ถูกถามในความคิดเห็น) คุณเพียงแค่เพิ่มglobalแทนที่นิพจน์ทั่วไป

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

หวังว่านี่จะช่วยให้ผู้แสดงความคิดเห็นที่สนใจ

การแทนที่ HTML เป็นทั้งหน้าเว็บ

ในการแทนที่ HTML สำหรับทั้งหน้าเว็บคุณควรอ้างถึงเนื้อหาinnerHTMLของเอกสาร

document.body.innerHTML


ขอบคุณมากสำหรับการตอบกลับของคุณ แต่คุณสามารถบอกวิธีระบุสีในจาวาสคริปต์ได้หรือไม่
Ankit

คุณสามารถแทนที่"<span class='highlight'>"ด้วย"<span style='color: " + color + ";'>"สีควรเป็นอย่างเช่นvar color = "#ff0000";
Yaniro

จะเกิดอะไรขึ้นถ้าฉันต้องการเน้นที่เกิดขึ้นทั้งหมดของคำในทั้งหน้า @guy mograbi
Baqer Naqvi

4
ใช้ง่าย "แทนที่" เป็นความคิดที่ดี ฉันได้อธิบายเหตุผลไว้ที่นี่: stackoverflow.com/a/32758672/3894981
เพื่อน

2
นี่ไม่ใช่ความคิดที่ดีเพราะจะพยายามเน้นแท็ก HTML / แอตทริบิวต์ / ฯลฯ ตัวอย่างเช่นจะเกิดอะไรขึ้นในกรณีของ: <img src="fox.jpg" /> คุณจะได้รับ HTML ที่ไม่ถูกต้องซึ่งดูเหมือนว่า: <img src="<span class='highlight'>fox</span>.jpg" /> ไม่ดี
dcporter7

52

วิธีแก้ปัญหาที่นำเสนอนี้ค่อนข้างแย่

  1. คุณไม่สามารถใช้ regex ได้เนื่องจากวิธีนี้คุณค้นหา / ไฮไลต์ในแท็ก html
  2. คุณไม่สามารถใช้ regex ได้เนื่องจากไม่สามารถทำงานกับ UTF * ได้อย่างถูกต้อง (อะไรก็ตามที่มีอักขระที่ไม่ใช่ภาษาละติน / อังกฤษ)
  3. คุณไม่สามารถทำ innerHTML.replace ได้เพราะจะใช้ไม่ได้เมื่ออักขระมีสัญกรณ์ HTML พิเศษเช่น&amp;สำหรับ & &lt;สำหรับ <, &gt;for> &auml;สำหรับä, &ouml;สำหรับö &uuml;สำหรับü &szlig;สำหรับßเป็นต้น

สิ่งที่คุณต้องทำ:

ห่วงผ่านเอกสาร HTML ค้นหาโหนดข้อความทั้งหมดได้textContentรับตำแหน่งของไฮไลท์ข้อความด้วยindexOf(กับตัวเลือกtoLowerCaseถ้ามันควรจะเป็นกรณีตาย) ทุกอย่างผนวกก่อนที่จะindexofเป็นtextNodeผนวกข้อความตรงกับช่วงไฮไลท์ และทำซ้ำสำหรับส่วนที่เหลือของโหนดข้อความ (สตริงไฮไลต์อาจเกิดขึ้นหลายครั้งในtextContentสตริง)

นี่คือรหัสสำหรับสิ่งนี้:

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

};

จากนั้นคุณสามารถใช้งานได้ดังนี้:

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

นี่คือตัวอย่างเอกสาร 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>

โดยวิธีการที่ถ้าคุณค้นหาในฐานข้อมูลที่มีLIKE,
เช่นWHERE textField LIKE CONCAT('%', @query, '%')[ซึ่งคุณไม่ควรทำคุณควรใช้ Fulltext ค้นหาหรือ Lucene] จากนั้นคุณสามารถหลบหนีตัวละครทุกตัวมี \ และเพิ่ม SQL หนีคำสั่งวิธีการที่ คุณจะพบอักขระพิเศษที่เป็นนิพจน์ LIKE

เช่น

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

และคุณค่าของ @query ไม่ใช่'%completed%'แต่'%\c\o\m\p\l\e\t\e\d%'

(ทดสอบแล้วใช้งานได้กับ SQL-Server และ PostgreSQL และระบบ RDBMS อื่น ๆ ทั้งหมดที่รองรับ ESCAPE)


รุ่นแก้ไขสคริปต์รุ่น:

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 

การใช้งาน:

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

คำตอบที่ดี .. วิธีการดูเหมือน overkill แต่รัดกุม! แน่นอนจะมีความสนใจในการทำทดสอบความเร็วกับวิธีการที่เป็นในกรณีของฉันผลลัพธ์ที่ขี้เกียจโหลดลงใน DOM (ตามที่มีสามารถเป็นพันผล) อยากรู้ว่าวิธีนี้จะเพิ่มเวลาแฝงที่สูงเพื่อโหลดขี้เกียจ
Pogrindis

6
ขออภัยข้อโต้แย้งของคุณไม่เป็นความจริง 1. คุณสามารถใช้ RegExp ได้อย่างแน่นอนคุณไม่ควรค้นหาภายในค่า HTML แต่เป็นค่าข้อความขององค์ประกอบ 2. คุณอย่างแน่นอนสามารถใช้นิพจน์ทั่วไปที่มีตัวอักษรที่ออกเสียงเป็นดำเนินการในmark.js 3. สัญกรณ์ HTML จะถูกแปลงเป็นอักขระจริงใน DOM ของเบราว์เซอร์ดังนั้นคุณจึงใช้มันอย่างแน่นอน!
เพื่อน

1
@julmot; ถึง 1: ซึ่งหมายความว่าคุณต้องวนซ้ำทุกองค์ประกอบซึ่งเป็นสิ่งที่ฉันทำอย่างแม่นยำ เว้นแต่คุณจะไม่สนใจว่าจะสูญเสียการจัดรูปแบบซึ่งในกรณีนี้คุณสามารถค้นหาใน document.body.innerText ซึ่งจะค่อนข้างช้า 3. ไม่อยู่ใน DOM แต่อยู่ใน innerText หรือคุณสมบัติ textContent ของ text-element ซึ่งหมายความว่าคุณต้องวนซ้ำองค์ประกอบข้อความอีกครั้ง ไม่สามารถทำได้ด้วย regEx AFAIK 2: ไม่รู้จัก mark.js แต่ฉันจะหลีกเลี่ยงทุกสิ่งที่ทำ jQuery.each เพราะมันช้ามาก
Stefan Steiger

1
@StefanSteiger 1. จากนั้นคุณควรแก้ไขความสัมพันธ์ในการตัดสินใจของคุณตามที่บอกว่าเราไม่สามารถค้นหาด้วย RegExp ได้เลยซึ่งไม่เป็นความจริง 2. ไม่ใช้ jQuery.each อะไรทำให้คุณคิดอย่างนั้น? 3. นี่ไม่เป็นความจริงอย่างน้อยก็ใน Firefox เช่นจะถูกแปลงเป็นตัวละครที่เกิดขึ้นจริงแม้เมื่อมีการใช้&auml; innerHTML
เพื่อน

1
สวัสดี @StefanSteiger ที่จริงฉันใช้วิธีแก้ปัญหาของคุณ อันนี้เหมาะมาก แต่มีปัญหาบางอย่างเช่นถ้า II มี P ซึ่งมีสองช่วงและช่วงหนึ่งมีข้อมูลเช่น Diploma MSBTE และช่วงที่สองมีข้อมูล 2012 ตอนนี้ถ้าสตริงที่ฉันต้องการเน้นคือ Diploma MSBTE 2012 สตริงทั้งหมดนี้ฉันตรวจสอบแล้วว่าไม่ทำงานหากทุกอย่างที่จะจับคู่มีอยู่ในช่วงเดียวแสดงว่าใช้งานได้ แต่ถ้าเนื้อหาข้อความอยู่ในแท็กต่างกัน มันไม่ทำงาน. คุณช่วยบอกอะไรเกี่ยวกับเรื่องนี้ได้ไหม?
ganeshk

43

เหตุใดการใช้ฟังก์ชันไฮไลต์ที่ทำเองจึงเป็นความคิดที่ไม่ดี

สาเหตุที่อาจเป็นความคิดที่ไม่ดีในการเริ่มสร้างฟังก์ชันไฮไลต์ของคุณเองตั้งแต่เริ่มต้นก็เพราะว่าคุณจะต้องเจอกับปัญหาที่คนอื่นแก้ไขไปแล้ว ความท้าทาย:

  • คุณจะต้องลบโหนดข้อความที่มีองค์ประกอบ HTML เพื่อเน้นการจับคู่ของคุณโดยไม่ทำลายเหตุการณ์ DOM และทริกเกอร์การสร้าง DOM ซ้ำแล้วซ้ำอีก (ซึ่งจะเป็นเช่นนั้นinnerHTML)
  • หากคุณต้องการลบองค์ประกอบที่ไฮไลต์คุณจะต้องลบองค์ประกอบ HTML ที่มีเนื้อหาและต้องรวมโหนดข้อความที่แยกออกเพื่อค้นหาเพิ่มเติม สิ่งนี้จำเป็นเนื่องจากปลั๊กอินเน้นข้อความทุกตัวจะค้นหาภายในโหนดข้อความเพื่อหารายการที่ตรงกันและหากคำหลักของคุณจะถูกแบ่งออกเป็นหลายโหนดข้อความก็จะไม่พบ
  • คุณจะต้องสร้างการทดสอบเพื่อให้แน่ใจว่าปลั๊กอินของคุณใช้งานได้ในสถานการณ์ที่คุณไม่ได้คิด และฉันกำลังพูดถึงการทดสอบข้ามเบราว์เซอร์!

ฟังดูซับซ้อน? หากคุณต้องการคุณสมบัติบางอย่างเช่นการละเว้นองค์ประกอบบางอย่างจากการไฮไลต์การแมปไดอะคริติกการแมปคำพ้องความหมายการค้นหาภายใน iframe การค้นหาคำที่แยกจากกัน ฯลฯ สิ่งนี้จะซับซ้อนมากขึ้นเรื่อย ๆ

ใช้ปลั๊กอินที่มีอยู่

เมื่อใช้ปลั๊กอินที่มีอยู่และใช้งานได้ดีคุณไม่ต้องกังวลกับสิ่งที่มีชื่อข้างต้น บทความ10 jQuery ปลั๊กอินเน้นข้อความบน Sitepoint เปรียบเทียบปลั๊กอินเน้นข้อความยอดนิยม

ดูที่mark.js

mark.jsเป็นปลั๊กอินที่เขียนด้วย JavaScript บริสุทธิ์ แต่ยังมีให้ใช้งานเป็นปลั๊กอิน jQuery ได้รับการพัฒนาเพื่อให้มีโอกาสมากกว่าปลั๊กอินอื่น ๆ พร้อมตัวเลือกในการ:

  • ค้นหาคำหลักแยกกันแทนที่จะเป็นคำที่สมบูรณ์
  • เครื่องหมายกำกับแผนที่ (ตัวอย่างเช่นถ้า "justo" ควรตรงกับ "justò" ด้วย)
  • ละเว้นการจับคู่ภายในองค์ประกอบที่กำหนดเอง
  • ใช้องค์ประกอบไฮไลต์ที่กำหนดเอง
  • ใช้คลาสไฮไลต์ที่กำหนดเอง
  • แผนที่คำพ้องความหมายที่กำหนดเอง
  • ค้นหาภายใน iframe ด้วย
  • ไม่พบเงื่อนไข

การสาธิต

หรือคุณสามารถเห็นซอนี้

ตัวอย่างการใช้งาน :

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

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

เป็นโอเพนซอร์สฟรีและพัฒนาบน GitHub ( การอ้างอิงโครงการ )


4
การเน้นข้อความเพียงอย่างเดียวไม่ใช่เหตุผลที่ดีพอที่ฉันจะรวม jQuery
รอย

10
@Roy ฉันได้รับสิ่งนี้เป็นหัวใจ ข่าวดีเนื่องจาก v6.0.0 mark.jsสละการพึ่งพา jQuery และทำให้ตอนนี้สามารถเลือกใช้เป็นปลั๊กอิน jQuery ได้
เพื่อน

เป็นจริงทั้งหมดยกเว้น: จุดที่ 1 เป็นไปไม่ได้เนื่องจากคุณไม่สามารถรับตัวจัดการเหตุการณ์ที่ลงทะเบียนได้และแม้ว่าคุณจะทำได้คุณก็ไม่สามารถตั้งค่าฟังก์ชันที่ไม่ระบุตัวตนได้ ... 2nd: mark.js ไม่พบข้อความระหว่างสองแท็กเช่น <span> s </span> ed จะไม่พบ sed ... 3rd: เมื่อใดก็ตามที่เบราว์เซอร์ (รวมถึงเวอร์ชันใหม่) มาพร้อมกับที่คุณยังไม่ได้ทดสอบมันอาจพังได้ นั่นเป็นความจริงเสมอไม่ว่าคุณจะเขียนข้อสอบกี่ข้อก็ตาม ที่ 17kb เครื่องหมายใหญ่เกินไปสำหรับสิ่งที่ทำ
Stefan Steiger

คุณหมายถึงอะไร @StefanSteiger? ไม่สามารถพูดอะไรบางอย่างในจุดแรกโดยไม่มีข้อมูลนั้น อย่างไรก็ตามความคิดเห็นที่สองไม่ถูกต้อง mark.js สามารถค้นหารายการที่ตรงกันระหว่างแท็กโดยใช้acrossElementsตัวเลือก และความคิดเห็นที่สาม; mark.js นั้นไม่ใหญ่นักเมื่อเทียบกับฟังก์ชันที่มีให้ และไม่เป็นไปได้ยากที่จะมีสิ่งผิดปกติเกิดขึ้นในอนาคตเนื่องจาก mark.js ได้รับการทดสอบเช่นเริ่ม Chrome 30 และในเวอร์ชันใหม่ทั้งหมดที่มีการทดสอบหน่วยข้ามเบราว์เซอร์และไม่เคยมีปัญหาใด ๆ กับเวอร์ชันที่กำลังจะมาถึง
เพื่อน

@dude: สามคะแนนหลังย่อหน้าแรก อ่าตกลงไม่มีตัวเลือกนั้นในการสาธิตที่ฉันดู ในกรณีนี้มันอาจจะสมเหตุสมผล แต่ถึงกระนั้นฉันก็พบว่ามันใหญ่เกินไป
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 ยินดีต้อนรับสู่ SO คำอธิบายบางส่วนของรหัสน่าจะดี!
Nippey

มีวิธีเลือกข้อความโดยไม่ต้องสร้างโหนดอื่นหรือไม่?
Dave Gregory

@ user191433 คำถามไม่ใช่แค่การเลือกข้อความ แต่ยังใช้สไตล์ด้วย สำหรับสิ่งนั้นคุณต้องมีโหนด
Christophe

คำเตือน / เคล็ดลับที่ JavaScript span.style.backgroundColor = "yellow";แปลเป็น CSSstyle="background-color: yellow;"นั่นคือความแตกต่างเล็กน้อยระหว่าง camelCase และ dashed-notation ทำให้ฉันสะดุดในตอนแรก
MarkHu

1
คำตอบของ PS Mohit ที่stackoverflow.com/questions/7991474/…เป็นตัวแปรที่มีประสิทธิภาพมากขึ้นของรหัสนี้ (ตัวอย่างเช่นการละเว้นตัวแปรเริ่มต้นและจุดสิ้นสุดซึ่งเป็นเพียงการวินิจฉัย / ไม่ทำงานที่นี่)
MarkHu

8

นี่คือโซลูชัน 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>'
    );
}

สิ่งนี้ใช้ได้ดีกับฉันเมื่อบล็อกข้อความที่ฉันพยายามเน้นมีแท็ก HTML
John Chapman

คุณยังสามารถปรับแต่งฟังก์ชันเพื่อยอมรับคำหลาย ๆ คำผ่านสัญลักษณ์ท่อ regexp เช่นone|two|three
Klemen Tušar

จะไม่แทนที่ข้อความหากท้ายข้อความมี>อักขระ แก้ไข regex โดยใช้(?!([^<]+)?<)เพื่อให้ทำงานได้
Archie Reyes

แก้ไขตามที่ร้องขอ
Klemen Tušar

สมบูรณ์แบบ! นี่คือสิ่งที่ดีที่สุดสำหรับฉัน
marco burrometo

6

ไม่มีวิธีแก้ปัญหาอื่นใดที่ตรงกับความต้องการของฉันเลยและแม้ว่าโซลูชันของ Stefan Steiger จะทำงานได้ตามที่ฉันคาดไว้ แต่ฉันก็พบว่ามันละเอียดเกินไป

ต่อไปนี้เป็นความพยายามของฉัน:

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

ฉันขอแนะนำให้ใช้บางอย่างเช่นescape-string-regexpหากคำหลักของคุณสามารถมีอักขระพิเศษที่ต้องใช้ Escape ใน regexes:

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

สิ่งนี้ใช้ได้ดีสำหรับฉัน แต่ก็ต้องมีวิธี "ยกเลิกการทำเครื่องหมาย" ด้วย
jwzumwalt

5

ฉันมีปัญหาเดียวกันข้อความจำนวนมากเข้ามาในคำขอ xmlhttp ข้อความนี้อยู่ในรูปแบบ html ฉันต้องเน้นทุกเหตุการณ์

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(r,"<span class='hl'>$1</span>")

ในการตอบคำถามของคุณ: คุณสามารถเว้น g ไว้ในตัวเลือก regexp และจะแทนที่เฉพาะเหตุการณ์แรกเท่านั้น แต่ยังคงเป็นสิ่งที่อยู่ในคุณสมบัติ img src และทำลายแท็กรูปภาพ:

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

นี่เป็นวิธีที่ฉันแก้ไขได้ แต่สงสัยว่ามีวิธีที่ดีกว่านี้หรือไม่สิ่งที่ฉันพลาดในนิพจน์ทั่วไป:

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

นี่เป็นเพียงการแก้ปัญหา regex ที่ทำงานสำหรับฉันโดยไม่ต้องล้อเล่นกับหรือ<img src="word"> <a href="word">
yvesmancera

1
กฎทอง: ไม่เลย ใช้. ปกติ นิพจน์. ถึง. ระเบียบ เกี่ยวกับ. ด้วย. XML
ScottMcGready

4

ตัวอย่าง TypeScript อย่างง่าย

หมายเหตุ: ในขณะที่ฉันเห็นด้วยกับ @Stefan ในหลาย ๆ สิ่งฉันต้องการเพียงการเน้นการจับคู่แบบง่ายๆ :

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

จากนั้นสร้างผลลัพธ์ที่แท้จริง:

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

เนื่องจาก HTML5 คุณสามารถใช้ไฟล์ <mark></mark>แท็กเพื่อเน้นข้อความได้ คุณสามารถใช้จาวาสคริปต์เพื่อตัดข้อความ / คำสำคัญระหว่างแท็กเหล่านี้ นี่คือตัวอย่างเล็กน้อยของวิธีการทำเครื่องหมายและยกเลิกการทำเครื่องหมายข้อความ

การสาธิต JSFIDDLE


innerHTMLอันตราย. มันจะลบเหตุการณ์
เพื่อน

2
นอกจากนี้ยังทำงานไม่ถูกต้องเนื่องจากตัวอย่างเช่นหากคุณเข้าสู่ JSFIDDLE "Lorem" ระบบจะทำเครื่องหมายเพียงอินสแตนซ์แรกเท่านั้น
agm1984

1
ยินดีต้อนรับคุณเพียงแค่ต้องแทนที่สิ่งที่เกิดขึ้นทั้งหมดของคำหลัก นี่คือตัวอย่างของ regex ทั่วโลกjsfiddle.net/de5q704L/73
kasper Taeymans

2

ก้าวไปข้างหน้าอย่างรวดเร็วในปี 2019 ขณะนี้ Web API ได้รองรับการเน้นข้อความแล้ว:

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

และคุณพร้อมที่จะไป! anchorNodeคือโหนดเริ่มต้นการเลือกfocusNodeคือโหนดสิ้นสุดการเลือก และถ้าเป็นโหนดข้อความoffsetคือดัชนีของอักขระเริ่มต้นและสิ้นสุดในโหนดที่เกี่ยวข้อง นี่คือเอกสาร

พวกเขายังมีการสาธิตสด


โอ้นี่ยอดเยี่ยมมาก เพียงแค่ใช้สิ่งนี้: selection.setBaseAndExtent (neededNode, 0, requiredNode, 1); เพื่อเน้นโหนดเดียวที่คุณต้องการ และใช้ได้กับ Gutenberg
tonyAndr

1

ผมสงสัยว่าที่มากเกินไปคุณอาจจะลองสิ่งที่ผมได้เรียนรู้เกี่ยวกับการนี้โพสต์

ฉันใช้:

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>

คุณสามารถลองได้ที่นี่: http://henriquedonati.com/projects/Extension/extension.html

xc


1

หากคุณต้องการให้ไฮไลต์ในการโหลดหน้าเว็บมีวิธีใหม่

เพียงแค่เพิ่ม #:~:text=Highlight%20These

ลองเข้าลิงค์นี้

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


-1

ใช้วิธีการsurroundContents ()ในประเภทช่วง อาร์กิวเมนต์เดียวคือองค์ประกอบที่จะตัดช่วงนั้น

function styleSelected() {
  bg = document.createElement("span");
  bg.style.backgroundColor = "yellow";
  window.getSelection().getRangeAt(0).surroundContents(bg);
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.