突出显示元素中的子字符串

时间:2012-01-29 06:21:44

标签: javascript jquery html

在事件中突出显示div元素中特定子字符串的最佳方法是什么? 通过突出显示,我的意思是应用一些CSS样式,如黄色背景或其他东西。

基本上我需要一个简单的JS客户端函数:

function (element, start, end) { 
  // element is the element to manipulate.  a div or span will do
  // start and end are the start and end positions within the text of that
  // element where highlighting should be.
  // do the stuff
}

只有一个高亮显示处于活动状态。

7 个答案:

答案 0 :(得分:7)

您需要将您想要的文本包装在其自己的<span>标记中,以便您可以为该文本提供自己的样式。使用您请求的函数定义,您可以这样做:

function (element, start, end) { 
    var str = element.innerHTML;
    str = str.substr(0, start) +
        '<span class="hilite">' + 
        str.substr(start, end - start + 1) +
        '</span>' +
        str.substr(end + 1);
    element.innerHTML = str;
}

然后,您可以为类hilite定义CSS以控制该文本的样式。

.hilite {color: yellow;}

这假设start和end是要突出显示的第一个和最后一个字符的innerHTML的索引。

如果您希望能够在同一元素上重复调用它(以移动高光),您可以这样做:

function (element, start, end) {
    var item = $(element);
    var str = item.data("origHTML");
    if (!str) {
        str = item.html();
        item.data("origHTML", str);
    }
    str = str.substr(0, start) +
        '<span class="hilite">' + 
        str.substr(start, end - start + 1) +
        '</span>' +
        str.substr(end + 1);
    item.html(str);
}

答案 1 :(得分:5)

为什么使用自制高亮功能是一个坏主意

从头开始构建自己的突出显示功能可能是一个坏主意的原因是因为你肯定会遇到其他人已经解决的问题。挑战:

  • 您需要删除包含HTML元素的文本节点,以突出显示您的匹配,而不会破坏DOM事件并反复触发DOM重新生成(例如innerHTML就是这种情况)
  • 如果要删除突出显示的元素,则必须删除包含其内容的HTML元素,并且还必须组合拆分的文本节点以进行进一步搜索。这是必要的,因为每个荧光笔插件都会在文本节点内搜索匹配项,如果您的关键字将被拆分为多个文本节点,则无法找到它们。
  • 您还需要构建测试以确保您的插件能够在您没有考虑过的情况下工作。我正在谈论跨浏览器测试!

听起来很复杂?如果你想要一些功能,比如忽略突出显示,变音符号映射,同义词映射,iframe内搜索,分离词搜索等一些元素,这就变得越来越复杂。

使用现有插件

使用现有的,实施良好的插件时,您不必担心上面提到的内容。 Sitepoint上的文章 10 jQuery text highlighter plugins 比较了流行的荧光笔插件。

查看mark.js

mark.js是一个用纯JavaScript编写的插件,但也可以作为jQuery插件使用。它的开发目的是提供比其他插件更多的机会,可以选择:

  • 分别搜索关键字而不是完整的字词
  • map diacritics(例如,如果“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(project reference)上开发了开源。

答案 2 :(得分:3)

如果您没有附加事件或复杂的HTML,您只需在HTML上进行搜索和替换:

element.innerHTML = element.innerHTML.replace(/search/gi, function(match) {
    return '<span class="highlight">' + match + '</span>'
});

如果你想要更好的东西,你可以直接操作DOM而不使用innerHTML,这将保留事件并适用于更复杂的HTML:

/*
 * Takes in an array of consecutive TextNodes and returns a document fragment with `word` highlighted
 */
function highlight_text_nodes($nodes, word) {
    if (!$nodes.length) {
        return;
    }

    var text = '';

    // Concatenate the consecutive nodes to get the actual text
    for (var i = 0; i < $nodes.length; i++) {
        text += $nodes[i].textContent;
    }

    var $fragment = document.createDocumentFragment();

    while (true) {
        // Tweak this if you want to change the highlighting behavior
        var index = text.toLowerCase().indexOf(word.toLowerCase());

        if (index === -1) {
            break;
        }

        // Split the text into [before, match, after]
        var before = text.slice(0, index);
        var match = text.slice(index, index + word.length);
        text = text.slice(index + word.length);

        // Create the <mark>
        var $mark = document.createElement('mark');
        $mark.className = 'found';
        $mark.appendChild(document.createTextNode(match));

        // Append it to the fragment
        $fragment.appendChild(document.createTextNode(before));
        $fragment.appendChild($mark);
    }

    // If we have leftover text, just append it to the end
    if (text.length) {
        $fragment.appendChild(document.createTextNode(text));
    }

    // Replace the nodes with the fragment
    $nodes[0].parentNode.insertBefore($fragment, $nodes[0]);

    for (var i = 0; i < $nodes.length; i++) {
        var $node = $nodes[$nodes.length - i - 1];
        $node.parentNode.removeChild($node);
    }
}


/*
 * Highlights all instances of `word` in `$node` and its children
 */
function highlight($node, word) {
    var $children = $node.childNodes;
    var $current_run = [];

    for (var i = 0; i < $children.length; i++) {
        var $child = $children[i];

        if ($child.nodeType === Node.TEXT_NODE) {
            // Keep track of consecutive text nodes
            $current_run.push($child);
        } else {
            // If we hit a regular element, highlight what we have and start over
            highlight_text_nodes($current_run, word);
            $current_run = [];

            // Ignore text inside of our <mark>s
            if ($child.nodeType === Node.ELEMENT_NODE && $child.className !== 'found') {
                highlight($child, word);
            }
        }
    }

    // Just in case we have only text nodes as children
    if ($current_run.length) {
        highlight_text_nodes($current_run, word);
    }
}

/*
 * Removes all highlighted <mark>s from the given node
 */
function unhighlight($node) {
    var $marks = [].slice.call($node.querySelectorAll('mark.found'));

    for (var i = 0; i < $marks.length; i++) {
        var $mark = $marks[i];

        // Replace each <mark> with just a text node of its contents
        $mark.parentNode.replaceChild(document.createTextNode($mark.childNodes[0].textContent), $mark);
    }
}

演示:https://jsfiddle.net/wLkbbo5m/4/

如果您想要更多功能,只需使用库(如mark.js)。重新发明整个轮没有意义。

答案 3 :(得分:1)

您可以使用mark.js,它提供了一个简单而强大的插件:

$('p').mark('sit');
mark {
  background: orange;
  color: black;
}
<script src="https://code.jquery.com/jquery-latest.min.js"></script>
<script src="https://cdn.jsdelivr.net/mark.js/8.6.0/jquery.mark.min.js"></script>

<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</p>

答案 4 :(得分:0)

在此处尝试Highlighter.js库: https://gist.github.com/wliwanag/03d95916c7ba5d17e226

它支持:    突出显示子元素中的文本。    使用文字中的单词作为搜索键(例如,下面的示例将突出显示&#34;简单地&#34;,&#34; 1500&#34;等等。)

使用:

var searchInput = "simply 1500 bee y";
var textSource = $("#text-container").html();
var htmlSource = $("#html-container").html();

$("#text-result").html(highlightHtml(textSource, searchInput));
$("#html-result").html(highlightHtml(htmlSource, searchInput));

Fiddle

答案 5 :(得分:0)

在React项目中

我担心其他库的DOM操作如何在React项目中工作,所以我使用react-hightlight-words解决了这个问题。

有关配置,请参见the table of props

开箱即用,它使用搜索词而不是索引范围,但是可以解决与该问题相同的问题,除非将其锁定到索引。但是,如果是 的情况,则可以为findChunks属性提供一个功能,以任意检测要突出的部分。 (请参阅here,如何定义默认的findChunks。)

来自the description on GitHub的简单示例。

import React from "react";
import ReactDOM from "react-dom";
import Highlighter from "react-highlight-words";

ReactDOM.render(
    <Highlighter
        highlightClassName="YourHighlightClass"
        searchWords={["and", "or", "the"]}
        autoEscape={true}
        textToHighlight="The dog is chasing the cat. Or perhaps they're just playing?"
    />,
    document.getElementById("root")
);

安装npm i --save react-highlight-words

答案 6 :(得分:-1)

如果您只希望替换开始/结束之间的特定文本(而不是每次出现),它将看起来像这样:

function(element, start, end) {
    textToHilight = element.innerHTML.substr(start, end);
    element.innerHTML = element.innerHTML.substring(0, start) + "<span class='hilight'>" + textToHilight + "</span>" + element.innerHTML.substring(end);
}