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

网友投稿 249 2023-06-25


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

前面的话

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

操作说明

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

//默认地,为页面上所有的h3标签生成目录

   

    //或者,为页面上所有class="test"的标签生成目录

   

 如下图所示,打开HTML源代码编辑器,在最后引入js即可

【功能简要zfEKMhyNoW说明】

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

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

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

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

目录参照

首先,要确定的是,基于什么生成目录。是文章中的

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

博文最终都处于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);

};

目录连接

目录如何与章节进行对应呢,最常用的就是使用锚点。以基于文章中的

//为每一个章节标题顺序添加锚点标识

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键,再点击右键,则出现原生的右键菜单。


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:详解Java豆瓣电影爬虫——小爬虫成长记(附源码)
下一篇:详解基于java的Socket聊天程序——服务端(附demo)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~