Skip to content

Instantly share code, notes, and snippets.

@prinsss
Created June 11, 2017 14:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save prinsss/ecddd370f07fbab69e4bf9c36085c54c to your computer and use it in GitHub Desktop.
Save prinsss/ecddd370f07fbab69e4bf9c36085c54c to your computer and use it in GitHub Desktop.
依赖于 jQuery 的纯前端生成文章目录(Table of Contents)。本来是为了辣鸡 Ghost 博客写的,可惜我现在要投奔 Hexo 了 ( ̄3 ̄)
/**
* 差不多就是把一些项目中的现有代码黏在一起,能用而已。目前实现的功能有:
*
* - 生成 TOC(废话)
* - 滚动监听(高亮当前项目)
* - 点击跳转
*/
// https://github.com/ghiculescu/jekyll-table-of-contents
(function($){
$.fn.toc = function (options) {
var defaults = {
noBackToTopLinks: false,
title: '<i>Jump to...</i>',
minimumHeaders: 3,
scope: 'body',
selector: 'h1, h2, h3, h4, h5, h6',
overwrite: true, // generate new IDs for header elements
listType: 'ol', // values: [ol|ul]
showEffect: 'show', // values: [show|slideDown|fadeIn|none]
showSpeed: 'slow', // set to 0 to deactivate effect
classes: { list: '', item: '' }
},
options = $.extend(defaults, options);
function fixedEncodeURIComponent(str) {
return encodeURIComponent(str).replace(/[!'()*]/g, function(c) {
return '%' + c.charCodeAt(0).toString(16);
});
}
function createLink(header) {
var innerText = (header.textContent === undefined) ? header.innerText : header.textContent;
return "<a href='#" + fixedEncodeURIComponent(header.id) + "'>" + innerText + "</a>";
}
if (options.overwrite) {
var i = 0;
$(options.scope).find(options.selector).each(function () {
$(this).attr('id', 'article-title-' + i);
i++;
});
}
var headers = $(options.scope).find(options.selector).filter(function () {
// get all headers with an ID
var previousSiblingName = $(this).prev().attr( "name" );
if (!this.id && previousSiblingName) {
this.id = $(this).attr( "id", previousSiblingName.replace(/\./g, "-") );
}
return this.id;
}), output = $(this);
if (!headers.length || headers.length < options.minimumHeaders || !output.length) {
$(this).hide();
return;
}
if (0 === options.showSpeed) {
options.showEffect = 'none';
}
var render = {
show: function() { output.hide().html(html).show(options.showSpeed); },
slideDown: function() { output.hide().html(html).slideDown(options.showSpeed); },
fadeIn: function() { output.hide().html(html).fadeIn(options.showSpeed); },
none: function() { output.html(html); }
};
var get_level = function(ele) { return parseInt(ele.nodeName.replace("H", ""), 10); };
var highest_level = headers.map(function(_, ele) { return get_level(ele); }).get().sort()[0];
var return_to_top = '<i class="icon-arrow-up back-to-top"> </i>';
var level = get_level(headers[0]),
this_level,
html = options.title + " <" +options.listType + " class=\"" + options.classes.list +"\">";
headers.on('click', function() {
if (!options.noBackToTopLinks) {
window.location.hash = this.id;
}
})
.addClass('clickable-header')
.each(function(_, header) {
this_level = get_level(header);
if (!options.noBackToTopLinks && this_level === highest_level) {
$(header).addClass('top-level-header').after(return_to_top);
}
if (this_level === level) // same level as before; same indenting
html += "<li class=\"" + options.classes.item + "\">" + createLink(header);
else if (this_level <= level){ // higher level than before; end parent ol
for(var i = this_level; i < level; i++) {
html += "</li></"+options.listType+">"
}
html += "<li class=\"" + options.classes.item + "\">" + createLink(header);
}
else if (this_level > level) { // lower level than before; expand the previous to contain a ol
for(i = this_level; i > level; i--) {
html += "<" + options.listType + " class=\"" + options.classes.list +"\">" +
"<li class=\"" + options.classes.item + "\">"
}
html += createLink(header);
}
level = this_level; // update for the next one
});
html += "</"+options.listType+">";
if (!options.noBackToTopLinks) {
$(document).on('click', '.back-to-top', function() {
$(window).scrollTop(0);
window.location.hash = '';
});
}
render[options.showEffect]();
return this;
};
})(jQuery);
/**
* jQuery lightweight plugin boilerplate
*
* @file_name jquery.navScrollSpy.js
* @author liuyidi <liuyidi1993@gmail.com>
* Licensed under the MIT license
*
* example:
* <ul id="nav">
* <li class="active"><a href="#box1">box1</a></li>
* <li><a href="#box2">box2</a></li>
* </ul>
*
* $("#nav").navScrollSpy({
* active:"",
* scrollSpeed: 750
* });
*
* 滚动监听 点击切换 平滑滚动
*/
;(function ($, window, document, undefined) {
// pluginName
var pluginName = "navScrollSpy";
// defaults options
var defaults = {
navContainer: '#nav', // 外部容器
navItems: 'a', // 元素
active : 'active', // 当前
easing : 'swing', // 动效
speed: 550 // 速度
};
function navScrollSpy (element, options) {
this.element = element;
this.options = $.extend({}, defaults, options);
this.$ele = $(element); // 获得 $("#nav")
this.$win = $(window); // 获取 window
this._defaults = defaults;
this._name = pluginName;
this.anchorItems = {};
this.init();
};
navScrollSpy.prototype = {
init: function() {
this.navItems = this.$ele.find(this.options.navItems);
this.anchorItems = this.getAnchorItems(this.navItems);
// 点击切换导航按钮样式, 更改上下文 this
this.navItems.on("click", $.proxy(this.clickSwitch, this));
// 滚动切换导航按钮
this.$win.on("scroll", $.proxy(this.scrolling, this));
return this;
},
getAnchorItems: function (navItems) {
var items = [];
// find items in selected element
navItems.each(function () {
var id = $(this).attr('href').split('#')[1];
items[id] = {
// get the offset top of anchor element with corresponding id
top: $('#' + id).offset().top,
height: $(this).height(),
elem: this
};
});
return items;
},
// 导航变化
active: function(self, parent) {
var active = self.options.active;
self.$ele.find("."+active).removeClass(active);
parent.addClass(active);
},
// 滚动切换
scrolling: function () {
// var st = $(window).scrollTop();
// var wH = $(window).height();
// 临界条件: $("#id").offset().top-$(window).scrollTop()>$(window).height()/2;
for (var id in this.anchorItems) {
var item = this.anchorItems[id];
// if(st >= this.boxs[box]-parseInt(wH/2)){
if ($(window).scrollTop() >= item.top - item.height / 3) {
var parent = this.$ele.find('a[href="#'+id+'"]').parent();
this.active(this, parent);
}
};
},
// 点击切换
clickSwitch: function(e) {
var navItemClicked = $(e.currentTarget); // 获得当前的 a
var parent = navItemClicked.parent(); // 获得 a 的 li 元素
var self = this;
var target = navItemClicked.attr('href').split('#')[1]; // 新的 a #id
if (!parent.hasClass(self.options.active)) {
// 导航切换
self.active(self, parent);
// 滚动开始
self.scrollTo(target, function(){
//callback
});
}
e.preventDefault(); // 有 active 阻止冒泡
},
// 滚动到某个地方
scrollTo: function(target, callback){
// 获取目标元素的 TOP 值
var offsetTop = this.anchorItems[target].top;
var $el = $('html,body');
if (!$el.is(":animated")) {
$el.animate({
scrollTop: offsetTop
}, this.options.speed, this.options.easing,callback);
}
}
};
$.fn.navScrollSpy = function(options) {
return this.each(function () {
if(!$.data(this, "plugin_"+pluginName)){
$.data(this, "plugin_"+pluginName,new navScrollSpy(this, options));
}
});
};
})(jQuery, window, document);
// 示例,$('#toc') 为 TOC 容器,在 '.post-content' 中检索并生成目录
$('#toc').toc({
scope: '.post-content'
}).navScrollSpy({
navContainer: '#toc'
});
/* 自定义样式 */
#toc ol {
list-style: none;
}
#toc ol li a {
color: inherit;
}
#toc ol li.active > a:first-child {
color: #009553; /* 高亮当前项 */
}
#toc ol li, #toc ol li a {
overflow-x: hidden;
text-overflow: ellipsis;
-o-text-overflow: ellipsis;
white-space: nowrap;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment