【功能简要说明】
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);};目录连接
//为每一个章节标题顺序添加锚点标识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);目录样式
/*动态样式*/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(); } }})(); };最后