詳解自動生成博客目錄案例
前面的話
有朋友在博客下面留言,詢問博客目錄是如何生成的。接下來就詳細(xì)介紹實現(xiàn)過程
操作說明
關(guān)于博客目錄自動生成,已經(jīng)封裝成catalog.js文件,只要引用該文件即可
//默認(rèn)地,為頁面上所有的h3標(biāo)簽生成目錄
<script src=">
//或者,為頁面上所有class="test"的標(biāo)簽生成目錄
<script src="
如下圖所示,打開HTML源代碼編輯器,在最后引入js即可

【功能簡要說明】
1、點擊目錄項,對應(yīng)章節(jié)標(biāo)題將顯示在可視區(qū)上方
2、滾動滾輪,目錄項會對應(yīng)章節(jié)標(biāo)題的變化而相應(yīng)地變化
3、點擊目錄右上角的關(guān)閉按鈕,可以將目錄縮小為"顯示目錄"四個字,雙擊縮小后的目錄,可恢復(fù)默認(rèn)狀態(tài)
4、目錄可以拖拽至任意地方
目錄參照
首先,要確定的是,基于什么生成目錄。是文章中的<h3>標(biāo)簽,還是文章中的class="list"的標(biāo)簽。所以,更人性化的做法是,將其作為參數(shù),默認(rèn)參數(shù)為<h3>標(biāo)簽
由于博客園的博文除了自己生成的博客內(nèi)容外,博客園還會添加諸如評論、公告、廣告等元素。所以,第一步要先定位博文
博文最終都處于id="cnblogs_post_body"的div中

//DOM結(jié)構(gòu)穩(wěn)定后再操作
window.onload = function(){
/*設(shè)置章節(jié)標(biāo)題函數(shù)*/
function setCatalog(){
//獲取頁面中所有的script標(biāo)題
var aEle = document.getElementsByTagName('script');
//設(shè)置sel變量,用于保存其選擇符的字符串值
var sel;
//獲取script標(biāo)簽上的data-selector值
Array.prototype.forEach.call(aEle,function(item,index,array){
sel = item.getAttribute('data-selector');
if(sel) return;
})
//默認(rèn)參數(shù)為h3標(biāo)簽
if(sel == undefined){
sel ='h3';
}
//選取文章中所有的章節(jié)標(biāo)題
var tempArray = document.querySelectorAll(sel);
};
目錄連接
目錄如何與章節(jié)進行對應(yīng)呢,最常用的就是使用錨點。以基于文章中的<h3>標(biāo)簽生成目錄為例,為每一個<h3>標(biāo)簽按照順序添加錨點(#anchor1,#anchor2...)
//為每一個章節(jié)標(biāo)題順序添加錨點標(biāo)識
Array.prototype.forEach.call(tempArray, function(item, index, array) {
item.setAttribute('id','anchor' + (1+index));
});
目錄顯示
在文章左側(cè)顯示目錄,目錄顯示的內(nèi)容就是對應(yīng)章節(jié)的題目
//設(shè)置全局變量Atitle保存添加錨點標(biāo)識的標(biāo)題項
var aTitle = setCatalog();
/*生成目錄*/
function buildCatalog(arr){
//由于每個部件的創(chuàng)建過程都類似,所以寫成一個函數(shù)進行服用
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;
}
//取得章節(jié)標(biāo)題的個數(shù)
len = arr.length;
//創(chuàng)建最外層div
var oBox = buildPart({
selector:'div',
id:'box',
className:'box'
});
//創(chuàng)建關(guān)閉按鈕
buildPart({
selector:'span',
id:'boxQuit',
className:'box-quit',
innerHTML:'×',
appendToBox:true
});
//創(chuàng)建目錄標(biāo)題
buildPart({
selector:'h6',
className:'box-title',
innerHTML:'目錄',
appendToBox:true
});
//創(chuàng)建目錄項
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);
目錄樣式
為目錄設(shè)置樣式,最外層div設(shè)置最小寬度和最大寬度。當(dāng)目錄項太寬時,顯示...。由于最終要封裝為一個js文件,所以樣式采用動態(tài)樣式的形式
/*動態(tài)樣式*/
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};");
};
點擊事件
為各目錄項增加點擊事件,使用事件代理,增加性能
//由于點擊事件和滾輪事件都需要將目錄項發(fā)生樣式變化,所以聲明錨點激活函數(shù)
function anchorActive(obj){
var parent = obj.parentNode;
var aAnchor = parent.getElementsByTagName('a');
//將所有目錄項樣式設(shè)置為默認(rèn)狀態(tài)
Array.prototype.forEach.call(aAnchor,function(item,index,array){
item.className = 'box-anchor';
})
//將當(dāng)前目錄項樣式設(shè)置為點擊狀態(tài)
obj.className = 'box-anchor box-anchorActive';
}
var oBox = document.getElementById('box');
//設(shè)置目錄內(nèi)各組件的點擊事件
oBox.onclick = function(e){
e = e || event;
var target = e.target || e.srcElement;
//獲取target的href值
var sHref = target.getAttribute('href');
//設(shè)置目錄項的點擊事件
if(/anchor/.test(sHref)){
anchorActive(target);
}
}
隱藏功能
目錄有時是有用的,但有時又是礙事的。所以,為目錄添加一個關(guān)閉按鈕,使其隱藏,目錄內(nèi)容全部消失,關(guān)閉按鈕變成“顯示目錄”四個字。再次點擊則完全顯示
由于后續(xù)的拖拽功能需要使用點擊事件。所以,重新顯示目錄的事件使用雙擊實現(xiàn)
var oBox = document.getElementById('box');
//設(shè)置目錄內(nèi)各組件的點擊事件
oBox.onclick = function(e){
e = e || event;
var target = e.target || e.srcElement;
//設(shè)置關(guān)閉按鈕的點擊事件
if(target.id == 'boxQuit'){
target.innerHTML = '顯示目錄';
target.style.background = '#3399ff';
this.className = 'box boxHide';
}
}
//設(shè)置關(guān)閉按鈕的雙擊事件
var oBoxQuit = document.getElementById('boxQuit');
oBoxQuit.ondblclick = function(){
this.innerHTML = '×';
this.style.background = '';
this.parentNode.className = 'box';
}
滾輪功能
當(dāng)使用滾輪時,觸發(fā)滾輪事件,當(dāng)前目錄對應(yīng)可視區(qū)內(nèi)相應(yīng)的文章內(nèi)容
//設(shè)置滾輪事件
var wheel = function(e){
//獲取列表項
var aAnchor = oBox.getElementsByTagName('a');
//獲取章節(jié)題目項
aTitle.forEach(function(item,index,array){
//獲取當(dāng)前章節(jié)題目離可視區(qū)上側(cè)的距離
var iTop = item.getBoundingClientRect().top;
//獲取下一個章節(jié)題目
var oNext = array[index+1];
//如果存在下一個章節(jié)題目,則獲取下一個章節(jié)題目離可視區(qū)上側(cè)的距離
if(oNext){
var iNextTop = array[index+1].getBoundingClientRect().top;
}
//當(dāng)前章節(jié)題目離可視區(qū)上側(cè)的距離小于10時
if(iTop <= 10){
//當(dāng)下一個章節(jié)題目不存在, 或下一個章節(jié)題目離可視區(qū)上側(cè)的距離大于10時,設(shè)置當(dāng)前章節(jié)題目對應(yīng)的目錄項為激活態(tài)
if(iNextTop > 10 || !oNext){
anchorActive(aAnchor[index]);
}
}
});
}
document.body.onmousewheel = wheel;
document.body.addEventListener('DOMMouseScroll',wheel,false);
拖拽功能
由于不同計算機的分辨率不同,所以目錄的顯示位置也不同。為目錄增加一個拖拽功能,可以把其放在任意合適的地方
//拖拽實現(xiàn)
oBox.onmousedown = function(e){
e = e || event;
//獲取元素距離定位父級的x軸及y軸距離
var x0 = this.offsetLeft;
var y0 = this.offsetTop;
//獲取此時鼠標(biāo)距離視口左上角的x軸及y軸距離
var x1 = e.clientX;
var y1 = e.clientY;
document.onmousemove = function(e){
e = e || event;
//獲取此時鼠標(biāo)距離視口左上角的x軸及y軸距離
x2 = e.clientX;
y2 = e.clientY;
//計算此時元素應(yīng)該距離視口左上角的x軸及y軸距離
var X = x0 + (x2 - x1);
var Y = y0 + (y2 - y1);
//將X和Y的值賦給left和top,使元素移動到相應(yīng)位置
oBox.style.left = X + 'px';
oBox.style.top = Y + 'px';
}
document.onmouseup = function(e){
//當(dāng)鼠標(biāo)抬起時,拖拽結(jié)束,則將onmousemove賦值為null即可
document.onmousemove = null;
//釋放全局捕獲
if(oBox.releaseCapture){
oBox.releaseCapture();
}
}
//阻止默認(rèn)行為
return false;
//IE8-瀏覽器阻止默認(rèn)行為
if(oBox.setCapture){
oBox.setCapture();
}
}
代碼展示
//DOM結(jié)構(gòu)穩(wěn)定后,再操作
window.onload = function(){
/*動態(tài)樣式*/
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};");
};
/*設(shè)置章節(jié)標(biāo)題函數(shù)*/
function setCatalog(){
//獲取頁面中所有的script標(biāo)題
var aEle = document.getElementsByTagName('script');
//設(shè)置sel變量,用于保存其選擇符的字符串值
var sel;
//獲取script標(biāo)簽上的data-selector值
Array.prototype.forEach.call(aEle,function(item,index,array){
sel = item.getAttribute('data-selector');
if(sel) return;
})
//默認(rèn)參數(shù)為h3標(biāo)簽
if(sel == undefined){
sel ='h3';
}
//選取博文
var article = document.getElementById('cnblogs_post_body');
//選取文章中所有的章節(jié)標(biāo)題
var tempArray = article.querySelectorAll(sel);
//為每一個章節(jié)標(biāo)題順序添加錨點標(biāo)識
Array.prototype.forEach.call(tempArray, function(item, index, array) {
item.setAttribute('id','anchor' + (1+index));
});
//返回章節(jié)標(biāo)題這個類數(shù)組
return tempArray;
}
//設(shè)置全局變量Atitle保存添加錨點標(biāo)識的標(biāo)題項
var aTitle = setCatalog();
/*生成目錄*/
function buildCatalog(arr){
//由于每個部件的創(chuàng)建過程都類似,所以寫成一個函數(shù)進行服用
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;
}
//取得章節(jié)標(biāo)題的個數(shù)
len = arr.length;
//創(chuàng)建最外層div
var oBox = buildPart({
selector:'div',
id:'box',
className:'box'
});
//創(chuàng)建關(guān)閉按鈕
buildPart({
selector:'span',
id:'boxQuit',
className:'box-quit',
innerHTML:'×',
appendToBox:true
});
//創(chuàng)建目錄標(biāo)題
buildPart({
selector:'h6',
className:'box-title',
innerHTML:'目錄',
appendToBox:true
});
//創(chuàng)建目錄項
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');
//設(shè)置目錄內(nèi)各組件的點擊事件
oBox.onclick = function(e){
e = e || event;
var target = e.target || e.srcElement;
//設(shè)置關(guān)閉按鈕的點擊事件
if(target.id == 'boxQuit'){
target.innerHTML = '顯示目錄';
target.className = 'box-quit box-quitAnother'
this.className = 'box boxHide';
}
//獲取target的href值
var sHref = target.getAttribute('href');
//設(shè)置目錄項的點擊事件
if(/anchor/.test(sHref)){
anchorActive(target);
}
}
/*設(shè)置關(guān)閉按鈕的雙擊事件*/
var oBoxQuit = document.getElementById('boxQuit');
oBoxQuit.ondblclick = function(){
this.innerHTML = '×';
this.className = 'box-quit';
this.parentNode.className = 'box';
}
//由于點擊事件和滾輪事件都需要將目錄項發(fā)生樣式變化,所以聲明錨點激活函數(shù)
function anchorActive(obj){
var parent = obj.parentNode;
var aAnchor = parent.getElementsByTagName('a');
//將所有目錄項樣式設(shè)置為默認(rèn)狀態(tài)
Array.prototype.forEach.call(aAnchor,function(item,index,array){
item.className = 'box-anchor';
})
//將當(dāng)前目錄項樣式設(shè)置為點擊狀態(tài)
obj.className = 'box-anchor box-anchorActive';
}
//設(shè)置滾輪事件
var wheel = function(e){
//獲取列表項
var aAnchor = oBox.getElementsByTagName('a');
//獲取章節(jié)題目項
aTitle.forEach(function(item,index,array){
//獲取當(dāng)前章節(jié)題目離可視區(qū)上側(cè)的距離
var iTop = item.getBoundingClientRect().top;
//獲取下一個章節(jié)題目
var oNext = array[index+1];
//如果存在下一個章節(jié)題目,則獲取下一個章節(jié)題目離可視區(qū)上側(cè)的距離
if(oNext){
var iNextTop = array[index+1].getBoundingClientRect().top;
}
//當(dāng)前章節(jié)題目離可視區(qū)上側(cè)的距離小于10時
if(iTop <= 10){
//當(dāng)下一個章節(jié)題目不存在, 或下一個章節(jié)題目離可視區(qū)上側(cè)的距離大于10時,設(shè)置當(dāng)前章節(jié)題目對應(yīng)的目錄項為激活態(tài)
if(iNextTop > 10 || !oNext){
anchorActive(aAnchor[index]);
}
}
});
}
document.body.onmousewheel = wheel;
document.body.addEventListener('DOMMouseScroll',wheel,false);
//拖拽實現(xiàn)
oBox.onmousedown = function(e){
e = e || event;
//獲取元素距離定位父級的x軸及y軸距離
var x0 = this.offsetLeft;
var y0 = this.offsetTop;
//獲取此時鼠標(biāo)距離視口左上角的x軸及y軸距離
var x1 = e.clientX;
var y1 = e.clientY;
document.onmousemove = function(e){
e = e || event;
//獲取此時鼠標(biāo)距離視口左上角的x軸及y軸距離
x2 = e.clientX;
y2 = e.clientY;
//計算此時元素應(yīng)該距離視口左上角的x軸及y軸距離
var X = x0 + (x2 - x1);
var Y = y0 + (y2 - y1);
//將X和Y的值賦給left和top,使元素移動到相應(yīng)位置
oBox.style.left = X + 'px';
oBox.style.top = Y + 'px';
}
document.onmouseup = function(e){
//當(dāng)鼠標(biāo)抬起時,拖拽結(jié)束,則將onmousemove賦值為null即可
document.onmousemove = null;
//釋放全局捕獲
if(oBox.releaseCapture){
oBox.releaseCapture();
}
}
//阻止默認(rèn)行為
return false;
//IE8-瀏覽器阻止默認(rèn)行為
if(oBox.setCapture){
oBox.setCapture();
}
}
})();
};
最后
如果有自己的需求,可以把代碼下載下來,進行相應(yīng)參數(shù)的修改
如果點擊右鍵,會出現(xiàn)自定義右鍵菜單,包括回到頂部、點贊、評論這三個功能;如果按住ctrl鍵,再點擊右鍵,則出現(xiàn)原生的右鍵菜單。
以上就是本文的全部內(nèi)容,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作能帶來一定的幫助,同時也希望多多支持腳本之家!
- 讓你的博文自動帶上縮址的實現(xiàn)代碼,方便發(fā)到微博客上
- 用ajax自動加載blogjava和博客園的rss
- Linux下SVN服務(wù)器自動更新文件到Web目錄的方法
- rsync備份時自動創(chuàng)建目錄的方法
- 使用visual studio自動創(chuàng)建IIS虛擬目錄
- Windows下實現(xiàn)MySQL自動備份的批處理(復(fù)制目錄或mysqldump備份)
- php自動獲取目錄下的模板的代碼
- 使用FTP下載目錄,即FTP命令批量自動下載的bat文件
- 使用ADSI、ASP和一對魔術(shù)戲法自動地創(chuàng)立一個虛擬目錄的方法
- 一個可以自動創(chuàng)建多級目錄的函數(shù)
相關(guān)文章
通過JS動態(tài)創(chuàng)建一個html DOM元素并顯示
需要通過點擊某個元素后, 動態(tài)創(chuàng)建一個DOM元素并顯示,因此寫了一些相關(guān)的js函數(shù),在此記錄下2014-10-10
javascript下function聲明一些小結(jié)
function聲明一些東西,我們都知道function和var一樣是預(yù)處理的在js里面,但是到底什么是函數(shù)聲明呢,我們來看幾個例子2007-12-12
一文詳解JavaScript?如何將?HTML?轉(zhuǎn)成?Markdown
這篇文章主要介紹了一文詳解JavaScript如何將HTML轉(zhuǎn)成Markdown,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價值,需要的小伙伴可以參考一下2022-08-08
基于js與flash實現(xiàn)的網(wǎng)站flv視頻播放插件代碼
這篇文章主要介紹了基于js與flash實現(xiàn)的網(wǎng)站flv視頻播放插件代碼,該功能在很多網(wǎng)站上都有著廣泛的應(yīng)用,本文以實例形式對其進行介紹,需要的朋友可以參考下2014-10-10
JavaScript實現(xiàn)Sleep函數(shù)的代碼
大家知道,JavaScript中沒有內(nèi)置我們常用的sleep()函數(shù),只有定時器setTimeout()和循環(huán)定時器setInterval()2007-03-03

