详解自动生成博客目录案例

前面的话

有朋友在博客下面留言,询问博客目录是如何生成的。接下来就详细介绍实现过程

操作说明

关于博客目录自动生成,已经封装成catalog.js文件,只要引用该文件即可

//默认地,为页面上所有的h3标签生成目录
    <script src="http://files.cnblogs.com/files/xiaohuochai/catalog.js"></script>
    //或者,为页面上所有class="test"的标签生成目录
    <script src="http://files.cnblogs.com/files/xiaohuochai/catalog.js" data-seletor=".test"></script>
 如下图所示,打开HTML源代码编辑器,在最后引入js即可

【功能简要说明】

1、点击目录项,对应章节标题将显示在可视区上方

2、滚动滚轮,目录项会对应章节标题的变化而相应地变化

3、点击目录右上角的关闭按钮,可以将目录缩小为"显示目录"四个字,双击缩小后的目录,可恢复默认状态

4、目录可以拖拽至任意地方

目录参照

首先,要确定的是,基于什么生成目录。是文章中的<h3>标签,还是文章中的class="list"的标签。所以,更人性化的做法是,将其作为参数,默认参数为<h3>标签

由于博客园的博文除了自己生成的博客内容外,博客园还会添加诸如评论、公告、广告等元素。所以,第一步要先定位博文

博文最终都处于id="cnblogs_post_body"的div中

//DOM结构稳定后再操作
window.onload = function(){
 /*设置章节标题函数*/
 function setCatalog(){
 //获取页面中所有的script标题
 var aEle = document.getElementsByTagName('script');
 //设置sel变量,用于保存其选择符的字符串值
 var sel;
 //获取script标签上的data-selector值
 Array.prototype.forEach.call(aEle,function(item,index,array){
 sel = item.getAttribute('data-selector');
 if(sel) return;
 })
 //默认参数为h3标签
 if(sel == undefined){
 sel ='h3';
 }
 //选取文章中所有的章节标题
 var tempArray = document.querySelectorAll(sel);
};

目录连接

  目录如何与章节进行对应呢,最常用的就是使用锚点。以基于文章中的<h3>标签生成目录为例,为每一个<h3>标签按照顺序添加锚点(#anchor1,#anchor2...)

//为每一个章节标题顺序添加锚点标识
Array.prototype.forEach.call(tempArray, function(item, index, array) {
 item.setAttribute('id','anchor' + (1+index));
});

目录显示

在文章左侧显示目录,目录显示的内容就是对应章节的题目

//设置全局变量Atitle保存添加锚点标识的标题项
 var aTitle = setCatalog();
 /*生成目录*/
 function buildCatalog(arr){
 //由于每个部件的创建过程都类似,所以写成一个函数进行服用
 function buildPart(json){
 var oPart = document.createElement(json.selector);
 if(json.id){oPart.setAttribute('id',json.id);}
 if(json.className){oPart.className = json.className;}
 if(json.innerHTML){oPart.innerHTML = json.innerHTML;}
 if(json.href){oPart.setAttribute('href',json.href);}
 if(json.appendToBox){
 oBox.appendChild(oPart);
 }
 return oPart;
 }
 //取得章节标题的个数
 len = arr.length;
 //创建最外层div
 var oBox = buildPart({
 selector:'div',
 id:'box',
 className:'box'
 });
 //创建关闭按钮
 buildPart({
 selector:'span',
 id:'boxQuit',
 className:'box-quit',
 innerHTML:'×',
 appendToBox:true
 });
 //创建目录标题
 buildPart({
 selector:'h6',
 className:'box-title',
 innerHTML:'目录',
 appendToBox:true
 });
 //创建目录项
 for(var i = 0; i < len; i++){
 buildPart({
 selector:'a',
 className:'box-anchor',
 href:'#anchor' + (1+i),
 innerHTML:'['+(i+1)+']'+arr[i].innerHTML,
 appendToBox:true
 });
 }
 //将目录加入文档中
 document.body.appendChild(oBox);
 }
 buildCatalog(aTitle);

目录样式

  为目录设置样式,最外层div设置最小宽度和最大宽度。当目录项太宽时,显示...。由于最终要封装为一个js文件,所以样式采用动态样式的形式

/*动态样式*/
function loadStyles(str){
 loadStyles.mark = 'load';
 var style = document.createElement("style");
 style.type = "text/css";
 try{
 style.innerHTML = str;
 }catch(ex){
 style.styleSheet.cssText = str;
 }
 var head = document.getElementsByTagName('head')[0];
 head.appendChild(style);
}
if(loadStyles.mark != 'load'){
 loadStyles("h6{margin:0;padding:0;}\
 .box{position: fixed; left: 10px;top: 60px;font:16px/30px '宋体'; border: 2px solid #ccc;padding: 4px; border-radius:5px;min-width:80px;max-width:118px;overflow:hidden;cursor:default;}\
 .boxHide{border:none;width:60px;height:30px;padding:0;}\
 .box-title{text-align:center;font-size:20px;color:#ccc;}\
 .box-quit{position: absolute; right: 0;top: 4px;cursor:pointer;font-weight:bold;}\
 .box-anchor{display:block;text-decoration:none;color:black; border-left: 3px solid transparent;padding:0 3px;margin-bottom: 3px;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;}\
 .box-anchor:hover{color:#3399ff;}\
 .box-anchorActive{color:#3399ff;text-decoration:underline;border-color:#2175bc};");
};

点击事件

为各目录项增加点击事件,使用事件代理,增加性能

//由于点击事件和滚轮事件都需要将目录项发生样式变化,所以声明锚点激活函数
function anchorActive(obj){
 var parent = obj.parentNode;
 var aAnchor = parent.getElementsByTagName('a');
 //将所有目录项样式设置为默认状态
 Array.prototype.forEach.call(aAnchor,function(item,index,array){
 item.className = 'box-anchor';
 })
 //将当前目录项样式设置为点击状态
 obj.className = 'box-anchor box-anchorActive';
}
var oBox = document.getElementById('box');
//设置目录内各组件的点击事件
oBox.onclick = function(e){
 e = e || event;
 var target = e.target || e.srcElement;
 //获取target的href值
 var sHref = target.getAttribute('href');
 //设置目录项的点击事件
 if(/anchor/.test(sHref)){
 anchorActive(target);
 }
}

隐藏功能

目录有时是有用的,但有时又是碍事的。所以,为目录添加一个关闭按钮,使其隐藏,目录内容全部消失,关闭按钮变成“显示目录”四个字。再次点击则完全显示

  由于后续的拖拽功能需要使用点击事件。所以,重新显示目录的事件使用双击实现

var oBox = document.getElementById('box');
//设置目录内各组件的点击事件
oBox.onclick = function(e){
 e = e || event;
 var target = e.target || e.srcElement;
 //设置关闭按钮的点击事件
 if(target.id == 'boxQuit'){
 target.innerHTML = '显示目录';
 target.style.background = '#3399ff';
 this.className = 'box boxHide';
 }
}
//设置关闭按钮的双击事件
var oBoxQuit = document.getElementById('boxQuit');
oBoxQuit.ondblclick = function(){
 this.innerHTML = '×';
 this.style.background = '';
 this.parentNode.className = 'box';
}

滚轮功能

当使用滚轮时,触发滚轮事件,当前目录对应可视区内相应的文章内容

//设置滚轮事件
var wheel = function(e){
 //获取列表项
 var aAnchor = oBox.getElementsByTagName('a');
 //获取章节题目项
 aTitle.forEach(function(item,index,array){
 //获取当前章节题目离可视区上侧的距离
 var iTop = item.getBoundingClientRect().top;
 //获取下一个章节题目
 var oNext = array[index+1];
 //如果存在下一个章节题目,则获取下一个章节题目离可视区上侧的距离
 if(oNext){
 var iNextTop = array[index+1].getBoundingClientRect().top;
 }
 //当前章节题目离可视区上侧的距离小于10时
 if(iTop <= 10){
 //当下一个章节题目不存在, 或下一个章节题目离可视区上侧的距离大于10时,设置当前章节题目对应的目录项为激活态
 if(iNextTop > 10 || !oNext){
 anchorActive(aAnchor[index]);
 }
 }
 });
}
document.body.onmousewheel = wheel;
document.body.addEventListener('DOMMouseScroll',wheel,false);

拖拽功能

由于不同计算机的分辨率不同,所以目录的显示位置也不同。为目录增加一个拖拽功能,可以把其放在任意合适的地方

//拖拽实现
oBox.onmousedown = function(e){
 e = e || event;
 //获取元素距离定位父级的x轴及y轴距离
 var x0 = this.offsetLeft;
 var y0 = this.offsetTop;
 //获取此时鼠标距离视口左上角的x轴及y轴距离
 var x1 = e.clientX;
 var y1 = e.clientY;
 document.onmousemove = function(e){
 e = e || event;
 //获取此时鼠标距离视口左上角的x轴及y轴距离
 x2 = e.clientX;
 y2 = e.clientY;
 //计算此时元素应该距离视口左上角的x轴及y轴距离
 var X = x0 + (x2 - x1);
 var Y = y0 + (y2 - y1);
 //将X和Y的值赋给left和top,使元素移动到相应位置
 oBox.style.left = X + 'px';
 oBox.style.top = Y + 'px';
 }
 document.onmouseup = function(e){
 //当鼠标抬起时,拖拽结束,则将onmousemove赋值为null即可
 document.onmousemove = null;
 //释放全局捕获
 if(oBox.releaseCapture){
 oBox.releaseCapture();
 }
 }
 //阻止默认行为
 return false;
 //IE8-浏览器阻止默认行为
 if(oBox.setCapture){
 oBox.setCapture();
 }
}

代码展示

//DOM结构稳定后,再操作
window.onload = function(){
 /*动态样式*/
 function loadStyles(str){
 loadStyles.mark = 'load';
 var style = document.createElement("style");
 style.type = "text/css";
 try{
 style.innerHTML = str;
 }catch(ex){
 style.styleSheet.cssText = str;
 }
 var head = document.getElementsByTagName('head')[0];
 head.appendChild(style);
 }
 if(loadStyles.mark != 'load'){
 loadStyles("h6{margin:0;padding:0;}\
 .box{position: fixed; left: 10px;top: 60px;font:16px/30px '宋体'; border: 2px solid #ccc;padding: 4px; border-radius:5px;min-width:80px;max-width:118px;overflow:hidden;cursor:default;background:rgba(0,0,0,0.1);}\
 .boxHide{border:none;width:60px;height:30px;padding:0;}\
 .box-title{text-align:center;font-size:20px;color:#444;}\
 .box-quit{position: absolute;text-align:center; right: 0;top: 4px;cursor:pointer;font-weight:bold;}\
 .box-quitAnother{background:#3399ff;left:0;top:0;}\
 a.box-anchor{display:block;text-decoration:none;color:black; border-left: 3px solid transparent;padding:0 3px;margin-bottom: 3px;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;}\
 a.box-anchor:hover{color:#3399ff;}\
 a.box-anchorActive{color:#3399ff;text-decoration:underline;border-color:#2175bc};");
 };
 /*设置章节标题函数*/
 function setCatalog(){
 //获取页面中所有的script标题
 var aEle = document.getElementsByTagName('script');
 //设置sel变量,用于保存其选择符的字符串值
 var sel;
 //获取script标签上的data-selector值
 Array.prototype.forEach.call(aEle,function(item,index,array){
 sel = item.getAttribute('data-selector');
 if(sel) return;
 })
 //默认参数为h3标签
 if(sel == undefined){
 sel ='h3';
 }
 //选取博文
 var article = document.getElementById('cnblogs_post_body');
 //选取文章中所有的章节标题
 var tempArray = article.querySelectorAll(sel);
 //为每一个章节标题顺序添加锚点标识
 Array.prototype.forEach.call(tempArray, function(item, index, array) {
 item.setAttribute('id','anchor' + (1+index));
 });
 //返回章节标题这个类数组
 return tempArray;
 }
 //设置全局变量Atitle保存添加锚点标识的标题项
 var aTitle = setCatalog();

 /*生成目录*/
 function buildCatalog(arr){
 //由于每个部件的创建过程都类似,所以写成一个函数进行服用
 function buildPart(json){
 var oPart = document.createElement(json.selector);
 if(json.id){oPart.setAttribute('id',json.id);}
 if(json.className){oPart.className = json.className;}
 if(json.innerHTML){oPart.innerHTML = json.innerHTML;}
 if(json.href){oPart.setAttribute('href',json.href);}
 if(json.appendToBox){
 oBox.appendChild(oPart);
 }
 return oPart;
 }
 //取得章节标题的个数
 len = arr.length;
 //创建最外层div
 var oBox = buildPart({
 selector:'div',
 id:'box',
 className:'box'
 });
 //创建关闭按钮
 buildPart({
 selector:'span',
 id:'boxQuit',
 className:'box-quit',
 innerHTML:'×',
 appendToBox:true
 });
 //创建目录标题
 buildPart({
 selector:'h6',
 className:'box-title',
 innerHTML:'目录',
 appendToBox:true
 });
 //创建目录项
 for(var i = 0; i < len; i++){
 buildPart({
 selector:'a',
 className:'box-anchor',
 href:'#anchor' + (1+i),
 innerHTML:'['+(i+1)+']'+arr[i].innerHTML,
 appendToBox:true
 });
 }
 //将目录加入文档中
 document.body.appendChild(oBox);
 }
 buildCatalog(aTitle);
 /*事件部分*/
 (function(){
 var oBox = document.getElementById('box');
 //设置目录内各组件的点击事件
 oBox.onclick = function(e){
 e = e || event;
 var target = e.target || e.srcElement;
 //设置关闭按钮的点击事件
 if(target.id == 'boxQuit'){
 target.innerHTML = '显示目录';
 target.className = 'box-quit box-quitAnother'
 this.className = 'box boxHide';
 }
 //获取target的href值
 var sHref = target.getAttribute('href');
 //设置目录项的点击事件
 if(/anchor/.test(sHref)){
 anchorActive(target);
 }
 }
 /*设置关闭按钮的双击事件*/
 var oBoxQuit = document.getElementById('boxQuit');
 oBoxQuit.ondblclick = function(){
 this.innerHTML = '×';
 this.className = 'box-quit';
 this.parentNode.className = 'box';
 }
 //由于点击事件和滚轮事件都需要将目录项发生样式变化,所以声明锚点激活函数
 function anchorActive(obj){
 var parent = obj.parentNode;
 var aAnchor = parent.getElementsByTagName('a');
 //将所有目录项样式设置为默认状态
 Array.prototype.forEach.call(aAnchor,function(item,index,array){
 item.className = 'box-anchor';
 })
 //将当前目录项样式设置为点击状态
 obj.className = 'box-anchor box-anchorActive';
 }
 //设置滚轮事件
 var wheel = function(e){
 //获取列表项
 var aAnchor = oBox.getElementsByTagName('a');
 //获取章节题目项
 aTitle.forEach(function(item,index,array){
 //获取当前章节题目离可视区上侧的距离
 var iTop = item.getBoundingClientRect().top;
 //获取下一个章节题目
 var oNext = array[index+1];
 //如果存在下一个章节题目,则获取下一个章节题目离可视区上侧的距离
 if(oNext){
 var iNextTop = array[index+1].getBoundingClientRect().top;
 }
 //当前章节题目离可视区上侧的距离小于10时
 if(iTop <= 10){
 //当下一个章节题目不存在, 或下一个章节题目离可视区上侧的距离大于10时,设置当前章节题目对应的目录项为激活态
 if(iNextTop > 10 || !oNext){
 anchorActive(aAnchor[index]);
 }
 }
 });
 }
 document.body.onmousewheel = wheel;
 document.body.addEventListener('DOMMouseScroll',wheel,false);
 //拖拽实现
 oBox.onmousedown = function(e){
 e = e || event;
 //获取元素距离定位父级的x轴及y轴距离
 var x0 = this.offsetLeft;
 var y0 = this.offsetTop;
 //获取此时鼠标距离视口左上角的x轴及y轴距离
 var x1 = e.clientX;
 var y1 = e.clientY;
 document.onmousemove = function(e){
 e = e || event;
 //获取此时鼠标距离视口左上角的x轴及y轴距离
 x2 = e.clientX;
 y2 = e.clientY;
 //计算此时元素应该距离视口左上角的x轴及y轴距离
 var X = x0 + (x2 - x1);
 var Y = y0 + (y2 - y1);
 //将X和Y的值赋给left和top,使元素移动到相应位置
 oBox.style.left = X + 'px';
 oBox.style.top = Y + 'px';
 }
 document.onmouseup = function(e){
 //当鼠标抬起时,拖拽结束,则将onmousemove赋值为null即可
 document.onmousemove = null;
 //释放全局捕获
 if(oBox.releaseCapture){
 oBox.releaseCapture();
 }
 }
 //阻止默认行为
 return false;
 //IE8-浏览器阻止默认行为
 if(oBox.setCapture){
 oBox.setCapture();
 }
 }
 })();
};

最后

 如果有自己的需求,可以把代码下载下来,进行相应参数的修改

 如果点击右键,会出现自定义右键菜单,包括回到顶部、点赞、评论这三个功能;如果按住ctrl键,再点击右键,则出现原生的右键菜单。

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持我们!

(0)

相关推荐

  • 使用visual studio自动创建IIS虚拟目录

    1, 在web属性中,点击创建"Create Virtual Directory". 2, 在IIS中将显示创建好的虚拟目录 注意:如果虚拟目录层数大于1,则需要在C:\inetpub\wwwroot中手动创建对应的子目录.

  • php自动获取目录下的模板的代码

    目录下必须有default.gif(此图为模板缩略图)的才为合法的模板 复制代码 代码如下: function get_template () { $template = array (); $dir = CMS_ROOT.'/tpl/'; $n = 0; if (is_dir($dir)) { if ($dh = opendir($dir)) { while (($file = readdir($dh)) !== false) { if ($file =='.' or $file == '..

  • Windows下实现MySQL自动备份的批处理(复制目录或mysqldump备份)

    今天有个需求要在 Windows 下实现数据库自动备份,拼凑了一下解决办法. 实现的特性 可指定多个数据库 按照 年/月/日 的方式组织备份 可选的使用 WinRAR 压缩备份 使用计划任务实现定时备份 具体代码 备份 复制代码 代码如下: @echo off & setlocal ENABLEEXTENSIONS :: ---------- 配置项 ---------- :: 备份放置的路径,加 \ set BACKUP_PATH=D:\Backup\ :: 要备份的数据库名称,多个用空格分隔

  • 一个可以自动创建多级目录的函数

    <% '创建多级目录,可以创建不存在的根目录 '参数:要创建的目录名称,可以是多级 '返回逻辑值,True成功,False失败 '创建目录的根目录从当前目录开始 '--------------------------------------------------- Function CreateMultiFolder(ByVal CFolder)     Dim objFSO, PhCreateFolder, CreateFolderArray, CreateFolder     Dim i,

  • rsync备份时自动创建目录的方法

    一直以来都习惯用 rsync + ssh 作服务器文件同步,但之前都没有在命令参数里面找到类似 mkdir -p 的功能,可以在同步某个指定文件的时候自动在目标服务器上根据需要创建完整的父级目录.比如某些按年月日自动生成的目录型文件需要在文件生成的时候即时同步: 复制代码 代码如下: rsync -lprztW --rsh="/usr/bin/ssh -l rsyncuser" /var/2007/12/13/1001.gif 10.0.0.1:/var/2007/12/13/1001

  • Linux下SVN服务器自动更新文件到Web目录的方法

    说明: 服务器操作系统:CentOS 服务器IP:192.168.21.134 SVN安装路径:/usr/local/svn SVN项目路径:/home/svn SVN登录账号:osyunwei SVN登录密码:123456 Web站点根目录:/home/web 实现目的: 当svn项目中有任何修改更新时,系统会自动实时从svn中检出文件并同步到Web站点根目录 具体操作: 一.使用SVN中post-commit实现自动实时从svn中检出文件并同步到Web站点根目录 cd /home/svn/h

  • 让你的博文自动带上缩址的实现代码,方便发到微博客上

    复制代码 代码如下: <script type="text/javascript"> $(function(){ c_url = 'http://s8.hk:8088/s8/s?format=text&longUrl='; c_url += document.location.href; c_url += '&jsonp=?' $.getJSON(c_url, function(data){ $("<div>本文短址:</div&

  • 用ajax自动加载blogjava和博客园的rss

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>this is test</title> <sc

  • 使用FTP下载目录,即FTP命令批量自动下载的bat文件

    FTP不支持目录的下载,不过FTP命令提供了mget命令,允许多文件下载,但每下载一个文件都需要确认,不能自动完成.本文主要介绍使用批处理,结合FTP的相关命令来实现批量文件的下载,以便达到目录下载的目的. 大致想法如下: ftp -s:filename hosts >result.txt 执行脚本,并将结果定向输出到result.txt脚本内容,如cd mydirlsbye可以将mydir命令列出然后使用批处理分析result.txt从150 Opening ASCII mode data c

  • 使用ADSI、ASP和一对魔术戏法自动地创立一个虚拟目录的方法

    使用ADSI.ASP和一对魔术戏法自动地创立一个虚拟目录,第二部分 Tom Moran 和 Jeff Sandquist 1999年2月22日 我们回来了.大家翘首以盼的这篇文章的第二部分现在在这儿.并且我想你将花费整个晚上看你的猫清洗自己. 这个月,Jeff Sandquist和我完成了我们的动态目录服务接口(Active Directory Services Interfaces ,ADSI)应用程序.如果你来晚了一点,没有问题:只要见这个系列的Part 1 http://www.micro

随机推荐