Как выделить текст с помощью javascript

98

Может ли кто-нибудь помочь мне с функцией javascript, которая может выделять текст на веб-странице. И требование - выделять только один раз, а не выделять все вхождения текста, как мы это делаем в случае поиска.

Анкит
источник
4
Если вы разместите код функции, мы сможем помочь. Если вы попросите нас создать для вас такую ​​функцию ... это будет менее вероятно. Вы должны что-то делать самостоятельно. Начни что-то делать и возвращайся, когда застрянешь.
Феликс Клинг
7
Да, я прочитал «Как спрашивать» и кое-что сделал сам, но застрял, поэтому и спросил. Я работаю на Android и плохо разбираюсь в javasript, поэтому не могу сделать это самостоятельно. Раньше я использовал другой javascript, который работал, но не без определенных ограничений. Возможно, я использовал неправильные слова, задавая этот вопрос, и мне очень жаль, но, пожалуйста, не думайте иначе.
Ankit
1
Этот плагин может вас заинтересовать: github.com/julmot/jmHighlight . Он может выделять ключевые слова отдельно или как термин, может выделять совпадение с вашим настраиваемым элементом и именем класса, а также может искать диакритические знаки. Сверху он позволяет фильтровать контекст, в котором нужно искать совпадения.
чувак
1
Оформить заказ по регулярному выражению ... stackoverflow.com/a/45519242/2792959
Я подготовил статью о том , что здесь, exhesham.com/2017/11/20/...
Hesham Ясина

Ответы:

101

Вы можете использовать эффект выделения 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регулярное выражение замены.

"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
Бакер Накви
4
Использование простой «замены» - плохая идея . Я объяснил почему здесь: stackoverflow.com/a/32758672/3894981
чувак,
2
Это не лучшая идея, потому что это попытается выделить теги / атрибуты HTML / и т. Д. Например, что произойдет в случае: <img src="fox.jpg" /> Вы получите недопустимый HTML- <img src="<span class='highlight'>fox</span>.jpg" />
код,
47

Предлагаемые здесь решения довольно плохие.

  1. Вы не можете использовать регулярное выражение, потому что таким образом вы выполняете поиск / выделение в тегах html.
  2. Вы не можете использовать регулярное выражение, потому что оно не работает должным образом с UTF * (что-либо с нелатинскими / английскими символами).
  3. Вы не можете просто выполнить innerHTML.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, '%')[чего не следует делать, вы должны использовать полнотекстовый поиск или Lucene], тогда вы можете экранировать каждый символ с помощью \ и добавить SQL-escape-оператор, таким образом вы найдете специальные символы, которые являются LIKE-выражениями.

например

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

и значение @query не '%completed%'но'%\c\o\m\p\l\e\t\e\d%'

(протестировано, работает с SQL-Server и 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 (как есть CAN быть тысячи результатов), любопытные , если этот метод будет добавить высокую латентность для ленивых нагрузки.
Погриндис
5
Извините, но ни один из ваших аргументов не соответствует действительности. 1. Вы абсолютно можете использовать RegExp, вам просто нужно искать не внутри значения HTML, а только по текстовому значению элемента. 2. Вы можете абсолютно использовать RegExp с диакритическими символами, как это реализовано в mark.js . 3. Нотации HTML будут преобразованы в фактические символы в DOM браузера, так что вы также обязательно их используете!
чувак
1
@julmot; To 1: Это означает, что вам нужно перебирать каждый элемент, что я и делаю. Если вы не заботитесь о потере форматирования, в этом случае вы можете искать в document.body.innerText, что будет довольно медленно. 3. Не в DOM, а в свойстве innerText или textContent текстового элемента. Это снова означает, что вам нужно перебирать текстовые элементы; не может быть выполнено с помощью regEx AFAIK. 2: Не знаю mark.js, но я бы избегал всего, что делает jQuery.each, потому что это чертовски медленно.
Stefan Steiger
1
@StefanSteiger 1. Затем вам следует исправить отношение решения, поскольку в нем говорится, что мы вообще не можем выполнять поиск с помощью RegExp, что неверно. 2. Он не использует jQuery.each. Что заставляет вас думать, что? 3. Это не так, по крайней мере, в Firefox. &auml;например, будет преобразован в фактический символ, даже при использовании innerHTML.
чувак
1
Привет, @StefanSteiger. На самом деле я использую ваши решения. Этот идеален. Но есть некоторая проблема, например, если у меня есть P, в котором есть два промежутка, и один промежуток имеет данные, такие как Diploma MSBTE, а второй промежуток имеет данные 2012. Теперь, если строка, которую я хочу выделить, это Diploma MSBTE 2012, вся эта строка, я проверил, что это не работает, если все, что будет сопоставлено, присутствует в одном диапазоне, тогда оно работает, но если текстовое содержимое находится в тегах diff, тогда Это не работает. Не могли бы вы рассказать что-нибудь об этом?
ganeshk 07
41

Почему использовать самодельную функцию выделения - плохая идея

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

  • Вам нужно будет удалить текстовые узлы с элементами 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
@ Рой, я принял это близко к сердцу. Хорошие новости, начиная с версии 6.0.0 mark.js отказался от зависимости jQuery и теперь позволяет использовать его как плагин jQuery.
чувак
Все верно, за исключением: 1-я точка невозможна, потому что вы не можете получить зарегистрированные обработчики событий, и даже если бы вы могли, вы не могли бы установить анонимные функции ... 2-й: mark.js также не находит текст между двумя тегами, например <span> s </span> ed не найдет sed ... 3-е: всякий раз, когда появляется браузер (включая новую версию), который вы еще не тестировали, он может сломаться. Это всегда верно, независимо от того, сколько тестов вы напишете. При размере 17 Кбайт отметки слишком велики для того, что он делает.
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
Мохит, добро пожаловать в SO. Было бы неплохо какое-нибудь описание кода!
Nippey
не должен ли быть способ выделить текст без создания другого узла?
Дэйв Грегори
@ user191433 вопрос не только в выборе текста, но и в применении стилей. Для этого вам понадобится узел.
Christophe
Напоминание / подсказка о том, что JavaScript span.style.backgroundColor = "yellow";переводится в CSS - style="background-color: yellow;"эта тонкая разница между camelCase и пунктирной нотацией сначала сбила меня с толку.
MarkHu
1
PS Ответ Мохита на 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
вертикального
Он не заменит текст, если в конце текста есть >символ. Измените регулярное выражение using, (?!([^<]+)?<)чтобы оно работало.
Арчи Рейес
Изменено по запросу.
Клемен Тушар
Отлично! Это лучшее для меня
марко бурромето
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>");
});
HMR
источник
Это было единственное решение для регулярных выражений, которое сработало для меня, не возясь с <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);
Elclanrs
источник
Это сработало для меня, но для этого также нужен способ «
снять отметку
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 06
1
Хорошо, вам просто нужно заменить все вхождения ключевого слова. вот пример глобального регулярного выражения jsfiddle.net/de5q704L/73
kasper Taeymans
2

Перенесемся в 2019 год: в веб-API теперь есть встроенная поддержка выделения текста:

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

И вам хорошо! anchorNode- начальный узел выбора, focusNode- конечный узел выбора. И, если они являются текстовыми узлами, offsetэто индекс начального и конечного символа в соответствующих узлах. Вот документация

У них даже есть живая демонстрация

JGuo
источник
о, это великолепно. просто используйте его следующим образом: 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);
}
архоскины
источник