[javascript] 자바 스크립트를 사용하여 텍스트를 강조 표시하는 방법

누군가가 웹 페이지의 텍스트를 강조 할 수있는 자바 스크립트 기능으로 나를 도울 수 있습니까? 그리고 요구 사항은-한 번만 강조 표시하는 것입니다. 검색의 경우처럼 텍스트의 모든 항목을 강조 표시하는 것과는 다릅니다.



답변

jquery 강조 효과를 사용할 수 있습니다 .

그러나 원시 자바 스크립트 코드에 관심이 있으시면 제가 얻은 것을 살펴보십시오. HTML에 붙여 넣기를 복사하고 파일을 열고 “강조 표시”를 클릭하십시오. 그러면 “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을 전체 웹 페이지로 대체

전체 웹 페이지의 HTML을 대체하려면 innerHTML문서 본문을 참조해야합니다 .

document.body.innerHTML


답변

여기에 제공된 솔루션은 매우 나쁩니다.

  1. 정규식을 사용할 수 없습니다. 그렇게하면 html 태그에서 검색 / 강조 표시하기 때문입니다.
  2. 정규식은 UTF * (비 라틴 / 영어 문자가있는 모든 것)에서 제대로 작동하지 않기 때문에 사용할 수 없습니다.
  3. innerHTML.replace를 수행 할 수 없습니다. 문자에 특수 HTML 표기법이있을 때 작동하지 않기 때문입니다 (예 : &amp;for & &lt;, &gt;for>, &auml;for >, for ä, &ouml;for ö &uuml;for ü &szlig;for ß 등).

해야 할 일 :

루프가 HTML 문서를 통해 얻을 모든 텍스트 노드를 찾을 수 textContent와 하이라이트 텍스트의 위치를 얻을 수 indexOf(선택 사양으로 toLowerCase는 대소 문자를 구분해야하는 경우)하기 전에, APPEND 모든 것을 indexof같은 textNode, APPEND 하이라이트 범위와 일치하는 텍스트, 나머지 텍스트 노드에 대해 반복합니다 (강조 문자열은 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, '%')[당신이, 당신은 전체 텍스트 검색 또는 루씬을 사용해야하지 말아야한다, 당신은, 그런 식으로 모든 문자를 이스케이프 \ 및 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


답변

직접 만든 하이라이트 기능을 사용하는 것이 나쁜 생각 인 이유

처음부터 자신 만의 하이라이트 기능을 구축하는 것이 좋지 않은 이유는 다른 사람들이 이미 해결 한 문제에 부딪 힐 것이기 때문입니다. 과제 :

  • DOM 이벤트를 파괴하지 않고 DOM 재생성을 반복하지 않고 일치를 강조하기 위해 HTML 요소가있는 텍스트 노드를 제거해야합니다 (예 :이 경우 innerHTML).
  • 강조 표시된 요소를 제거하려면 콘텐츠와 함께 HTML 요소를 제거하고 추가 검색을 위해 분할 된 텍스트 노드를 결합해야합니다. 이는 모든 하이 라이터 플러그인이 텍스트 노드 내부에서 일치 항목을 검색하고 키워드가 여러 텍스트 노드로 분할되면 찾을 수 없기 때문에 필요합니다.
  • 또한 생각하지 않은 상황에서 플러그인이 작동하는지 확인하기 위해 테스트를 빌드해야합니다. 그리고 저는 브라우저 간 테스트에 대해 이야기하고 있습니다!

복잡하게 들리나요? 강조 표시, 분음 부호 매핑, 동의어 매핑, iframe 내부 검색, 분리 된 단어 검색 등에서 일부 요소를 무시하는 것과 같은 일부 기능을 원하는 경우 이는 점점 더 복잡해집니다.

기존 플러그인 사용

잘 구현 된 기존 플러그인을 사용할 때 위에 언급 된 것에 대해 걱정할 필요가 없습니다. Sitepoint 의 기사 10 jQuery 텍스트 하이 라이터 플러그인은 인기있는 하이 라이터 플러그인 을 비교합니다.

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에서 무료로 개발 된 오픈 소스입니다 ( 프로젝트 참조 ).


답변

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


답변

다음은 내 정규식 순수 JavaScript 솔루션입니다.

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


답변

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

질문에 대답하려면 regexp 옵션에서 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>");
});


답변

다른 솔루션 중 어느 것도 내 요구 사항에 맞지 않으며 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 와 같은 것을 사용하는 것이 좋습니다 .

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