整合营销服务商

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

免费咨询热线:

记Prism实现文章代码高亮

、前言

1.1、时代的眼泪

在建站初期一直使用富文本默认的样式,其中在代码部分的展示对阅读造成了很大的困扰,

故此,在近期有对文章明细中代码样式部分进行改造,在之前是单调的灰色块,如下:


改造后无论是从格式还是样式都有了质的飞跃,和使用IDE的情况很接近了。


1.2、当世的英雄

下面我就把整个的改造过程分享给大家,希望能有些帮助。

二、Prims实现代码高亮

2.1、什么是Prism

简单来说,Prism.js是一个轻量级的代码着色器,可以使Html中的代码像IDE中一样对背景色,语法,关键字做出高亮处理。

附上其官网:https://prismjs.com/

2.2、如何下载Prism

以我目前适用的Nuxt为例:

在官网的download下我们可以看到有语言、核心库、主题、插件三大项让用户去自定义选择,如下:






2.2、引入JS和CSS

以我的Nuxt项目为例,在页面用配置Head属性如下:


         head() {
            return {
              title: this.articleTitle + "-光慕华",
              link: [
                { rel: 'stylesheet', href: '/css/prism.css' }
              ],
              script: [
                { src: '/js/prism.js'},
                { src: '/js/jquery.min.js'}
              ]
            }
          },

Java


2.3、在Created钩子函数中进行代码高亮处理


        created(){    
              if (typeof window !== 'undefined') {
                  setTimeout(()=>{
                    // 这里加定时器让它后执行,不然没效果
                    Prism.highlightAll() 
                  },200)
                }
          },

Java


三、写在最后


选择Prosm主要还是因为一下原因

① 支持丰富

② 可以根据需要自定义配置

③ 引入和使用简单

希望本篇文章能对需要的人起到一定的帮助作用,加油!

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

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

本以为一个 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前端最新入门资料,一起学习,一起成长!

者:HelloGitHub-追梦人物

文中涉及的示例代码,已同步更新到HelloGitHub-Team 仓库[1]

为了让博客文章具有良好的排版,显示更加丰富的格式,我们使用 Markdown 语法来书写博文。Markdown 是一种 HTML 文本标记语言,只要遵循它约定的语法格式,Markdown 的解析工具就能够把 Markdown 文档转换为标准的 HTML 文档,从而使文章呈现更加丰富的格式,例如标题、列表、代码块等等 HTML 元素。由于 Markdown 语法简单直观,不用超过 5 分钟就可以轻松掌握常用的标记语法,因此大家青睐使用 Markdown 书写 HTML 文档。下面让我们的博客也支持使用 Markdown 写作。

安装 Python Markdown

将 Markdown 格式的文本解析成标准的 HTML 文档是一个复杂的工程,好在已有好心人帮我们完成了这些工作,直接拿来使用即可。首先安装 Markdown,这是一个 Python 第三方库,在项目根目录下运行命令 pipenv install markdown。

在 detail 视图中解析 Markdown

将 Markdown 格式的文本解析成 HTML 文本非常简单,只需调用这个库的 markdown 方法。我们书写的博客文章内容存在 Post 的 body 属性里,回到我们的详情页视图函数,对 post 的 body 的值做一下解析,把 Markdown 文本转为 HTML 文本再传递给模板:

blog/views.py

import markdown
from django.shortcuts import get_object_or_404, render

from .models import Post

def detail(request, pk): 
	post = get_object_or_404(Post, pk=pk) 
	post.body = markdown.markdown(post.body, 
								extensions=[ 
									'markdown.extensions.extra', 
									'markdown.extensions.codehilite', 
									'markdown.extensions.toc', 
								]) 
	return render(request, 'blog/detail.html', context={'post': post})

这样我们在模板中显示 {{ post.body }} 的时候,就不再是原始的 Markdown 文本了,而是解析过后的 HTML 文本。注意这里我们给 markdown 解析函数传递了额外的参数 extensions,它是对 Markdown 语法的拓展,这里使用了三个拓展,分别是 extra、codehilite、toc。extra 本身包含很多基础拓展,而 codehilite 是语法高亮拓展,这为后面的实现代码高亮功能提供基础,而 toc 则允许自动生成目录(在以后会介绍)。

来测试一下效果,进入后台,这次我们发布一篇用 Markdown 语法写的测试文章看看,你可以使用以下的 Markdown 测试代码进行测试,也可以自己书写你喜欢的 Markdown 文本。假设你是 Markdown 新手请参考一下这些教程,一定学一下,保证你可以在 5 分钟内掌握常用的语法格式,而以后对你写作受用无穷。可谓充电 5 分钟,通话 2 小时。以下是我学习中的一些参考资料:

•Markdown——入门指南[2]

•Markdown 语法说明[3]

# 一级标题

## 二级标题

### 三级标题

- 列表项1
- 列表项2
- 列表项3

> 这是一段引用

```python
def detail(request, pk): 
	post = get_object_or_404(Post, pk=pk) 
	post.body = markdown.markdown(post.body, 
								extensions=[ 
									'markdown.extensions.extra', 
									'markdown.extensions.codehilite', 
									'markdown.extensions.toc', 
								]) 
	return render(request, 'blog/detail.html', context={'post': post})​
```

如果你发现无法显示代码块,即代码无法换行,请检查代码块的语法是否书写有误。代码块的语法如上边的测试文本中最后一段所示。

你可能想在文章中插入图片,目前能做的且推荐做的是使用外链引入图片。比如将图片上传到七牛云这样的云存储服务器,然后通过 Markdown 的图片语法将图片引入。Markdown 引入图片的语法为:![图片说明](图片链接)

safe 标签

我们在发布的文章详情页没有看到预期的效果,而是类似于一堆乱码一样的 HTML 标签,这些标签本应该在浏览器显示它自身的格式,但是 django 出于安全方面的考虑,任何的 HTML 代码在 django 的模板中都会被转义(即显示原始的 HTML 代码,而不是经浏览器渲染后的格式)。为了解除转义,只需在模板变量后使用 safe 过滤器即可,告诉 django,这段文本是安全的,你什么也不用做。在模板中找到展示博客文章内容的 {{ post.body }} 部分,为其加上 safe 过滤器:{{ post.body|safe }},大功告成,这下看到预期效果了。

safe 是 django 模板系统中的过滤器(Filter),可以简单地把它看成是一种函数,其作用是作用于模板变量,将模板变量的值变为经过滤器处理过后的值。例如这里 {{ post.body|safe }},本来 {{ post.body }}经模板系统渲染后应该显示 body 本身的值,但是在后面加上 safe 过滤器后,渲染的值不再是 body 本身的值,而是由 safe 函数处理后返回的值。过滤器的用法是在模板变量后加一个 | 管道符号,再加上过滤器的名称。可以连续使用多个过滤器,例如 {{ var|filter1|filter2 }}

代码高亮

程序员写博客免不了要插入一些代码,Markdown 的语法使我们容易地书写代码块,但是目前来说,显示的代码块里的代码没有任何颜色,很不美观,也难以阅读,要是能够像代码编辑器里一样让代码高亮就好了。

代码高亮我们借助 js 插件来实现,其原理就是 js 解析整个 html 页面,然后找到代码块元素,为代码块中的元素添加样式。我们使用的插件叫做 highlight.js 和 highlightjs-line-numbers.js,前者提供基础的代码高亮,后者为代码块添加行号。

首先在 base.html 的 head 标签里引入代码高亮的样式,有多种样式供你选择,这里我们选择 GitHub 主题的样式。样式文件直接通过 CDN 引入,同时在 style 标签里自定义了一点元素样式,使得代码块的显示效果更加完美。

<head> 
	... 
	<link href="https://cdn.bootcss.com/highlight.js/9.15.8/styles/github.min.css" rel="stylesheet"> 

	<style> 
		.codehilite { 
		padding: 0; 
		} 

		/* for block of numbers */ 
		.hljs-ln-numbers { 
			-webkit-touch-callout: none; 
			-webkit-user-select: none; 
			-khtml-user-select: none; 
			-moz-user-select: none; 
			-ms-user-select: none; 
			user-select: none; 

			text-align: center; 
			color: #ccc; ~
			color: #ccc; 
			border-right: 1px solid #CCC; 
			vertical-align: top; 
			padding-right: 5px; 
		} 
		.hljs-ln-n { 
			width: 30px; 
		} 

		/* for block of code */ 
		.hljs-ln .hljs-ln-code { 
			padding-left: 10px; 
			white-space: pre; 
		} 
</style>
</head>

然后是引入 js 文件,因为应该等整个页面加载完,插件再去解析代码块,所以把 js 文件的引入放在 body 底部:

<body> 
	<script src="https://cdn.bootcss.com/highlight.js/9.15.8/highlight.min.js"></script> 
	<script src="https://cdn.bootcss.com/highlightjs-line-numbers.js/2.7.0/highlightjs-line-numbers.min.js"></script> 
	<script> 
		hljs.initHighlightingOnLoad(); 
		hljs.initLineNumbersOnLoad(); 
</script>
</body>

非常简单,通过 CDN 引入 highlight.js 和 highlightjs-line-numbers.js,然后初始化了两个插件。再来看下效果,非常完美!

References

[1] HelloGitHub-Team 仓库: https://github.com/HelloGitHub-Team/HelloDjango-blog-tutorial

[2] Markdown——入门指南: http://www.jianshu.com/p/1e402922ee32/

[3] Markdown 语法说明: http://www.appinn.com/markdown/

欢迎关注 HelloGitHub 公众号,获取更多开源项目的资料和内容

『讲解开源项目系列』启动——让对开源项目感兴趣的人不再畏惧、让开源项目的发起者不再孤单。跟着我们的文章,你会发现编程的乐趣、使用和发现参与开源项目如此简单。欢迎联系我们给我们投稿,让更多人爱上开源、贡献开源~