整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

HTML高亮关键字的完美解决方案

近项目做晕头,一个接一个,其中遇到这样的一个功能,在网页中高亮关键字的实现方法,下面小编把实现代码及解决方案分享给大家,感兴趣的的朋友跟随小编一起看看吧

最近做项目遇到这样的一个功能:在网页中高亮关键字。

本以为一个 innerHTML replace 就能实现的简单操作,却遇到了许多的问题。本文就记录这些问题和最终的完美解决办法, 希望能对有同样遭遇的小伙伴有所帮助。只对结果感兴趣的,忽略过程,直接跳过看结果吧~

常用做法:正则替换

思路:要想高亮元素,那么需要将关键字提取出来用标签包裹,然后对标签进行样式调整。使用 innerHTML,或 outHTML, 而不能使用 innerText,outText。

const regex = new RegExp(keyword,"g")
element.innerHTML = element.innerHTML.replace(regex,"<b class="a">"+keyword+"</b>")
element.classList.add("highlight")

这样做存在的隐患有如下:

()\
div
<div id="parent">
 <div class="test">test</div>
 </div>

关键字父节点 element 通过 class 来进行背景染色处理,对原始DOM有一定程度污染,可能对 element 再次定位造成影响。(作为插件希望尽可能少改变原始DOM)

正则优化一:仅处理位于标签内的元素

var formatKeyword = text.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&') // 转义处理keyword包含的特殊字符,如 /.
var finder = new RegExp(">.*?"++".*?<") // 提取位于标签内的文本,避免误操作 class、id 等
 
element.innerHTML = element.innerHTML.replace(finder,function(matched){
 return matched.replace(text,"<br>"+text+</br>)
})// 对提取的标签内文本进行关键字替换

以能解决大多数问题,但依旧存在的问题是,只要标签属性存在类似 < 符号,将会打破匹配规则导致正则提取内容错误, HTML5 dataset 可以自定义任意内容,故这些特殊字符是无法避免的。

<div dataset="p>d">替换</div>

正则优化二:清除可能影响的标签

<div id="keyword">keyword</div>
 =》将闭合标签用变量替换
 [replaced1]keyword[replaced2]//闭合标签内 id="keyword" 不会被处理
 =》
 [replaced1]<b>keyword</b>[replaced2]
 =》将暂存变量 replaced 替换为原先标签
 <div id="keyword"><b>keyword</b></div>
  • 这种思路及源码从这里来, 但存在问题是:
  • 如果 [replaced1] 包含 keyword, 那么替换时将发生异常

最重要的,当标签值中包含 <> 符号时,此方法也不能正确的提取标签

总之在经过了N多尝试之后,通过正则都没能有效的处理各种情况。然后换了个思路,不通过字符串的方式,通过节点处理。element.childNodes 可以最有效的清理标签内的干扰信息。

[完美解决方案]通过 DOM 节点处理

<div id="parent">
 keyword 1
 <span id="child">
 keyword 2
 </span>
 </div>

通过 parent.childNodes 得到所有子节点。child 节点可以通过 innerText.replce(keyword,result) 的方式替换得到想要的高亮效果,如下: <span id="child"><b>keyword</b> 2</span> (递归处理:当child节点不含子节点时进行replace操作)。

但是 keyword 1 是属于文本节点,只能修改文本内容,无法增加 HTML,更无法单独控制其样式。而文本节点也不能转换为普通节点,这也是最苦恼的事情。

最后~,本文的重点来了,因为这个功能,让我第一次认真接触到了文本节点这个东西。从这里发现了Text,使用切割文本节点并替换的方式实现高亮。

源码以及还原高亮见源码

const reg = new RegExp(keyword.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'))
highlight = function (node,reg){
 if (node.nodeType == 3) { //只处理文本节点
 const match = node.data.match(new RegExp(reg));
 if (match) {
 const highlightEl = document.createElement("b");
 highlightEl.dataset.highlight="y"
 const wordNode = node.splitText(match.index)
 wordNode.splitText(match[0].length); // 切割成前 关键词 后三个Text 节点
 const wordNew = document.createTextNode(wordNode.data);
 highlightEl.appendChild(wordNew);//highlight 节点构建成功
 wordNode.parentNode.replaceChild(highlightEl, wordNode);// 替换该文本节点
 }
 } else if (node.nodeType == 1 && node.dataset.highlight!="y"
 ) {
 for (var i = 0; i < node.childNodes.length; i++) {
 highlight(node.childNodes[i], reg);
 i++
 }
 } 
}

总结

以上所述是小编给大家介绍的HTML高亮关键字的完美解决方案,希望对大家有所帮助,

学习从来不是一个人的事情,要有个相互监督的伙伴,想要学习或交流前端问题的小伙伴可以私信“学习”小明加群获取2019web前端最新入门资料,一起学习,一起成长!

头条创作挑战赛#

只需要按照3个步骤,就可以使用ChatGPT代码解释器从网站提取数据。

在之前的文章中,我向您展示了如何使用ChatGPT和ChatGPT的插件“Scraper”来抓取网站。这些都是不错的方法,但也存在一些限制和缺点。

但是,ChatGPT代码解释器将网络抓取提升到了一个不同的层次。

在本文中,我们将探讨如何使用ChatGPT代码解释器自动化网络抓取。

步骤1:访问目标网站并将其保存为HTML

要使用ChatGPT代码解释器抓取网站,我们需要上传目标网站的HTML文件。

为此,访问目标网站并按CTRL+S,并将文件保存为HTML。

假设我想提取Amazon上的电视价格。我只需要访问Amazon并输入“TVs”。

然后我按CTRL+S,下面的窗口就会弹出。确保你将文件保存为HTML。

这个HTML文件包含了Amazon网站的所有数据。现在我们需要使用代码解释器上传该文件。

步骤2:上传HTML文件并使用此提示

现在我们需要转到ChatGPT,选择GPT-4,然后点击“Code Interpreter”。

代码解释器只对ChatGPT Plus订阅者开放。如果你还没有启用它,可以按照此指南中的步骤激活它。

现在你应该在聊天栏中看到一个加号按钮。点击它上传HTML文件。

然后我们需要使用以下提示来提取数据并导出到CSV文件:

从HTML文件中提取产品名称和价格,将数据放在表格中,并导出到CSV文件。

步骤3:复制数据所在的HTML元素

前面的提示有时就足够了,但为了使提示更为健壮,我们需要指定我们的数据所在的HTML元素。

为此,访问目标网站,找到你想提取的数据(在这种情况下,是产品的名称和价格),然后右键点击它并选择“检查”。

开发者工具将弹出,并高亮显示包含你希望提取的数据的HTML元素,复制蓝色高亮的元素。

这是我复制的元素:

<span class="a-size-medium a-color-base a-text-normal">Introducing Amazon Fire TV 40" 2-Series 1080p HD smart TV, stream live TV without cable</span>

如果你重复这个步骤对价格进行操作,你会得到如下面的元素:

<span class="a-price-whole">189<span class="a-price-decimal">.</span></span>

现在,如果你有过抓取网站的经验,你可能知道总会有数据缺失,所以我们必须在我们的提示中处理这种情况。

这是我们将要使用的最后的提示:

从HTML文件中提取产品名称和价格,将数据放在表格中,并导出到CSV文件。

这是一个产品的元素:

<span class="a-size-medium a-color-base a-text-normal">Introducing Amazon Fire TV 40" 2-Series 1080p HD smart TV, stream live TV without cable</span>

这是价格的元素:

<span class="a-price-whole">189<span class="a-price-decimal">.</span></span>

如果产品的价格缺失,请将该价格作为空数据。

在给ChatGPT发出提示后,我得到了我请求的表格的预览和下载CSV文件的链接。

注意:仔细检查你的CSV文件。有时,ChatGPT会复制其他行的数据,而不是将空数据作为一个空单元格。如果是这种情况,你需要使用下面的提示:

有些产品(如LG 55-Inch Class UQ7570 Series 4K Smart TV, AI-Powered 4K, Cloud Gaming (55UQ7570PUJ, 2022)没有价格,您错误地将其他产品的价格指定给了它。您能解决这个问题并告诉我哪些产品没有价格吗?

那还不是全部!你可以通过提供新的HTML文件来抓取第2、3、4...页

到目前为止,我们抓取了Amazon的第一页,但还有其他页有更多的数据。要抓取所有页面,我们需要转到我们想要的页面,并将其保存为HTML。

然后我们上传这个新的HTML文件,并使用下面的提示。

这是前面网站的第[n]页。使用HTML文件提取数据,按照我之前描述的相同步骤。

这是我下载的CSV文件的一部分。

附加内容:使用略有不同的方法抓取Glassdoor

假设我们想抓取Glassdoor上的职位发布信息。我们将遵循相同的步骤。

首先,我们会访问Glassdoor,搜索我们想要的职位,然后按CTRL+S将页面保存为HTML。然后我们检查想要提取的数据,但现在我们不是复制蓝色高亮的整个元素,而是选择一个属性。在这种情况下,我会选择id。

我检查的元素的id是“job-title-1008760392125”。这可以简化为“job-title”。如果我们重复对岗位的雇主、地点和薪资的处理,我们会构建下面的提示:

从HTML文件中,查找下列ids的元素并提取其数据

job-employer

job-title

job-location

job-salary

将数据放在表格中,并导出到CSV文件。如果有缺失的数据,将其作为空数据。

现在你可能会收到下面的消息。

这是因为我们通过移除数字来缩短“job-title-1008760392125”,所以id的名称并不完整。因此我们必须告诉ChatGPT使用正则表达式匹配id名称的部分:

这些是ids的一部分,使用正则表达式匹配id名称的部分

然后我们得到了我们的数据!

utline与border的异同:

边框的设定在web设计中使用率非常的高,border:1px solid #00f;属于标准的边线写法,也可以实现单方向边线border-left:1px solid red;

1px red solid边线

(单边线)左边线

在CSS标准盒模型中,边线border是计算在容器总宽度和高度之中的,

总宽高是102*102

浏览器中呈现的总宽度和总高度102*102

但随着web布局要求越来越高,自适应布局应用逐渐广泛,横向排布四个div 各占据四分之一的宽度,但如果某一个要有边线border修饰,因为border是占据宽度的,最终会导致最后一个元素掉下来,因为实际宽度大于了总宽度。

各占据四分之一的宽度


第四个元素掉了下来

outline可以实现和border相同的效果,标准语法也基本相同(outline:1px solid red),也支持outline-styleoutline-widthoutline-color等分散属性。

但是outline不占位,不会增加元素的宽高。

outline使用,总宽高不变还是100*100

outline标准写法

outline缺点:

不支持圆角 outline-radius:3px;

②不支持单方向outline

不支持部分属性

outline在文本框中的作用:

默认的文本框input[type="text"]获取光标时会有边线高亮。

文本框高亮获取光标(新版本之前是蓝色边线)

实际上高亮的部分为outline在起作用

.text:focus{outline: 3px solid #00f;}

使用outline:none,可以去除默认文本框获取光标时出现的边线

outline:none

.outline{
		/*标准写法*/
		outline:1px solid red;
		/*单方向边线*/
		outline-left:4px solid #000; 
	}
	.outline:focus{
		/*去除默认边线*/
		outline: none;
	}

outline作为一个特殊的属性存在,在特殊的场景中会产生很棒的效果,灵活使用才能发挥出最大作用。