web-dev-qa-db-ja.com

JavaScriptを使用してテキストを強調表示する方法

誰かがウェブページ上のテキストを強調表示することができるjavascript関数で私を助けることができます。そして、要件は-検索の場合のようにテキストのすべての出現を強調表示するのではなく、一度だけ強調表示することです。

78
Ankit

Jquery ハイライト効果 を使用できます。

しかし、生のjavascriptコードに興味がある場合は、HTMLに貼り付けてコピーし、ファイルを開いて「ハイライト」をクリックしてください。これにより、Wordの「fox」が強調表示されます。パフォーマンスに関しては、これは小さなテキストと単一の繰り返し(あなたが指定したように)に役立つと思います

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をWebページ全体に置き換える

webページ全体のHTMLを置き換えるには、ドキュメントの本文のinnerHTMLを参照する必要があります。

document.body.innerHTML

80
guy mograbi

ここで提供されるソリューションは非常に悪いです。

  1. 正規表現を使用することはできません。そのようにすると、htmlタグで検索/強調表示されるためです。
  2. 正規表現はUTF *(非ラテン語/英語の文字を含むもの)では正常に動作しないため、使用できません。
  3. InnerHTML.replaceを実行することはできません。これは、文字に特別なHTML表記がある場合は機能しないためです。 &amp; for&、&lt; for <、&gt; for>、&auml; forä、&ouml; forö&uuml; forü&szlig;はßなど.

あなたがする必要があること:

HTMLドキュメントをループし、すべてのテキストノードを見つけ、textContentを取得し、indexOfでハイライトテキストの位置を取得します(大文字と小文字を区別しない場合はオプションのtoLowerCaseを使用) 、indexofの前にあるすべてをtextNodeとして追加し、一致したTextにハイライトスパンを追加し、残りの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エスケープステートメントを追加できます。 LIKE式。

例えば.

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

@queryの値は'%completed%'ではなく'%\c\o\m\p\l\e\t\e\d%'です

(テスト済み、SQL-Server、PostgreSQL、およびESCAPEをサポートする他のすべてのRDBMSシステムで動作します)


改訂されたTypeScriptバージョン:

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
37
Stefan Steiger

自作の強調表示機能を使用するのが悪い考えである理由

独自の強調表示機能をゼロから作成し始めるのがおそらく悪い考えである理由は、他の人がすでに解決した問題に必ず遭遇するからです。課題:

  • DOMイベントを破棄し、DOM再生成を何度もトリガーすることなく、一致を強調表示するには、HTML要素を持つテキストノードを削除する必要があります(例:innerHTML
  • 強調表示された要素を削除する場合は、HTML要素とそのコンテンツを削除し、さらに検索するために分割されたテキストノードを結合する必要があります。すべての蛍光ペンプラグインはテキストノード内で一致を検索し、キーワードが複数のテキストノードに分割される場合は検出されないため、これが必要です。
  • また、考えていない状況でプラグインが機能することを確認するためのテストを作成する必要があります。そして、私はクロスブラウザテストについて話しています!

複雑に聞こえますか?強調表示、発音区別符号マッピング、同義語マッピング、iframe内の検索、分離されたWord検索などの一部の要素を無視するなどの機能が必要な場合、これはますます複雑になります。

既存のプラグインを使用する

既存の適切に実装されたプラグインを使用する場合、上記の名前のものについて心配する必要はありません。 Sitepointの記事10 jQueryテキストハイライタープラグインは、人気のあるハイライトプラグインを比較しています。

mark.js をご覧ください

mark.js は、純粋なJavaScriptで記述されたプラグインですが、jQueryプラグインとしても利用可能です。以下のオプションを備えた他のプラグインよりも多くの機会を提供するために開発されました。

  • 完全な用語ではなく、キーワードを個別に検索する
  • マップの発音記号(たとえば、「justo」も「justò」に一致する必要がある場合)
  • カスタム要素内の一致を無視する
  • カスタムの強調表示要素を使用する
  • カスタム強調表示クラスを使用する
  • カスタムシノニムをマップする
  • iframe内でも検索
  • 条件が見つかりません

DEMO

または、 this fiddle を確認できます。

使用例

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

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

これは無料で、GitHubで開発されたオープンソースです( プロジェクトリファレンス )。

29
dude
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);
}
9
Mohit kumar

同じ問題があります。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>");
});
4
HMR

単純なTypeScriptの例

注:私は多くの点で@Stefanに同意していますが、必要なのはsimpleマッチの強調表示だけです:

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;
        }
    }
}
4
Slavo Vojacek

これが私の正規表現の純粋なJavaScriptソリューションです。

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

他のソリューションはどれも私のニーズに本当に合っていません。StefanSteigerのソリューションは期待どおりに機能しましたが、少し冗長すぎることがわかりました。

私の試みは次のとおりです。

/**
 * 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);
2
elclanrs

HTML5以降では、<mark></mark>タグを使用してテキストを強調表示できます。 javascriptを使用して、これらのタグの間にテキスト/キーワードをラップできます。テキストをマークおよびマーク解除する方法の小さな例を次に示します。

JSFIDDLEデモ

1
kasper Taeymans

私も this postで学んだことを試すことができると思っていました。

私が使用した:

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
Henrique Donati

highlight プラグインが最適であることがわかりました。これにより、コンテンツの一部を強調表示できます:

$( 'li')。highlight( 'bla');

1
Igor G.