好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

原生 js 小工具 v1.1:自动生成博文目录,文内标题平滑跳转:欢迎园友试用!

原生 js 小工具 v1.1:自动生成博文目录,文内标题平滑跳转:欢迎园友试用!

一、前言

  最近十来天都在学习原生 javascript,参考的是 《DOM Scripting》 这本英文原版书,写得确实非常不错,适合 js 基础非常不牢的小白~ 学的时候基本上是看一章就写点读书笔记发表在博客上,但却基本没动过手写代码。

  正好自己最近在写技术博文时遇到了一点小需求,就打算用 js 做一个小工具解决掉。

二、情景

  技术人员写博客时,有一个很常见的方式就是写系列博客(或者称作主题博文),即围绕某一个中心技术点,循序渐进,由浅入深地论述其方方面面。园子里这种现象非常常见,就拿排在前列的几位老大说说,例如  Artech 的 “ 深入剖析授权在 WCF 中的实现[共14篇] ”,例如 李永京 的 “ NHibernate 之旅系列文章 ”,如 小洋(燕洋天)  的 “ 浅谈ASP.NET 的内部机制 ” 等等,都是其中的典范之作。

  为什么高手们似乎都有写系列博客的倾向?个人认为有以下几点好处:

长时间专注于某一具体技术点,不断地深入探索 当你把技术心得说给他人听时,会强迫你去重新整理自己的思路,把其中的一些盲点暴露出来,然后弄清楚 对系列博文的布局安排,要求你将所学知识整理为框架、体系,对知识结构的构建非常有益 好的系列博文,条理分明,非常方便以后个人参考

  呵呵,总之,系列博文的写作非常有利于个人知识的深化和整理,可以说是从小工到专家的一个比较有效的方法之一。

  我们在写系列博文时,不仅要对文章前后的安排顺序仔细斟酌,而且每一篇文章的谋篇布局也是需要花一番头脑的。随便看看上面提到的高手的系列博 文,都会感觉逻辑之分明,条理之清晰。就拿永京老大的 “NHibernate 之旅” 系列文章为例,作者在每篇博文中都用到了大量的标题,主标题下还有次级标题,这样一来把整篇文章的内容结构很好地规划出来,同时为提高读者的阅读体验,还 在每篇文章的开篇列下了文章的标题结构。不多说,上图:

  

  呵呵,读者一打开这篇文章,映入眼帘的就是整篇文章的框架结构,非常清晰并富有逻辑性,有助于把握整篇文章的脉络。从这一点可以看出,为一篇技术博文划分逻辑层次,使用主标题(mainTitle)+ 次级标题(subTitle)的形式是极其常见和实用的。

  笔者的第一个原生 js 小工具的目的,就是为了增强这种 主标题 + 次级标题 的用户体验效果。

三、功能描述

核心功能只有两个:

自动生成博文目录

  如永京老大的博文,写完文章后把大大小小的标题抽出来放在文首固然是个好方法,但总觉得这样做比较麻烦。那么能否通过代码自动抽取标题,然后再包装一下插入文档中呢?

  小工具的第一个核心功能,就是为了解决上述的需求。基本实现原理:首先要求博主在写博文的时候,将主标题和次级标题用 HTML 标签中的 title tag(如:h1、h2、h3...)包起来;然后通过 JS 代码遍历整个包含博文正文的 <div>,过滤出这些标签,再把它们组装成 自定义列表 的形式,再插入到 HTML 文档中,如下:

 <  dl  >  
< dt > NHibernate中的查询方法 </ dt >
< dt > 条件查询 </ dt >
< dd > 创建ICriteria实例 </ dd >
< dd > 结果集限制 </ dd >
< dd > 结果集排序 </ dd >
< dd > 一些说明 </ dd >
< dt > 根据示例查询 </ dt >
< dt > 实例分析 </ dt >
< dt > 结语 </ dt >
</ dl >

  如果不加任何样式,它在浏览器中的默认显示效果如下:

  

  呵呵,不过我们当然可以手动为其添加 CSS 代码,后面会给出来的。

文内标题平滑跳转

  现在假设我们已经构造出了标题的菜单,插入到了文档中。如果我想点击其中的某一项,页面就会定位到相应位置的话,这样会方便很多(特别是在以后 再次浏览的时候)。对  HTML 比较熟的童鞋可能马上想到:用锚!对,我们可以把 <a name="title"></a> 放置到想要跳转的位置,然后再用 <a href="#title"></a> 来填充菜单,那么只要点击后者,浏览器窗口就会立即切换到前者所处的位置。

  这是一个不错的解决方案,但是缺点就在于这种跳转是瞬间的,而且目的地是未知的(不知道向下跳转还是向上跳转),这样会造成非常糟糕的用户体验,会打乱读者在浏览一篇文章时所形成的连贯性。所以我们不打算使用锚,而是自己来使用 JS 实现一种平滑跳转的效果。

  实现的原理也不难:首先通过调用 DOM 方法,判断出浏览器滚动条(scroll bar)的当前位置,记为 currentPos;然后计算出目标标题(target title)的距页面顶端的距离,记为 finalPos;最后通过一定的算法实现平滑过度。

四、源代码

下面给出 JS 源代码和一些必要的 CSS 样式:

JS 源代码 createContent.js

  一共有 5 个函数:

getPos(e) :获取元素位置,即距浏览器左边界的距离(left)和距浏览器上边界的距离(top); getScroll() :获取滚动条当前位置; moveWindow(finalpos, internal) :移动滚动条,finalPos 为目的位置,internal 为移动速度; moveFixElement(elementID, finalTop, finalRight, internal) :移动绝对定位(absolutely、fixed)元素,elementID 表示元素 ID,finalTop 和 finalRight 表示最终位置,internal 表示移动速度 createContent(id, mt, st, interval) :创建标题菜单,id 表示包含博文正文的 div 容器的 id,mt 和 st 分别表示主标题和次级标题的标签名称(如 H2、H3,需大写!),interval 表示移动的速度。 

  其前 4 个函数都是辅助性的函数,最后一个才是用户需要调用的创建标题菜单的函数。下面是全部的源代码:

 //  获取元素位置  
function getPos(e) {
var t = l = 0 ;
while (e)
{
t += e.offsetTop;
l += e.offsetLeft;
e = e.offsetParent;
}
return {top:t, left:l};
}

// 获取滚动条信息
function getScroll() {
return document.body.scrollTop | document.documentElement.scrollTop;
}

// 移动窗体
function moveWindow(finalpos, interval) {

// 若不支持此方法,则退出
if ( ! window.scrollTo) return false ;

// 窗体滚动时,禁用鼠标滚轮
window.onmousewheel = function (){
return false ;
};

// 清除计时
if (document.body.movement) {
clearTimeout(document.body.movement);
}

var currentpos = getScroll(); // 获取滚动条信息

var dist = 0 ;
if (currentpos == finalpos) { // 到达预定位置,则解禁鼠标滚轮,并退出
window.onmousewheel = function (){
return true ;
}
return true ;
}
if (currentpos < finalpos) { // 未到达,则计算下一步所要移动的距离
dist = Math.ceil((finalpos - currentpos) / 10);
currentpos += dist;
}
if (currentpos > finalpos) {
dist = Math.ceil((currentpos - finalpos) / 10);
currentpos -= dist;
}

var scrTop = getScroll(); // 获取滚动条信息
window.scrollTo( 0 , currentpos); // 移动窗口
if (getScroll() == scrTop) // 若已到底部,则解禁鼠标滚轮,并退出
{
window.onmousewheel = function (){
return true ;
}
return true ;
}

// 进行下一步移动
var repeat = " moveWindow( " + finalpos + " , " + interval + " ) " ;
document.body.movement = setTimeout(repeat, interval);
}

// 移动绝对定位元素
function moveFixElement(elementID, finalTop, finalRight, interval)
{
var elem = document.getElementById(elementID);
if (elem.movement){
clearTimeout(elem.movement);
}

finalTop = parseInt(finalTop);
finalRight = parseInt(finalRight);

var xpos = parseInt(elem.style.right);
var ypos = parseInt(elem.style.top);

var dist = 0 ;

if (xpos == finalRight && ypos == finalTop){
return true ;
}

if (xpos < finalRight){
dist = Math.ceil((finalRight - xpos) / 10 );
xpos += dist;
}
if (xpos > finalRight){
dist = Math.ceil((xpos - finalRight) / 10 );
xpos -= dist;
}

if (ypos < finalTop){
dist = Math.ceil((finalTop - ypos) / 10 );
ypos += dist;
}
if (ypos > finalTop){
dist = Math.ceil((ypos - finalTop) / 10 );
ypos -= dist;
}

elem.style.right = xpos + " px " ;
elem.style.top = ypos + " px " ;

var repeat = " moveFixElement(' " + elementID + " ', " + finalTop + " , " + finalRight + " , " + interval + " ) " ;
elem.movement = setTimeout(repeat, interval);
}

// 创建菜单
function createContent(id, mt, st, interval)
{
// 获取博文正文div容器
var elem = document.getElementById(id);
if ( ! elem) return false ;

// 获取div中所有元素结点
var nodes = elem.getElementsByTagName( " * " );

// 创建自定义列表
var dlist = document.createElement( " dl " );

// 创建div容器
var blogContent = document.createElement( " div " );
blogContent.setAttribute( " id " , " blogContent " );

var title = document.createElement( " div " );
title.setAttribute( " id " , " contentTitle " );

var dldiv = document.createElement( " div " );
dldiv.setAttribute( " id " , " dldiv " );

var num = 0 ;

// 遍历所有元素结点
for ( var i = 0 ; i < nodes.length; i ++ )
{
if (nodes[i].nodeName == mt || nodes[i].nodeName == st)
{
// 获取标题文本
var nodetext = nodes[i].firstChild.nodeValue;
// 插入锚
nodes[i].setAttribute( " id " , " blogTitle " + num);

switch (nodes[i].nodeName)
{
case mt: // 若为主标题
var item = document.createElement( " dt " );
break ;
case st: // 若为子标题
var item = document.createElement( " dd " );
break ;
}

// 创建锚链接
var itemtext = document.createTextNode(nodetext);
item.appendChild(itemtext);
item.setAttribute( " name " , num);
item.onclick = function (){ // 添加鼠标点击触发函数
var pos = getPos(document.getElementById( " blogTitle " + this .getAttribute( " name " )));
if ( ! moveWindow(pos.top, interval)) return false ;
};

// 将自定义表项加入自定义列表中
dlist.appendChild(item);
num ++ ;
}
}

if (num == 0 ) return false ;

// 将自定义列表加入文档中
dldiv.appendChild(dlist);
blogContent.appendChild(title);
blogContent.appendChild(dldiv);
document.body.appendChild(blogContent);

// 设置初始化位置
blogContent.style.right = 0 - dldiv.offsetWidth + " px " ;
blogContent.style.top = (window.innerHeight - title.offsetHeight) / 2 + " px " ;

// 点击 title,目录平滑伸缩
title.onclick = function (){
var blogContent = document.getElementById( " blogContent " );
var dldiv = document.getElementById( " dldiv " );
if (parseInt(blogContent.style.right) == 0 ){
moveFixElement( " blogContent " , blogContent.style.top, - dldiv.offsetWidth, 20 );
}
else {
moveFixElement( " blogContent " , blogContent.style.top, 0 , 20 );
}
};

// 添加窗口 resize 事件响应
window.onresize = function (){
var title = document.getElementById( " contentTitle " );
blogContent.style.top = (window.innerHeight - title.offsetHeight) / 2 + " px " ;
}
}

CSS 样式代码

  最终生成的标题菜单(content of titles)的 HTML 结构图如下所示:

  

  所以我们可为其编写如下的 CSS 代码:

 /*  三个div容器的样式  */  
#blogContent {
position : fixed ;
}
#contentTitle {
background-image : url(title.png) ;
background-position : -46px 0px ;
width : 46px ;
height : 148px ;
float : left ;
cursor : pointer ;
}
#dldiv {
float : left ;
font-family : '微软雅黑' ;
color : #3191B4 ;
background-color : #F1FAFB ;
border : 1px dotted #3191B4 ;
border-radius : 5px ;
}
/* 自定义列表样式 */
#dldiv dl {
margin : 8px 8px 8px 12px ;

}
#dldiv dd,dt {
cursor : pointer ;
}
#dldiv dd:hover, dt:hover {
color : #A7995A ;
}
#dldiv dt {
font-weight : bold ;
margin-top : 10px ;
font-size : 15px ;

}
#dldiv dd {
margin : 4px 15px ;
font-size : 12px ;
}

五、使用方法

写博

  最重要的当然是写博啦,如果不写博,那么这个工具就毫无存在的价值了。在写博添加标题时,首先用鼠标选定标题文字,然后点击编辑栏的样式设置菜单,选择 标题1-标题6 其中的一项。如下图所示:

  

  当然,主标题要比次级标题设得大一些,对我来说,一般选择 h2 作为主标题,h3 作为次级标题。

添加 JS 引用

  首先在 博客后台管理->设置->公告  中添加对 js 文件的引用,如下:

  

调用函数

  然后,在博文编辑器中点击 HTML 功能键,在博文的 HTML 源码中添加如下 js 代码:

  

  方法 createContent 即为生成标题目录的 js 函数,它有四个参数:

   "cnblogs_post_body" :表示包含博文正文的 div 容器的 id;

   "H2" :表示主标题的 HTML tag(需大写!);

   "H3" :表示次级标题的 HTML tag(需大写!);

   20 :表示平滑跳转时的速度,数值越大速度越快。

  一般来说第一个参数不用改,后面三个根据具体情况而定。

添加 CSS 样式

  最后,我们需要对生成的标题构成的目录设置样式,包括位置、底色、字体等等,我们在 后台管理->设置->通过 CSS 设置页面样式 中添加上面列出的 CSS 代码:

  

  最终的效果,你应该在浏览本篇博文时已经看到了。

六、结语

  这是笔者初学原生 javascript 的第一个小作品,欢迎园友试用,也欢迎提出改进建议!

http://www.cnblogs.com/hustlzp/archive/2011/08/22/a-js-tool-for-auto-blog-title-content-generate.html

https://files.cnblogs.com/hustlzp/createContent.js

作者: Leo_wl

    

出处: http://www.cnblogs.com/Leo_wl/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于原生 js 小工具 v1.1:自动生成博文目录,文内标题平滑跳转:欢迎园友试用!的详细内容...

  阅读:33次