如何使用 javascript 突出显示文本

有人可以帮助我一个 javascript 函数,可以突出显示文本在网页上。 并且要求只突出显示一次,而不是像我们在搜索时那样突出显示文本的所有匹配项。

368221 次浏览

您可以使用 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

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

我遇到了同样的问题,一堆文本通过 xmlhttp 请求传入。这个文本是 html 格式的。我需要突出每一个事件。

str='<img src="brown fox.jpg" title="The brown fox" />'
+'<p>some text containing fox.</p>'

问题是我不需要在标签中突出显示文本,例如我需要突出显示 fox:

现在我可以把它替换成:

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 属性中的 g,并且会销毁 image 标记:

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

以下是我的 regexp 纯 JavaScript 解决方案:

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

由于 HTML5,您可以使用 <mark></mark>标记来突出显示文本。可以使用 javascript 在这些标记之间包装一些文本/关键字。下面是一个如何标记和取消标记文本的小示例。

JSFIDDLE DEMO

这里提供的解决方案非常糟糕。

  1. 不能使用正则表达式,因为这样可以在 html 标记中搜索/突出显示。
  2. 您不能使用正则表达式,因为它无法正确使用 UTF * (任何带有非拉丁/英文字符的东西)。
  3. 你不能仅仅使用 innerHTML 替换,因为当字符有一个特殊的 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 转义语句,这样你就可以找到特殊的字符,这些字符是 Like 表达式。

例如:。

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

@ query 的值不是 '%completed%'而是 '%\c\o\m\p\l\e\t\e\d%'

(经过测试,可以使用 SQL-Server 和 PostgreSQL,以及其他支持 ESCAPE 的 RDBMS 系统)


修订打字稿-版本:

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

范围类型上使用 目录()方法。它的唯一参数是一个包装 Range 的元素。

function styleSelected() {
bg = document.createElement("span");
bg.style.backgroundColor = "yellow";
window.getSelection().getRangeAt(0).surroundContents(bg);
}

简单类型脚本示例

注意: 虽然我在很多事情上同意@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;
}
}
}

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

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

  • 您需要删除包含 HTML 元素的文本节点来突出显示匹配项,而不会破坏 DOM 事件并一次又一次地触发 DOM 重生(例如 innerHTML)
  • 如果要删除突出显示的元素,则必须删除 HTML 元素及其内容,并且还必须组合拆分的文本节点以进行进一步搜索。这是必要的,因为每个高亮显示器插件搜索内部的文本节点匹配,如果你的关键字将被分割成几个文本节点,他们将不会被找到。
  • 您还需要构建测试,以确保您的插件在您没有考虑到的情况下工作。我说的是跨浏览器测试!

听起来很复杂?如果你想要一些特性,比如忽略高亮显示的一些元素,变音符映射,同义词映射,在 iframe 中搜索,单词搜索等等,这就变得越来越复杂。

使用现有的插件

当使用一个现有的、实现良好的插件时,你不必担心上面提到的事情。Sitepoint 上的文章 10个 jQuery 文本高亮插件比较了流行的高亮插件。

看看 Mark Js

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

  • 分别搜索关键字而不是完整的词
  • Map 变音符(例如,如果“ just to”也应该匹配“ 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 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

谢谢

其他的解决方案都不能真正满足我的需要,尽管 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);

快进到2019年,Web API 现在有了突出显示文本的本地支持:

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

你可以走了!anchorNode是选择开始节点,focusNode是选择结束节点。而且,如果它们是文本节点,则 offset是各个节点中开始和结束字符的索引。这是 文件

他们甚至有 现场演示

如果您还希望在页面加载时突出显示它,那么有一种新的方法。

加上 #:~:text=Highlight%20These

尝试在新的选项卡中访问这个链接

https://stackoverflow.com/questions/38588721#:~:text=Highlight%20a%20text

突出显示“突出显示文本”文本。

此外,目前只支持 Chrome (感谢 GitGitBoom)。

我想分享更多关于 滚动文本片段的使用

语法: #:~:text=[prefix-,]textStart[,textEnd][,-suffix]

如果要在一个 URL 中突出显示 多个文本片段(&text=)

例子 演示链接
#:~:text=javascript&text=highlight&text=Ankit 如何使用 javascript 突出显示文本

详情请浏览: https://web.dev/text-fragments/#textstart

简单地把你的话传递到下面的 功能:

function highlight_words(word) {
const page = document.body.innerHTML;
document.body.innerHTML = page.replace(new RegExp(word, "gi"), (match) => `<mark>${match}</mark>`);
}

用法 :

highlight_words("hello")

这将突出显示页面上该单词的所有实例。

如果你在标记标签中包围任何文本,浏览器会自动将其高亮显示为醒目的黄色。 详细信息请点击这里: < a href = “ https://dev.to/comscience/寻找-text-on-a-page-with-just-javascript-17b3”rel = “ nofollow norefrer”> https://dev.to/comscience/highlight-searched-text-on-a-page-with-just-javascript-17b3

 <h1>
Searching and Marking
</h1>


<input type="text" id="search"/>
<button onClick="search(id)" id="button">
Highlight
</button>


<p id="text">
What exactly is this Worker thread module, and why do we need it? In this post, we will talk about the historical reasons concurrency is implemented in JavaScript and Node.js, the problems we might find, current solutions, and the future of parallel processing with Worker threads.


Living in a single-threaded world
JavaScript was conceived as a single-threaded programming language that ran in a browser. Being single-threaded means that only one set of instructions is executed at any time in the same process (the browser, in this case, or just the current tab in modern browsers).


This made things easier for implementation and for developers using the language. JavaScript was initially a language only useful for adding some interaction to webpages, form validations, and so on — nothing that required the complexity of multithreading.
</p>

现在 JS 代码看起来像这样

function search(e) {
let searched = document.getElementById("search").value.trim();
if (searched !== "") {
let text = document.getElementById("text").innerHTML;
let re = new RegExp(searched,"g"); // search for all instances
let newText = text.replace(re, `<mark>${searched}</mark>`);
document.getElementById("text").innerHTML = newText;
}
}

关于控制论: 谢谢,下面的函数起作用了。但是有一个问题,因为它也替换了标签中的单词。如果要突出显示的单词是 target,以下是一个例子:

<a <mark>target</mark>="_blank" href="Its not OK to highlight <mark>target</mark> here">Its OK to highlight the words <mark>target</mark>s here</a>

我们如何防止这种情况发生?

function highlight_words(word) {
const page = document.body.innerHTML;
document.body.innerHTML = page.replace(new RegExp(word, "gi"), (match) => `<mark>${match}</mark>`);
}

我认为这个代码更好,因为突出所有重复的字符

function highlight(text) {
var inputText = document.getElementById("inputText");
var innerHTML = inputText.innerHTML;
var index = innerHTML.indexOf(text);
if (index >= 0) {
inputText.innerHTML=innerHTML.split(text).join('<span class="highlight">'+text+'</span>');
}
}
.highlight {
background-color: yellow;
}
<button onclick="highlight('fox')">Highlight</button>


<div id="inputText">
The fox went over the fence fox fox fox wen fox
</div>