Як виділити текст за допомогою JavaScript


98

Хтось може мені допомогти з функцією javascript, яка може виділити текст на веб-сторінці. І вимога полягає у тому, щоб - виділяти лише один раз, а не як виділяти всі входження тексту, як це робимо у випадку пошуку.


4
Якщо ви опублікуєте код функції, ми зможемо вам допомогти. Якщо ви попросите нас створити для вас таку функцію ... це менш імовірно. Треба щось робити самостійно. Почніть щось робити і повертайтеся, коли застрягнете.
Фелікс Клінг,

7
ТАК Я читав "Як запитувати" і я щось робив самостійно, але я застряг і тому запитав. Я працюю на Android і маю незначні знання про javasript, тому я не можу зробити це самостійно. Раніше я використовував інший javascript, який виконував цю роботу, але не без певних обмежень. Можливо, я не вживав правильних слів під час цього запитання, і мені шкода за це, але, будь ласка, не думайте про інше.
Ankit

1
Цей плагін може зацікавити вас: github.com/julmot/jmHighlight . Він може виділяти ключові слова окремо або як термін, може виділяти збіг із вашим користувацьким елементом та назвою класу, а також може шукати діакритичні знаки. Зверху це дозволяє фільтрувати контекст, в якому здійснюється пошук відповідностей.
чувак,

1
Checkout наступне регулярний вираз спосіб ... stackoverflow.com/a/45519242/2792959

Я підготував статтю про це тут, exhesham.com/2017/11/20/…
Hesham Yassin

Відповіді:


100

Ви можете використовувати ефект підсвічування jquery .

Але якщо вас цікавить необроблений код JavaScript, подивіться, що я отримав. Просто скопіюйте вставку в 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регулярний вираз replace.

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

Сподіваюся, це допоможе заінтригованим коментаторам.

Заміна HTML на всю веб-сторінку

щоб замінити HTML для цілої веб-сторінки, слід звернутися до innerHTMLтіла документа.

document.body.innerHTML


Велике спасибі за вашу відповідь, але чи можете ви також сказати мені, як вказати колір у самому javascript
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

46

Рішення, пропоновані тут, досить погані.

  1. Ви не можете використовувати регулярний вираз, оскільки таким чином ви здійснюєте пошук / виділення в тегах html.
  2. Ви не можете використовувати регулярний вираз, оскільки він не працює належним чином з UTF * (все, що має нелатинські / англійські символи).
  3. Ви не можете просто зробити внутрішнійHTML.replace, оскільки це не працює, коли символи мають спеціальні HTML-позначення, наприклад, &amp;для &, &lt;для <, &gt;для>, &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-search або Lucene], тоді ви можете уникнути кожного символу за допомогою \ та додати оператор SQL-escape-таким чином ви знайдете спеціальні символи, які є LIKE-виразами.

напр

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

а значення @query - ні '%completed%' але'%\c\o\m\p\l\e\t\e\d%'

(протестовано, працює з SQL-сервером та PostgreSQL та будь-якою іншою системою СУБД, що підтримує 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

Чудова відповідь .. Метод виглядає як надмірний, але короткий! Безумовно, мені буде цікаво провести тест на швидкість за допомогою цього методу, оскільки в моєму випадку результати ледаче завантажуються в DOM (оскільки результатів МОЖЕ бути тисячі), цікаво, чи додасть цей метод високу затримку для ледачого навантаження.
Погріндіс

5
Вибачте, але жоден з ваших аргументів не відповідає дійсності. 1. Ви абсолютно можете використовувати RegExp, просто не слід шукати всередині значення HTML, а текстове значення елемента. 2. Ви можете абсолютно використовувати RegExp з діакритичними символами, як це реалізовано в mark.js . 3. Позначення HTML будуть перетворені на фактичні символи в DOM браузера, тому ви також абсолютно їх використовуєте!
чувак,

1
@julmot; До 1: Це означає, що вам потрібно переглядати кожен елемент, саме це я і роблю. Якщо ви не дбаєте про втрату форматування, у цьому випадку ви можете шукати в document.body.innerText, що буде досить повільно. 3. Не в DOM, а у властивості innerText або textContent текстового елемента. Що ще раз означає, що вам потрібно переглядати елементи тексту; неможливо зробити за допомогою regEx AFAIK. 2: Не знаю mark.js, але я б уникав усього, що робить jQuery.each, тому що це по-справжньому повільно.
Стефан Штайгер

1
@StefanSteiger 1. Тоді вам слід виправити співвідношення рішення, оскільки там сказано, що ми взагалі не можемо шукати за допомогою RegExp, що не відповідає дійсності 2. Він не використовує jQuery.each. Що змушує вас думати про це? 3. Це неправда, принаймні у Firefox. &auml;наприклад, буде перетворено на дійсний символ, навіть під час використання innerHTML.
чувак

1
Привіт @StefanSteiger Насправді, я використовую твої рішення. Цей ідеальний. Але є така проблема, як, якщо у мене є Р, в якому є два проміжки, а один проміжок має дані, такі як Диплом MSBTE, а другий проміжок має дані 2012 року. Тепер якщо рядок, який я хочу виділити, є Diploma MSBTE 2012, то весь цей рядок, то я перевірив, що це не працює, якщо все, що буде співпадати, присутнє в одному інтервалі, тоді воно працює, але якщо текстовий вміст знаходиться в тегах diff, тоді Це не працює. Чи можете ви сказати щось про це?
ganeshk 07.03.18

41

Чому використання саморобної функції виділення - погана ідея

Причина, чому, мабуть, погана ідея починати створювати власну функцію підсвічування з нуля, полягає в тому, що ви, безумовно, зіткнетеся з проблемами, які інші вже вирішили. Виклики:

  • Вам потрібно буде видалити текстові вузли з елементами HTML, щоб виділити ваші збіги, не руйнуючи подій DOM і не запускаючи регенерацію DOM знову і знову (що буде у випадку, наприклад innerHTML )
  • Якщо ви хочете видалити виділені елементи, вам доведеться видалити елементи HTML з їх вмістом, а також поєднати розділені текстові вузли для подальших пошуків. Це необхідно, оскільки кожен плагін для виділення здійснює пошук збігів у текстових вузлах, і якщо ваші ключові слова будуть розділені на кілька текстових вузлів, вони не будуть знайдені.
  • Вам також потрібно буде побудувати тести, щоб переконатися, що ваш плагін працює в ситуаціях, про які ви не думали. А я про крос-браузерні тести!

Звучить складно? Якщо ви хочете, щоб деякі функції, такі як ігнорування деяких елементів, виділення, відображення діакритики, відображення синонімів, пошук усередині фреймів, пошук окремих слів тощо, стають дедалі складнішими.

Використовуйте наявний плагін

Використовуючи існуючий, добре реалізований плагін, вам не доведеться турбуватися про вищезазначені речі. У статті 10 jQuery текстові плагіни для виділення тексту на Sitepoint порівнюються популярні плагіни для виділення.

Погляньте на mark.js

mark.js - це такий плагін, який написаний на чистому JavaScript, але також доступний як плагін jQuery. Він був розроблений, щоб запропонувати більше можливостей, ніж інші плагіни, з опціями:

  • шукати ключові слова окремо замість повного терміна
  • діакритичні карти (наприклад, якщо "justo" також має відповідати "justò")
  • ігнорувати збіги всередині призначених для користувача елементів
  • використовувати спеціальний елемент підсвічування
  • використовувати спеціальний клас підсвічування
  • карта власні синоніми -
  • шукати також усередині фреймів
  • отримувати не знайдені умови

DEMO

Ви також можете побачити цю скрипку .

Приклад використання :

// 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-й пункт неможливий, тому що ви не можете отримати зареєстровані обробники подій, і навіть якби ви могли, ви не могли встановити анонімні функції ... 2-й: mark.js також не знаходить тексту між двома тегами, напр. <span> s </span> ed не знайде sed ... 3-е: щоразу, коли з’являється браузер (включаючи нову версію), який ви ще не тестували, він може зламатися. Це завжди так, незалежно від того, скільки тестів ви пишете. На 17 кб оцінки занадто великі для того, що вони роблять.
Стефан Штайгер

Які моменти Ви маєте на увазі @StefanSteiger? Не можу сказати щось перше без цієї інформації. Однак другий коментар помилковий, mark.js може знаходити збіги між тегами, використовуючи acrossElementsопцію. І до третього коментаря; mark.js не є великим порівняно з функціональними можливостями, які він пропонує. І ні, навряд чи щось зламається в майбутньому, оскільки mark.js тестувався, наприклад, запускаючи Chrome 30 та у всіх новіших версіях з крос-браузерними модульними тестами, і ніколи не виникало жодних проблем із майбутніми версіями.
чувак

@dude: Три пункти після першого абзацу. О, добре, не вистачає цього варіанту в демонстраційній програмі, яку я розглядав. У такому випадку це може мати певний сенс. Але все-таки я вважаю, що він занадто великий.
Стефан Штайгер

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
Мохіт, ласкаво просимо до SO. Якийсь опис коду було б непогано!
Ніппі

не повинен бути спосіб виділення тексту без створення іншого вузла?
Дейв Грегорі

@ user191433 питання полягає не лише у відборі тексту, а й у застосуванні стилів. Для цього вам потрібен вузол.
Крістоф

Нагадування / підказка про те, що JavaScript span.style.backgroundColor = "yellow";перекладає на CSS style="background-color: yellow;"- спочатку мене спокусила тонка різниця між камелею і пунктиром.
MarkHu

1
Відповідь PS Mohit за адресою stackoverflow.com/questions/7991474/… є більш спрощеним варіантом цього коду. (наприклад, опускаючи початкові та кінцеві змінні, які тут є виключно діагностичними / нефункціональними.)
MarkHu

7

Ось моє регулярне вираження чистого рішення 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.
Джон Чепмен,

Ви також можете налаштувати функцію прийому кількох слів за допомогою символу труби регулярного виразу, наприкладone|two|three
Klemen Tušar

Він не замінить текст, якщо кінець тексту має >символ. Змініть регулярний вираз, використовуючи (?!([^<]+)?<)його для роботи.
Арчі Рейес,

Змінено за запитом.
Klemen Tušar

Ідеально! Це найкраще для мене
Марко Бурромето

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 у параметрах регулярного виразу, і буде замінено лише перше входження, але це все ще є властивістю 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>");
});

Це було єдине рішення регулярного виразу, яке працювало для мене, не возившись з <img src="word">або <a href="word">.
yvesmancera

1
Золоте правило: Ніколи. Використовуйте. Регулярні. Вирази. До. Безлад. Про. С. XML.
ScottMcGready

5

Жодне з інших рішень насправді не відповідало моїм потребам, і хоча рішення Стефана Штайгера спрацювало, як я очікував, мені здалося це занадто багатослівним.

Ось моя спроба:

/**
 * 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, якщо ваші ключові слова можуть мати спеціальні символи, які потрібно було б екранувати в регулярних виразах:

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

Це добре працювало для мене, але також потрібен спосіб "зняти позначку" '
jwzumwalt

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> теги для виділення тексту. Ви можете використовувати javascript для перенесення тексту / ключового слова між цими тегами. Ось невеликий приклад того, як позначати та знімати позначку з тексту.

JSFIDDLE ДЕМО


innerHTMLнебезпечно. Це видалить події.
чувак

2
Це також не працює належним чином, оскільки, наприклад, якщо ви ввійдете в JSFIDDLE "Lorem", це лише позначить його перший екземпляр.
agm1984

1
Вам просто потрібно замінити всі входження ключового слова. ось приклад із регулярним виразом глобально jsfiddle.net/de5q704L/73
kasper Taeymans

2

Переходячи до 2019 року, веб-API тепер підтримує виділення текстів:

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

І вам добре їхати! anchorNodeє початковим вузлом виділення, focusNodeє вузлом закінчення виділення. І якщо це текстові вузли, offsetце індекс початкового та кінцевого символу у відповідних вузлах. Ось документація

У них навіть є демо в реальному часі


о це чудово. просто використовуйте його так: selection.setBaseAndExtent (бажаний вузол, 0, бажаний вузол, 1); щоб виділити єдиний потрібний вам вузол. і це працює з Гутенбергом
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


0

У нас, якщо ви також хочете, щоб це було виділено при завантаженні сторінки, є новий спосіб.

просто додайте #:~:text=Highlight%20These

спробуйте отримати доступ до цього посилання

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


-1

Використання методу surroundContents () для типу Range . Його єдиним аргументом є елемент, який оберне цей діапазон.

function styleSelected() {
  bg = document.createElement("span");
  bg.style.backgroundColor = "yellow";
  window.getSelection().getRangeAt(0).surroundContents(bg);
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.