近項(xiàng)目做暈頭,一個(gè)接一個(gè),其中遇到這樣的一個(gè)功能,在網(wǎng)頁(yè)中高亮關(guān)鍵字的實(shí)現(xiàn)方法,下面小編把實(shí)現(xiàn)代碼及解決方案分享給大家,感興趣的的朋友跟隨小編一起看看吧
最近做項(xiàng)目遇到這樣的一個(gè)功能:在網(wǎng)頁(yè)中高亮關(guān)鍵字。
本以為一個(gè) innerHTML replace 就能實(shí)現(xiàn)的簡(jiǎn)單操作,卻遇到了許多的問(wèn)題。本文就記錄這些問(wèn)題和最終的完美解決辦法, 希望能對(duì)有同樣遭遇的小伙伴有所幫助。只對(duì)結(jié)果感興趣的,忽略過(guò)程,直接跳過(guò)看結(jié)果吧~
常用做法:正則替換
思路:要想高亮元素,那么需要將關(guān)鍵字提取出來(lái)用標(biāo)簽包裹,然后對(duì)標(biāo)簽進(jìn)行樣式調(diào)整。使用 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>
關(guān)鍵字父節(jié)點(diǎn) element 通過(guò) class 來(lái)進(jìn)行背景染色處理,對(duì)原始DOM有一定程度污染,可能對(duì) element 再次定位造成影響。(作為插件希望盡可能少改變?cè)糄OM)
正則優(yōu)化一:僅處理位于標(biāo)簽內(nèi)的元素
var formatKeyword = text.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&') // 轉(zhuǎn)義處理keyword包含的特殊字符,如 /. var finder = new RegExp(">.*?"++".*?<") // 提取位于標(biāo)簽內(nèi)的文本,避免誤操作 class、id 等 element.innerHTML = element.innerHTML.replace(finder,function(matched){ return matched.replace(text,"<br>"+text+</br>) })// 對(duì)提取的標(biāo)簽內(nèi)文本進(jìn)行關(guān)鍵字替換
以能解決大多數(shù)問(wèn)題,但依舊存在的問(wèn)題是,只要標(biāo)簽屬性存在類(lèi)似 < 符號(hào),將會(huì)打破匹配規(guī)則導(dǎo)致正則提取內(nèi)容錯(cuò)誤, HTML5 dataset 可以自定義任意內(nèi)容,故這些特殊字符是無(wú)法避免的。
<div dataset="p>d">替換</div>
正則優(yōu)化二:清除可能影響的標(biāo)簽
<div id="keyword">keyword</div> =》將閉合標(biāo)簽用變量替換 [replaced1]keyword[replaced2]//閉合標(biāo)簽內(nèi) id="keyword" 不會(huì)被處理 =》 [replaced1]<b>keyword</b>[replaced2] =》將暫存變量 replaced 替換為原先標(biāo)簽 <div id="keyword"><b>keyword</b></div>
最重要的,當(dāng)標(biāo)簽值中包含 <> 符號(hào)時(shí),此方法也不能正確的提取標(biāo)簽
總之在經(jīng)過(guò)了N多嘗試之后,通過(guò)正則都沒(méi)能有效的處理各種情況。然后換了個(gè)思路,不通過(guò)字符串的方式,通過(guò)節(jié)點(diǎn)處理。element.childNodes 可以最有效的清理標(biāo)簽內(nèi)的干擾信息。
[完美解決方案]通過(guò) DOM 節(jié)點(diǎn)處理
<div id="parent"> keyword 1 <span id="child"> keyword 2 </span> </div>
通過(guò) parent.childNodes 得到所有子節(jié)點(diǎn)。child 節(jié)點(diǎn)可以通過(guò) innerText.replce(keyword,result) 的方式替換得到想要的高亮效果,如下: <span id="child"><b>keyword</b> 2</span> (遞歸處理:當(dāng)child節(jié)點(diǎn)不含子節(jié)點(diǎn)時(shí)進(jìn)行replace操作)。
但是 keyword 1 是屬于文本節(jié)點(diǎn),只能修改文本內(nèi)容,無(wú)法增加 HTML,更無(wú)法單獨(dú)控制其樣式。而文本節(jié)點(diǎn)也不能轉(zhuǎn)換為普通節(jié)點(diǎn),這也是最苦惱的事情。
最后~,本文的重點(diǎn)來(lái)了,因?yàn)檫@個(gè)功能,讓我第一次認(rèn)真接觸到了文本節(jié)點(diǎn)這個(gè)東西。從這里發(fā)現(xiàn)了Text,使用切割文本節(jié)點(diǎn)并替換的方式實(shí)現(xiàn)高亮。
源碼以及還原高亮見(jiàn)源碼
const reg = new RegExp(keyword.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&')) highlight = function (node,reg){ if (node.nodeType == 3) { //只處理文本節(jié)點(diǎn) 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); // 切割成前 關(guān)鍵詞 后三個(gè)Text 節(jié)點(diǎn) const wordNew = document.createTextNode(wordNode.data); highlightEl.appendChild(wordNew);//highlight 節(jié)點(diǎn)構(gòu)建成功 wordNode.parentNode.replaceChild(highlightEl, wordNode);// 替換該文本節(jié)點(diǎn) } } else if (node.nodeType == 1 && node.dataset.highlight!="y" ) { for (var i = 0; i < node.childNodes.length; i++) { highlight(node.childNodes[i], reg); i++ } } }
總結(jié)
以上所述是小編給大家介紹的HTML高亮關(guān)鍵字的完美解決方案,希望對(duì)大家有所幫助,
學(xué)習(xí)從來(lái)不是一個(gè)人的事情,要有個(gè)相互監(jiān)督的伙伴,想要學(xué)習(xí)或交流前端問(wèn)題的小伙伴可以私信“學(xué)習(xí)”小明加群獲取2019web前端最新入門(mén)資料,一起學(xué)習(xí),一起成長(zhǎng)!
辛萬(wàn)苦寫(xiě)了篇技術(shù)分享,貼了一堆代碼,興高采烈地發(fā)到了自己的博客網(wǎng)站上。結(jié)果卻發(fā)現(xiàn)代碼全是白底黑字,字體還難看得很,你瞬間就沒(méi)了興致。能不能讓網(wǎng)頁(yè)也能像 IDE 那樣,做帶語(yǔ)法高亮的炫酷顯示呢?來(lái)看一看 Highlight.js 吧,看這個(gè)語(yǔ)法高亮庫(kù)如何點(diǎn)亮你的代碼。
Highlight.js
Highlight.js,是在 Github 上由 highlight.js 組織開(kāi)源的前端代碼語(yǔ)法高亮庫(kù),代碼倉(cāng)庫(kù)在 https://github.com/highlightjs/highlight.js,目前版本為 10.1.0。其不依賴(lài)于任何框架,自帶對(duì)于大量編程語(yǔ)言和標(biāo)記語(yǔ)言的語(yǔ)法高亮規(guī)則,和主流的高亮色彩方案,且可以自由擴(kuò)展。其支持自動(dòng)語(yǔ)言檢測(cè),使用極為方便,是在網(wǎng)頁(yè)上進(jìn)行語(yǔ)法高亮的不二之選。
highlight.js語(yǔ)法高亮庫(kù)
Highlight.js 的 CSS 文件的選擇決定高亮配色方案,默認(rèn)為 Default,另外還有如 Monokai Sublime、Ocean、Solarized Dark、Tomorrow 等經(jīng)典的主流配色方案。
而 JS 文件的選擇決定可以支持的語(yǔ)言。主要的 highlight.min.js 包含了一些主流的語(yǔ)言,包括 C++、XML、Markdown、Java 等。如果需要一些其他的語(yǔ)言,則要另外引用該語(yǔ)言對(duì)應(yīng)JS文件。
Highlight.js 在瀏覽器中可以簡(jiǎn)單的引用 CDN 來(lái)使用:
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.1.0/styles/default.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.1.0/highlight.min.js"></script>
Highlight.js 使用十分簡(jiǎn)單,在引用了 CSS 和 JS 后,執(zhí)行
hljs.initHighlightingOnLoad();
Highlight.js就會(huì)自動(dòng)查找網(wǎng)頁(yè)中以標(biāo)簽 pre 和 code 所包裹的代碼
<pre><code>...</code></pre>
并自動(dòng)檢測(cè)代碼語(yǔ)言,進(jìn)行高亮渲染。我們也可以為 code 標(biāo)簽添加語(yǔ)言名稱(chēng)的 class,來(lái)顯式地標(biāo)明代碼語(yǔ)言。我們可以看一個(gè)使用示例,注意實(shí)際代碼中尖括號(hào)等 HTML 轉(zhuǎn)義字符需要進(jìn)行轉(zhuǎn)義處理:
<html>
<head>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.1.0/styles/monokai-sublime.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.1.0/highlight.min.js"></script>
</head>
<body>
<pre><code class="cpp">#include <iostream>
int main(int argc, char *argv[]) {
/* An annoying "Hello World" example */
for (auto i = 0; i < 0xFFFF; i++)
cout << "Hello, World!" << endl;
char c = '\n';
unordered_map <string, vector<string> > m;
m["key"] = "\\\\"; // this is an error
return -2e3 + 12l;
}
</code></pre>
<script>
hljs.initHighlightingOnLoad();
</script>
</body>
</html>
該網(wǎng)頁(yè)對(duì)于 C++ 語(yǔ)言片段使用了 Monokai Sublime 主題進(jìn)行了語(yǔ)法高亮渲染:
使用Highlight.js渲染C++代碼
可以看到,包括關(guān)鍵字、注釋和字面值等都有了不同顏色的渲染,輸出十分美觀(guān)。以下則是使 Dracula 主題對(duì) Javascript 代碼渲染的例子:
使用Highlight.js渲染Javascript代碼
我們也可以不使用 pre 和 code 標(biāo)簽來(lái)包裹代碼,改為使用自定義的容器,使用時(shí)需要注意換行和等寬字體的問(wèn)題。
document.querySelectorAll('div.code').forEach((block) => {
hljs.highlightBlock(block);
});
在渲染大量代碼時(shí),為避免瀏覽器卡死,可以使用 Web Worker 來(lái)在后臺(tái)進(jìn)行渲染:
// index.html
addEventListener('load', () => {
const code = document.querySelector('#code');
const worker = new Worker('worker.js'); // 新建Worker
worker.onmessage = (event) => { code.innerHTML = event.data; } // 接受渲染后的HTML
worker.postMessage(code.textContent); // 傳遞代碼
});
// worker.js
onmessage = (event) => {
importScripts('<path>/highlight.min.js');
const result = self.hljs.highlightAuto(event.data); // 高亮渲染
postMessage(result.value); // 返回HTML
};
Highlight.js 使得在前端頁(yè)面進(jìn)行語(yǔ)法高亮變得十分方便,為在網(wǎng)頁(yè)顯示的代碼增添了顏色和生機(jī)。
Highlight.js 文檔詳盡,設(shè)計(jì)簡(jiǎn)潔,為編寫(xiě)新的語(yǔ)言支持和配色方案提供了很大支持,定制化能力和可擴(kuò)展性極強(qiáng)。Highlight.js 的代碼包含了對(duì)于各種語(yǔ)言的語(yǔ)法解析,和不同配色方案的設(shè)計(jì),對(duì)于對(duì)編程語(yǔ)言和語(yǔ)法高亮領(lǐng)域感興趣的開(kāi)發(fā)者是一座珍貴的寶庫(kù)。
行代碼就能讓我的網(wǎng)站支持代碼高亮的工具庫(kù),也支持在 Vue 中使用,強(qiáng)烈推薦給大家。
highlight.js 是一款使用 javascript 開(kāi)發(fā)代碼高亮工具庫(kù),能夠讓網(wǎng)頁(yè)上的代碼顯示接近我們使用的代碼編輯器的高亮樣式,從而看起來(lái)更舒服,增強(qiáng)閱讀體驗(yàn)。
highlight.js 官網(wǎng)截圖
常來(lái)我網(wǎng)站的小伙伴都知道,我的文章有一個(gè)欄目是“前端”,主要推薦一下實(shí)用的前端開(kāi)源項(xiàng)目或者組件庫(kù),寫(xiě)技術(shù)類(lèi)文章免不了要貼代碼,我的網(wǎng)站基于 wordpress 搭建,此前我一直為找一款代碼高亮插件煩惱,但大部分 wordpress 的代碼高亮插件實(shí)在太臃腫,出來(lái)的樣式又不美觀(guān)。大多時(shí)候是截圖 VsCode 的代碼界面,甚至還用過(guò) codepng 這個(gè)工具把代碼變成圖片貼在文章中,但這樣做是美觀(guān)了,但也存在2個(gè)問(wèn)題:
最終還是找到了 highlight.js,完美解決了上面兩個(gè)問(wèn)題,而且配置簡(jiǎn)單,演示漂亮,定制化簡(jiǎn)單。不禁感嘆:用純前端的方式解決,才能精準(zhǔn)控制,實(shí)現(xiàn)想要的效果。
下面以我的網(wǎng)站為例,展示將 highlight.js 用在我們的項(xiàng)目上的方法。首先 highlight.js 支持 cdn 直接引入和 npm 安裝,我的網(wǎng)站基于 wordpress 開(kāi)發(fā),主題是自己寫(xiě)的,最簡(jiǎn)單的方式就是在文章詳情頁(yè)引入 highlight.js 和主題樣式。
雖然 highlight.js 支持幾百種開(kāi)發(fā)語(yǔ)言,但為了將文件體積控制到最小,我們可以點(diǎn)擊“get version”按鈕進(jìn)入下載頁(yè),通過(guò)勾選我們需要的開(kāi)發(fā)語(yǔ)言,來(lái)構(gòu)建最輕量的庫(kù)。
下載解壓后得到的 highlight.min.js 就是我們需要引入的 js 文件,主題樣式都在 style 文件夾里,我選擇了一個(gè)比較喜歡的 monokai-sublime 主題,只需要一個(gè) css 文件,然后初始化:
<link href="/js/monokai-sublime.min.css" rel="stylesheet" type="text/css">
<script src="/js/highlight.min.js"></script>
<script>
hljs.highlightAll();
</script>
就是這么簡(jiǎn)單,highlight.js 會(huì)自動(dòng)將文章中的 <pre><code></code></pre> 代碼進(jìn)行識(shí)別語(yǔ)言并且高亮,一切就是這么簡(jiǎn)單。為了讓代碼顯示更協(xié)調(diào),我用幾行 css 控制了包裹層的圓角以及背景顏色、字體大小等,大功告成。
.post-content .wp-block-code {
background-color: #F6F8FF;
border-radius: 16px;
font-size: 16px;
padding: 22px 22px 22px 38px;
margin-top: 22px;
margin-bottom: 22px;
}
.post-content .wp-block-code {
line-height: 1.2;
font-size: 15px;
padding: 10px;
overflow-x: auto;
}
.post-content .wp-block-code code {
position: relative;
background-color: unset !important;
}
當(dāng)然 highlight.js 也能在 vue 項(xiàng)目中使用,安裝:
npm install highlight.js
在 Vue 文件中使用 (通過(guò) highlight.js for Vue ) :
<div id="app">
<!-- bind to a data property named `code` -->
<highlightjs autodetect :code="code" />
<!-- or literal code works as well -->
<highlightjs language='javascript' code="var x = 5;" />
</div>
需要注意的是,自動(dòng)識(shí)別模式不能100%識(shí)別出代碼所屬的開(kāi)發(fā)語(yǔ)言,識(shí)別錯(cuò)誤會(huì)導(dǎo)致高亮樣式是別的語(yǔ)言的,這種情況下可以手動(dòng)設(shè)置一個(gè) class 來(lái)精準(zhǔn)控制:
<pre><code class="language-javascript">...</code></pre>
官網(wǎng)提供了詳盡的使用文檔,有更多代碼高亮的控制,但不足的就是 highlight.js 沒(méi)有顯示行號(hào)的支持,需要通過(guò)再引入一個(gè)庫(kù) (highlightjs-line-numbers.js) 或者自行實(shí)現(xiàn)。
highlight.js 是一款基于 BSD 許可證開(kāi)源的 javascript 工具庫(kù),任何個(gè)人和公司都可以免費(fèi)下載用于自己的項(xiàng)目,包括商用項(xiàng)目。
關(guān)注我,持續(xù)分享高質(zhì)量的免費(fèi)開(kāi)源、免費(fèi)商用的資源。
↓↓點(diǎn)擊查看本次分享的網(wǎng)址以及代碼高亮效果
highlight.js - 讓網(wǎng)頁(yè)上的代碼高亮美化的免費(fèi)開(kāi)源工具庫(kù)|那些免費(fèi)的磚
*請(qǐng)認(rèn)真填寫(xiě)需求信息,我們會(huì)在24小時(shí)內(nèi)與您取得聯(lián)系。