近项目做晕头,一个接一个,其中遇到这样的一个功能,在网页中高亮关键字的实现方法,下面小编把实现代码及解决方案分享给大家,感兴趣的的朋友跟随小编一起看看吧
最近做项目遇到这样的一个功能:在网页中高亮关键字。
本以为一个 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>
最重要的,当标签值中包含 <> 符号时,此方法也不能正确的提取标签
总之在经过了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代码解释器自动化网络抓取。
要使用ChatGPT代码解释器抓取网站,我们需要上传目标网站的HTML文件。
为此,访问目标网站并按CTRL+S,并将文件保存为HTML。
假设我想提取Amazon上的电视价格。我只需要访问Amazon并输入“TVs”。
然后我按CTRL+S,下面的窗口就会弹出。确保你将文件保存为HTML。
这个HTML文件包含了Amazon网站的所有数据。现在我们需要使用代码解释器上传该文件。
现在我们需要转到ChatGPT,选择GPT-4,然后点击“Code Interpreter”。
代码解释器只对ChatGPT Plus订阅者开放。如果你还没有启用它,可以按照此指南中的步骤激活它。
现在你应该在聊天栏中看到一个加号按钮。点击它上传HTML文件。
然后我们需要使用以下提示来提取数据并导出到CSV文件:
从HTML文件中提取产品名称和价格,将数据放在表格中,并导出到CSV文件。
前面的提示有时就足够了,但为了使提示更为健壮,我们需要指定我们的数据所在的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名称的部分
然后我们得到了我们的数据!
边框的设定在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-style,outline-width,outline-color等分散属性。
但是outline不占位,不会增加元素的宽高。
outline使用,总宽高不变还是100*100
outline标准写法
outline缺点:
①不支持圆角 outline-radius:3px;
②不支持单方向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作为一个特殊的属性存在,在特殊的场景中会产生很棒的效果,灵活使用才能发挥出最大作用。
*请认真填写需求信息,我们会在24小时内与您取得联系。