JavaScript:Prism实现代码高亮

代码高亮解决方案

在之前用过Crayon Syntax Highlighter,Enlighter这两个代码高亮的插件.可能是js或其余部分存在冲突,总是导致奇奇怪怪的问题.也找了一些纯js的代码高亮框架.比如: Highlight.js.但最后发现一个高亮框架比较适合: Prism.js.

Prism.js

从我的角度介绍一下Prism.js:

  • 核心文件小(约2kb);
  • 可以按语言自动加载高亮文件(有条件)
  • 代码行号,复制代码等功能作为插件提供
  • 可按照插件架构自行扩展
  • 多种主题可选

最主要的不会存在升级Wordpress插件导致样式混乱的问题了.同时,在Prism.js的下载页面可以按照自己需要的部分进行下载,最大程度减少文件体积.

image-3180

综合引用

可以在Prism官网,直接下载一个打包好的JavaScript文件和CSS文件:

参考地址:

官方打包下载地址

打包之后的文件,只需要引入一个CSS和一个JavaScript文件即可.

示例

我目前使用的body下面的第一个div添加的class和data-toolbar-order:


		<div data-toolbar-order="show-language,copy-to-clipboard" class="line-numbers match-braces rainbow-braces">
		</div>

使用方式

  1. 先引入css
  2. 然后在pre,code中包含代码,同时为code设置class,class格式为: language-css,language-html,language-java或者lang-java,lang-css,lang-html.更多支持语言的简写,参考这里的Supported languages.
  3. 最后引入prism.js文件

下面的代码只会显示代码高亮(未引用语言文件的情况下,默认只包含了css,html,js语言的高亮文件):



<!DOCTYPE html>
<html>
<head>
	<title>test</title>
	<link href="https://cdn.bootcdn.net/ajax/libs/prism/9000.0.1/themes/prism-dark.min.css" rel="stylesheet">
</head>
<body>
<pre><code class="language-css">p { color: red }</code></pre>
</body>
<script src="https://cdn.bootcdn.net/ajax/libs/prism/9000.0.1/prism.min.js"></script>
</html>


使用插件

目前我使用了如下插件:

  • Line Numbers(显示行号)
  • Remove initial line feed(删除初始换行)
  • Normalize Whitespace(空白规范)
  • Toolbar(工具栏)
  • Copy to Clipboard Button(复制按钮)
  • Show Language(显示语言)
  • Match braces(括号高亮[选中单个括号,另外一个对应的括号同时高亮])

显示行号

引入方式:

css:


	<link href="https://cdn.bootcdn.net/ajax/libs/prism/1.20.0/plugins/line-numbers/prism-line-numbers.min.css" rel="stylesheet">

JavaScript:

	
		<script src="https://cdn.bootcdn.net/ajax/libs/prism/1.20.0/plugins/line-numbers/prism-line-numbers.min.js"></script>
	

然后在body下面的第一个div设置:

	
		<div id="page" class="line-numbers">
			......内容
		</div>

之后直接使用即可:


<pre><code class="language-css">
p { color: red }

</code></pre>

括号高亮

引入:

css:


		<link href="https://cdn.bootcdn.net/ajax/libs/prism/1.20.0/plugins/match-braces/prism-match-braces.min.css" rel="stylesheet">

JavaScript:


<script src="https://cdn.bootcdn.net/ajax/libs/prism/1.20.0/plugins/match-braces/prism-match-braces.min.js"></script>

参考效果: 官方文档

实现效果:

添加class: match-braces 到body下面的第一个div即可.例如:

	
		<div id="page" class="line-numbers match-braces">
			......内容
		</div>

如果要显示五颜六色的括号,则继续添加: rainbow-braces,例如:

	
		<div id="page" class="line-numbers match-braces rainbow-braces">
			......内容
		</div>

工具栏

引入:

CSS:

	
	<link href="https://cdn.bootcdn.net/ajax/libs/prism/1.20.0/plugins/toolbar/prism-toolbar.min.css" rel="stylesheet">

JavaScript:

	
	<script src="https://cdn.bootcdn.net/ajax/libs/prism/1.20.0/plugins/toolbar/prism-toolbar.min.js"></script>

然后给body设置工具栏要显示的内容:

	
<div data-toolbar-order="show-language,copy-to-clipboard" class="line-numbers match-braces rainbow-braces">
	......
</div>

show-language,copy-to-clipboard

第一个是显示代码块中的语言,第二个是复制代码.

工具栏可以自定义.具体参考: 工具栏插件文档

显示语言

引入:

	
<script src="https://cdn.bootcdn.net/ajax/libs/prism/1.20.0/plugins/show-language/prism-show-language.min.js"></script>

复制代码

引入:

	
<script src="https://cdn.bootcdn.net/ajax/libs/prism/1.20.0/plugins/copy-to-clipboard/prism-copy-to-clipboard.min.js"></script>

即可完成工具栏的效果.

ECMAScript什么时候能像TypeScript一样好用呢

前几天,TypeScript发布3.7版本,增加了很多好用的功能.其中我最喜欢的是可选链操作符(?.),当时第一感觉就是这简直太方便了..

image-2803

另外想到的是ECMAScript也有这个功能,查了一下MDN,确实有(https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/%E5%8F%AF%E9%80%89%E9%93%BE).但是文档里面有一段话扎心了…

警告:截至2019年8月,没有本地环境实现了这个功能。如果你使用Babel,你可以使用 this plugin 来模拟可选链。

{{EmbedInteractiveExample(“pages/js/expressions-optionalchainingoperator.html”)}}

也就是说…特性是有,但没有被支持,在文档的下方也确实显示了目前没有任何浏览器支持该特性.

于是,从ECMAScript换到TypeScript还是有必要,毕竟新版Vue不也换成用TypeScript编写了吗?

JavaScript: TypeScript 3.7发布

关于TypeScript

TypeScript是一种由微软开发的自由和开源的编程语言。它是JavaScript的一个严格超集,并添加了可选的静态类型和使用看起来像基于类的面向对象编程语法操作 Prototype。C#的首席架构师以及Delphi和Turbo Pascal的创始人安德斯·海尔斯伯格参与了TypeScript的开发。[4][5][6][7]

TypeScript设计目标是开发大型应用,然后转译成JavaScript。[8]由于TypeScript是JavaScript的严格超集,任何现有的JavaScript程序都是合法的TypeScript程序。

TypeScript支持为现存JavaScript库添加类型信息的定义文件,方便其他程序像使用静态类型的值一样使用现有库中的值。当前有第三方提供常用库如jQuery、MongoDB、Node.js和D3.js的定义文件。

TypeScript编译器本身也是用TypeScript编写,并被转译为JavaScript,以Apache License 2发布。 — 维基百科

image-2789

关于TypeScript中文网的提示

www.tslang.cn 是TypeScript中文网,上面的文档有些滞后,目前仍停留在3.1版本.不过作为入门学习还是可以的,但建议参考官网最新文档.

发布3.7版本

主要更新内容:

  1. Optional Chaining
  2. Nullish Coalescing
  3. Assertion Functions
  4. Better Support for never-Returning Functions
  5. (More) Recursive Type Aliases
  6. –declaration and –allowJs
  7. The useDefineForClassFields Flag and The declare Property Modifier
  8. Build-Free Editing with Project References
  9. Uncalled Function Checks
  10. // @ts-nocheck in TypeScript Files
  11. Semicolon Formatter Option

详细更新内容: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html

Optional Chaining

这是个很有用的功能,可以在编码时减少判断.

示例:

// 之前
if (foo && foo.bar && foo.bar.baz) {
    // ...
}


// 现在
if (foo?.bar?.baz) {
    // ...
}

// 语法是: ?. , 可作用于:


obj?.prop // 静态属性访问
obj?.[expr] // 动态访问
func?.(...args) // 函数或方法调用

其实在JavaScript官方中也定义了类似的,但目前没有任何浏览器支持.相关文档可以参考: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/%E5%8F%AF%E9%80%89%E9%93%BE

其余特性请参考官方更新日志.

JavaScript:jerichoTab每次刷新

关于

由于使用了jerichoTab,每次切换到已经打开的Tab时,不会刷新页面(可能导致页面内容状态不一致等问题).

jerichoTab是一款模拟 firefox 标签页的插件, 通过丰富的API可以轻松的实现动态添加, 删除, 激活tab, 当tab页过多时会在左右两段增加滚动条…引用自:【地址

jQuery LOGO
image-2448

解决方案

只需加一句即可:


//activate the tag(orderKey is the tagGuid of each tab)
setTabActive: function(orderKey) {
var lastTab = $.fn.jerichoTab.tabpage.children('li').filter('.tab_selected');
if (lastTab.length > 0) lastTab.swapTabEnable();
$('#jerichotab_' + orderKey).loadData(true);//每次点击都刷新页面.[添加这一句即可每次点击页签都刷新内容]
return $('#jerichotab_' + orderKey).swapTabEnable();
},

附上完整JavaScript代码:


///
/*
* jerichotab
* version: release-2.0.1 (05/13/2009)
* @ jQuery v1.3.*
*
* Licensed under the GPL:
* http://gplv3.fsf.org
*
* Copyright 2008, 2009 Jericho [ thisnamemeansnothing[at]gmail.com ]
========================================
#example:

========================================
========================================
#API:
#$.fn.initJerichoTab(Function):
*renderTo(String): the tab render to('#sample')
*uniqueId(String): the tab's id(It must be unique)
*tabs(Array): the tabs will be initialized, whose items will be formated as follows:
{
**title(String): the tab title text
**iconImg(String): the tab icon that displayed from title text,
**closeable(Boolean): the switch that controls whether the tab can be closed (true as default)
}
*activeTabIndex(Int): the tab you'd like to select after loading(0 as default)
*contentHeight(Int): height of the content div tag
*contentCss(Object): the same as style sheet
*loadOnce(Boolean): the switch controls if load tab content at the first time(true as default)
*tabWidth(Int): width of each tab(150 as default)
#$.fn.jerichoTab.addTab(Function):
*tabId(String); the unique tab Id(Unused, private)
*tabFirer(JQuery Object): the object that makes tab shown in a special way
*title(String): the tab title text
*data(Object): the tab data to load,including:
**dataType:type of data,
**dataLink:data link
#example(must use as suited):
##formtag:
*dataType:'formtag',
//***use the html tags in this page
*dataLink:'#example'
//***id of the tag you'd like to display in this tab
##iframe:
*dataType:'iframe',
//***use the iframe to load another page
*dataLink:''
//***such as 'iframetemplates/iframe.htm'
//***the relative url of the page you'd like to display in this tab,
//***and jerichoTab will use an iframe to load it
###html:
*dataType:'html',
//*** load data from html url
*dataLink:''
// *** the relative url of your html page
##ajax:
*dataType:'ajax',
//***use ajax to load data with asynchronous operations
*dataLink:''
//*** yes,u can write down your ajax handler url and then jerichotab'll make a callback,
//*** so the responseText will be displayed in the content holder(u can use html tags in your server callback datas)
*onLoadCompleted(Function): fired after the data has been loaded
*iconImg(String): the tab icon that displayed below title text(relative to...),
*closeable(Boolean): set whether the tab can be closed(true as default)
========================================
*/

//; (function($) {
$.extend($.fn, {
initJerichoTab: function(setting) {
var opts = $.fn.extend({
//the container of jerichotab(is required, a jQuery format selector String as '.container' or '#container')
renderTo: null,
//the unique id of jerichotab(is required and unique, not null)
uniqueId: null,
//format your tab data like this: [{title:'',iconImg:'',closeable:true},{title:'',iconImg:'',closeable:true}]
//it's an Array...
tabs: [],
//when the jerichotab has been loaded, the tab you'ld like to display first(start at 0, and 0 as default)
activeTabIndex: 0,
//the style sheet of tab content
contentCss: {
'height': '500px'
},
//if you set this property as true, the data'll be loaded only at the first time when users click the tab
//in other times jerichotab only swich it's css(display property) from 'none' to 'block'
loadOnce: true,
//the tab width (150 as default)
tabWidth: 110,
//set an ajaxload effect, jerichotab has provided two choices: 'usebg' | 'righttag'
//'usebg': control if set a big loading gif in the contentholder
//'righttag': this will set a small loading gif in the right top of contentholder
loader: 'righttag',
//两边滑块宽度
slidersWidth: 19,
//标题高度
titleHeight: 26
}, setting);
//initialize the jerichotab
function createJerichoTab() {
//make sure that a container and uniqueId were provided
if (opts.renderTo == null) { alert('you must set the \'renderTo\' property\r\t--JeirchoTab'); return; }
if (opts.uniqueId == null) { alert('you must set the \'uniqueId\' property\r\t--JeirchoTab'); return; }
if ($('#' + opts.uniqueId).length > 0) { alert('you must set the \'uniqueId\' property as unique\r\t--JeirchoTab'); return; }
//the jerichotab html tree:
/*

    ###tabpages here

###tabcontents here

*/
var jerichotab = $('

    ')
    .appendTo($(opts.renderTo));

    //apply contentcss to the contentholder
    $('.tab_content>.content', jerichotab).css(opts.contentCss);

    //fill data
    $.fn.jerichoTab = {
    master: jerichotab,
    tabWidth: opts.tabWidth,
    tabPageWidth: $('.tab_pages', jerichotab).width(),
    slidersWidth: opts.slidersWidth,
    loader: opts.loader,
    loadOnce: opts.loadOnce,
    tabpage: $('.tab_pages>.tabs>ul', jerichotab),
    addTab: function(tabsetting) {
    //set as the unique tab id and tabFirer tag
    tagGuid = (typeof tagGuid == 'undefined' ? 0 : tagGuid + 1);
    var curIndex = tagGuid;
    //this function will be open to all users for them to add tab at any time
    var ps = $.fn.extend({
    //if there is a DOM that cause the tab to be added to tabpages,
    //you should pass it to jerichotab, in which way tab'll only be activated when
    //user click the DOM next time
    tabFirer: null,
    title: '新增页签'+(curIndex+1),
    //the dataType and dataLink were suited as:
    //1.formtag:
    // dataType:'formtag',
    // --use the html tags in this page
    // dataLink:'#example'
    // --id of the tag you'ld like to display in this tab
    //2.iframe:
    // dataType:'iframe',
    // --use the iframe to load another page
    // dataLink:''
    // --such as 'iframetemplates/iframe.htm', set
    // --the relative url of the page u'ld like to display in this tab,
    // --and jerichoTab will use an iframe to load it
    //3.html:
    // dataType:'html',
    // --load html tags from a url
    // dataLink:''
    // --the relative url of your html page
    //4.ajax:
    // dataType:'ajax',
    // --use the ajax to load datas with asynchronous operations
    // dataLink:''
    // --yes,u can write down your ajax handler url and jerichotab'll make a callback,
    // --so the responseText will be displayed in the content holder(u can use html tags in your server callback datas)
    data: { dataType: '', dataLink: '' },
    //set the tab icon of each(relative to...)
    iconImg: '',
    //whether this tab can be closed(ture as default)
    closeable: true,
    //this function will be fired after all data has been loaded
    onLoadCompleted: null,
    // the tab's name
    name:''
    }, tabsetting);
    //window.console && console.log('%o', tabsetting);
    //check whether the tabFirer exists or not, and that it has an attribute['jerichotabindex'], then set the tab that tabFirer was connected activated
    //otherwise attach the jerichotabindex' attribute
    if (ps.tabFirer != null) {
    var jerichotabindex = ps.tabFirer.attr('jerichotabindex');
    if (typeof jerichotabindex != 'undefined' && $('#jerichotab_' + jerichotabindex).length > 0)
    return $.fn.setTabActive(jerichotabindex).adaptSlider().loadData();
    ps.tabFirer.attr('jerichotabindex', curIndex);
    }
    // set name
    if(ps.name == ''){
    ps.name = ps.title;
    }
    //newTab html tree:
    /*

  • JerichoTab

     
  • */
    /*var newTab = $('

  • ' +
    '

    ' +
    (ps.iconImg == '' ? '' : '

     

    ') +
    '

    ' + ps.title.cut(opts.tabWidth / 10 - 1) + '

    ' +
    '

    ' + (ps.closeable ? ' ' : '') + '

    ' +
    '

    ' +
    '

     

    ' +
    '

  • ')*/
    var newTab = $('

  • ' +
    '

    ' +
    (ps.iconImg == '' ? '' : '

     

    ') +
    '

    ' + ps.title.cut(opts.tabWidth / 10 - 1) + '

    ' +
    '

    ' + (ps.closeable ? ' ' : '') + '

    ' +
    '

    ' +
    '

     

    ' +
    '

  • ').appendTo($.fn.jerichoTab.tabpage).css('opacity', '0').applyHover()
    .applyCloseEvent().animate({ 'opacity': '1', width: opts.tabWidth }, 100, function() {
    $.fn.setTabActive(curIndex);
    });
    //use an Array named "tabHash" to restore the tab information
    tabHash = (typeof tabHash == 'undefined' ? [] : tabHash);
    tabHash.push({
    index: curIndex,
    tabFirer: ps.tabFirer,
    tabId: 'jerichotab_' + curIndex,
    holderId: 'jerichotabholder_' + curIndex,
    iframeId: 'jerichotabiframe_' + curIndex,
    onCompleted: ps.onLoadCompleted
    });
    return newTab.applySlider();
    },
    closeCurrentTab: function(tabsetting) {
    $('.tab_selected .tab_close>a').click();
    /*var ps = $.fn.extend({
    name:'',
    activeTabName:'',
    isReaload: false
    }, tabsetting);

    $.fn.jerichoTab.tabpage.children('li[name='+ps.name+']').remove();

    var isLoad = 0;
    if(ps.activeTabName != ''){
    var lis = $.fn.jerichoTab.tabpage.children('li');
    for(var i=0;i 0) ? ($.fn.jerichoTab.slidersWidth * 2) : 0);
    $(".tabs", $.fn.jerichoTab.master).width($.fn.jerichoTab.tabPageWidth).applySlider().adaptSlider();
    var fixHeight = -2;
    //if (Metronic.isIE8()){
    // fixHeight = 25;
    //}
    $('#jerichotab_contentholder').height($(opts.renderTo).height() - opts.titleHeight - 5 - fixHeight);
    $(".jericho_tab iframe").height($(".jericho_tab").parent().height() - opts.titleHeight - fixHeight);
    };
    $(window).resize(function() {
    $.fn.jerichoTab.resize();
    })
    //window.console && console.log('width :' + $.fn.jerichoTab.tabpage.width());
    },
    //activate the tag(orderkey is the tab order, start at 1)
    setTabActiveByOrder: function(orderKey) {
    var lastTab = $.fn.jerichoTab.tabpage.children('li').filter('.tab_selected');
    if (lastTab.length > 0) lastTab.swapTabEnable();
    return $('#jericho_tabs').filter(':nth-child(' + orderKey + ')').swapTabEnable();
    },
    //activate the tag(orderKey is the tagGuid of each tab)
    setTabActive: function(orderKey) {
    var lastTab = $.fn.jerichoTab.tabpage.children('li').filter('.tab_selected');
    if (lastTab.length > 0) lastTab.swapTabEnable();
    $('#jerichotab_' + orderKey).loadData(true);//每次点击都刷新页面.
    return $('#jerichotab_' + orderKey).swapTabEnable();
    },
    addEvent: function(e, h) {
    var target = this.get(0);
    if (target.addEventListener) {
    target.addEventListener(e, h, false);
    } else if (target.attachEvent) {
    target.attachEvent('on' + e, h);
    }
    },
    //create an iframe in the contentholder to load pages
    buildIFrame: function(src) {
    return this.each(function() {
    var onComleted = null, jerichotabiframe = '';
    for (var tab in tabHash) {
    if (tabHash[tab].holderId == $(this).attr('id')) {
    onComleted = tabHash[tab].onCompleted;
    jerichotabiframe = tabHash[tab].iframeId;
    break;
    }
    }
    src += (src.indexOf('?') == -1 ? '?' : '&') + 'tabPageId=' + jerichotabiframe;
    var iframe = $('