咱們在寫博客的時候,若是博文裏面有目錄,會給人結構清晰、一種一目瞭然的感受,看目錄就知道這篇博文要講解的內容,而且點擊目錄標題就能夠跳轉到具體的內容,這樣園友們在看博客的時候就能夠很方便地瀏覽本身感興趣的內容,可是遺憾的是博客園不支持博文目錄的生成,好像也有園友給博客園提建議,但願可以像CSDN那樣可以自動生成博文目錄,可是不知道是什麼緣由,博客園一直都沒有把這個功能加上去,既然沒有,那我就本身作吧,研究了2天,總算是按照本身的設想作出來了,最終效果以下:javascript
下面來介紹一下這個小工具的實現。html
這個小工具要實現的核心功能只有兩個:java
1.自動生成博文目錄。node
2.點擊目錄標題定位到標題對應的具體內容,就像使用word生成的目錄那樣。正則表達式
小工具的第一個核心功能,就是經過代碼自動抽取標題,而後再包裝一下插入文檔中。基本實現原理:首先要求博主在寫博文的時候,將主標題和次級標題用 HTML標籤中的 title tag(如:h一、h二、h3...)包起來;算法
好比"1.一、自動生成博文目錄"這個次級標題如今是段落的形式瀏覽器
將"1.一、自動生成博文目錄"設置成"標題3"app
而後經過 JS 代碼遍歷整個包含博文正文的 <div>,過濾出這些標籤,再把它們組裝成自定義列表的形式,再插入到HTML文檔中,以下:ide
1 <dl> 2 <dt>1、JAVA流式輸入/輸出原理</dt> 3 <dt>2、輸入輸出流分類</dt> 4 <dt>3、節點流和處理流</dt> 5 <dd>3.1.節點流類型</dd> 6 <dd>3.2.處理流類型</dd> 7 <dt>4、InputStream(輸入流)</dt> 8 <dd>4.1.InputStream的基本方法</dd> 9 <dt>5、OutputStream(輸出流)</dt> 10 <dd>5.1.OutputStream的基本方法</dd> 11 <dt>6、Reader流</dt> 12 <dd>6.1.Reader的基本方法</dd> 13 <dt>7、Writer流</dt> 14 <dd>7.1.Writer的基本方法</dd> 15 <dt>8、節點流講解</dt> 16 <dt>9、處理流講解</dt> 17 <dd>9.1.第一種處理流——緩衝流(Buffering)</dd> 18 <dd>9.2.第二種處理流——轉換流</dd> 19 <dd>9.3.第三種處理流——數據流</dd> 20 <dd>9.4.打印流——Print</dd> 21 <dd>9.5. 對象流——Object</dd> 22 <dt>10、IO流總結</dt> 23 </dl>
若是不加任何樣式,它在瀏覽器中的默認顯示效果以下:函數
常規實現方式:使用HTML的錨點連接
錨點也能夠理解成爲一種超連接,只不過它是網頁內部的超連接
好比咱們有一個網頁很長很長,並且裏面的內容,能夠分爲N個部分。這樣的話,咱們就能夠在網頁的頂部設置一些錨點,這樣即可以方便瀏覽者點擊相應的錨點,到達本頁內相應的位置,而沒必要在一個很長的網頁裏自行尋找。
範例:使用HTML的錨點連接定位到具體的內容
1 <html> 2 <head> 3 <title>使用HTML的錨點連接定位到具體的內容</title> 4 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> 5 </head> 6 <body> 7 <h1>目錄導航</h1> 8 <dl> 9 <!--設置錨點:<a href="#title0">1、JAVA流式輸入/輸出原理</a> 10 點擊這個超連接時就會自動定位到<h2 id="title0" name="title0">1、JAVA流式輸入/輸出原理</h2>這個標題所在的位置 11 --> 12 <dt><a href="#title0">1、JAVA流式輸入/輸出原理</a></dt> 13 <dt><a href="#title1">2、輸入輸出流分類</a></dt> 14 <dt><a href="#title2">3、節點流和處理流</a></dt> 15 <dd><a href="#title3">3.1.節點流類型</a></dd> 16 <dd><a href="#title4">3.2.處理流類型</a></dd> 17 <dt><a href="#title5">4、InputStream(輸入流)</a></dt> 18 <dd><a href="#title6">4.1.InputStream的基本方法</a></dd> 19 <dt><a href="#title7">5、OutputStream(輸出流)</a></dt> 20 <dd><a href="#title8">5.1.OutputStream的基本方法</a></dd> 21 <dt><a href="#title9">6、Reader流</a></dt> 22 <dd><a href="#title10">6.1.Reader的基本方法</a></dd> 23 <dt><a href="#title11">7、Writer流</a></dt> 24 <dd><a href="#title12">7.1.Writer的基本方法</a></dd> 25 <dt><a href="#title13">8、節點流講解</a></dt> 26 <dt><a href="#title14">9、處理流講解</a></dt> 27 <dd><a href="#title15">9.1.第一種處理流——緩衝流(Buffering)</a></dd> 28 <dd><a href="#title16">9.2.第二種處理流——轉換流</a></dd> 29 <dd><a href="#title17">9.3.第三種處理流——數據流</a></dd> 30 <dd><a href="#title18">9.4.打印流——Print</a></dd> 31 <dd><a href="#title19">9.5. 對象流——Object</a></dd> 32 <dt><a href="#title20">10、IO流總結</a></dt> 33 </dl> 34 <hr/> 35 <div style="background-color:Red; width:100%;height:150px;"> 36 <!--通過測試發現,使用name的形式添加錨點在IE8和火狐下都不起做用,要設置Id才起做用 37 爲了兼容,仍是將name屬性和id屬性都一塊兒寫上,而且屬性值設置成相同 38 --> 39 <h2 id="title0" name="title0">1、JAVA流式輸入/輸出原理</h2> 40 </div> 41 <div style="background-color:Yellow; width:100%;height:150px;"> 42 <h2 name="title1" id="title1">2、輸入輸出流分類</h2> 43 </div> 44 <div style="background-color:Silver; width:100%;height:150px;"> 45 <h2 name="title2" id="title2">3、節點流和處理流</h2> 46 <h3 name="title3" id="title3">3.1.節點流類型</h3> 47 <h3 name="title4" id="title4">3.2.處理流類型</h3> 48 </div> 49 <div style="background-color:Aqua; width:100%;height:150px;"> 50 <h2 name="title5" id="title5">4、InputStream(輸入流)</h2> 51 <h3 name="title6" id="title6">4.1.InputStream的基本方法</h3> 52 </div> 53 <div style="background-color:Fuchsia; width:100%;height:150px;"> 54 <h2 name="title7" id="title7">5、OutputStream(輸出流)</h2> 55 <h3 name="title8" id="title8">5.1.OutputStream的基本方法</h3> 56 </div> 57 <div style="background-color:Green; width:100%;height:150px;"> 58 <h2 name="title9" id="title9">6、Reader流</h2> 59 <h3 name="title10" id="title10">6.1.Reader的基本方法</h3> 60 </div> 61 <div style="background-color:Blue; width:100%;height:150px;"> 62 <h2 name="title11" id="title11">7、Writer流</h2> 63 <h3 name="title12" id="title12">7.1.Writer的基本方法</h3> 64 </div> 65 <div style="background-color:Olive; width:100%;height:150px;"> 66 <h2 name="title13" id="title13">8、節點流講解</h2> 67 </div> 68 <div style="background-color:Green; width:100%;height:150px;"> 69 <h2 name="title14" id="title14">9、處理流講解</h2> 70 <h3 name="title15" id="title15">9.1.第一種處理流——緩衝流(Buffering)</h3> 71 <h3 name="title16" id="title16">9.2.第二種處理流——轉換流</h3> 72 <h3 name="title17" id="title17">9.3.第三種處理流——數據流</h3> 73 <h3 name="title18" id="title18">9.4.打印流——Print</h3> 74 <h3 name="title19" id="title19">9.5. 對象流——Object</h3> 75 </div> 76 <div style="background-color:Purple; width:100%;height:150px;"> 77 <h2 name="title20" id="title20">10、IO流總結</h2> 78 </div> 79 <div style="height:130px;"> </div> 80 <div style="height:130px;"> </div> 81 <div style="height:130px;"> </div> 82 </body> 83 </html>
這是使用HTML錨點連接的解決方案,也是一種常規的實現方式,這種方案我以爲用戶體驗不怎麼好,跳轉到具體內容的時候速度太快了,沒有一種平滑過分的效果。
JavaScript實現方式
實現原理:首先經過調用 DOM 方法,判斷出瀏覽器滾動條(scroll bar)的當前位置,記爲 currentPos;而後計算出目標標題(target title)的距頁面頂端的距離,記爲 finalPos;最後經過必定的算法實現平滑過分。
下面是這個JS工具的相關源代碼:
1 /* 2 功能:生成博客目錄的JS工具 3 測試:IE8,火狐,google測試經過 4 孤傲蒼狼 5 2014-5-11 6 */ 7 var BlogDirectory = { 8 /* 9 獲取元素位置,距瀏覽器左邊界的距離(left)和距瀏覽器上邊界的距離(top) 10 */ 11 getElementPosition:function (ele) { 12 var topPosition = 0; 13 var leftPosition = 0; 14 while (ele){ 15 topPosition += ele.offsetTop; 16 leftPosition += ele.offsetLeft; 17 ele = ele.offsetParent; 18 } 19 return {top:topPosition, left:leftPosition}; 20 }, 21 22 /* 23 獲取滾動條當前位置 24 */ 25 getScrollBarPosition:function () { 26 var scrollBarPosition = document.body.scrollTop || document.documentElement.scrollTop; 27 return scrollBarPosition; 28 }, 29 30 /* 31 移動滾動條,finalPos 爲目的位置,internal 爲移動速度 32 */ 33 moveScrollBar:function(finalpos, interval) { 34 35 //若不支持此方法,則退出 36 if(!window.scrollTo) { 37 return false; 38 } 39 40 //窗體滾動時,禁用鼠標滾輪 41 window.onmousewheel = function(){ 42 return false; 43 }; 44 45 //清除計時 46 if (document.body.movement) { 47 clearTimeout(document.body.movement); 48 } 49 50 var currentpos =BlogDirectory.getScrollBarPosition();//獲取滾動條當前位置 51 52 var dist = 0; 53 if (currentpos == finalpos) {//到達預約位置,則解禁鼠標滾輪,並退出 54 window.onmousewheel = function(){ 55 return true; 56 } 57 return true; 58 } 59 if (currentpos < finalpos) {//未到達,則計算下一步所要移動的距離 60 dist = Math.ceil((finalpos - currentpos)/10); 61 currentpos += dist; 62 } 63 if (currentpos > finalpos) { 64 dist = Math.ceil((currentpos - finalpos)/10); 65 currentpos -= dist; 66 } 67 68 var scrTop = BlogDirectory.getScrollBarPosition();//獲取滾動條當前位置 69 window.scrollTo(0, currentpos);//移動窗口 70 if(BlogDirectory.getScrollBarPosition() == scrTop)//若已到底部,則解禁鼠標滾輪,並退出 71 { 72 window.onmousewheel = function(){ 73 return true; 74 } 75 return true; 76 } 77 78 //進行下一步移動 79 var repeat = "BlogDirectory.moveScrollBar(" + finalpos + "," + interval + ")"; 80 document.body.movement = setTimeout(repeat, interval); 81 }, 82 83 htmlDecode:function (text){ 84 var temp = document.createElement("div"); 85 temp.innerHTML = text; 86 var output = temp.innerText || temp.textContent; 87 temp = null; 88 return output; 89 }, 90 91 /* 92 建立博客目錄, 93 id表示包含博文正文的 div 容器的 id, 94 mt 和 st 分別表示主標題和次級標題的標籤名稱(如 H二、H3,大寫或小寫均可以!), 95 interval 表示移動的速度 96 */ 97 createBlogDirectory:function (id, mt, st, interval){ 98 //獲取博文正文div容器 99 var elem = document.getElementById(id); 100 if(!elem) return false; 101 //獲取div中全部元素結點 102 var nodes = elem.getElementsByTagName("*"); 103 //建立博客目錄的div容器 104 var divSideBar = document.createElement('DIV'); 105 divSideBar.className = 'sideBar'; 106 divSideBar.setAttribute('id', 'sideBar'); 107 var divSideBarTab = document.createElement('DIV'); 108 divSideBarTab.setAttribute('id', 'sideBarTab'); 109 divSideBar.appendChild(divSideBarTab); 110 var h2 = document.createElement('H2'); 111 divSideBarTab.appendChild(h2); 112 var txt = document.createTextNode('目錄導航'); 113 h2.appendChild(txt); 114 var divSideBarContents = document.createElement('DIV'); 115 divSideBarContents.style.display = 'none'; 116 divSideBarContents.setAttribute('id', 'sideBarContents'); 117 divSideBar.appendChild(divSideBarContents); 118 //建立自定義列表 119 var dlist = document.createElement("dl"); 120 divSideBarContents.appendChild(dlist); 121 var num = 0;//統計找到的mt和st 122 mt = mt.toUpperCase();//轉化成大寫 123 st = st.toUpperCase();//轉化成大寫 124 //遍歷全部元素結點 125 for(var i=0; i<nodes.length; i++) 126 { 127 if(nodes[i].nodeName == mt|| nodes[i].nodeName == st) 128 { 129 //獲取標題文本 130 var nodetext = nodes[i].innerHTML.replace(/<\/?[^>]+>/g,"");//innerHTML裏面的內容可能有HTML標籤,因此用正則表達式去除HTML的標籤 131 nodetext = nodetext.replace(/ /ig, "");//替換掉全部的 132 nodetext = BlogDirectory.htmlDecode(nodetext); 133 //插入錨 134 nodes[i].setAttribute("id", "blogTitle" + num); 135 var item; 136 switch(nodes[i].nodeName) 137 { 138 case mt: //若爲主標題 139 item = document.createElement("dt"); 140 break; 141 case st: //若爲子標題 142 item = document.createElement("dd"); 143 break; 144 } 145 146 //建立錨連接 147 var itemtext = document.createTextNode(nodetext); 148 item.appendChild(itemtext); 149 item.setAttribute("name", num); 150 item.onclick = function(){ //添加鼠標點擊觸發函數 151 var pos = BlogDirectory.getElementPosition(document.getElementById("blogTitle" + this.getAttribute("name"))); 152 if(!BlogDirectory.moveScrollBar(pos.top, interval)) return false; 153 }; 154 155 //將自定義表項加入自定義列表中 156 dlist.appendChild(item); 157 num++; 158 } 159 } 160 161 if(num == 0) return false; 162 /*鼠標進入時的事件處理*/ 163 divSideBarTab.onmouseenter = function(){ 164 divSideBarContents.style.display = 'block'; 165 } 166 /*鼠標離開時的事件處理*/ 167 divSideBar.onmouseleave = function() { 168 divSideBarContents.style.display = 'none'; 169 } 170 171 document.body.appendChild(divSideBar); 172 } 173 174 }; 175 176 window.onload=function(){ 177 /*頁面加載完成以後生成博客目錄*/ 178 BlogDirectory.createBlogDirectory("cnblogs_post_body","h2","h3",20); 179 }
1 /*生成博客目錄的CSS*/ 2 #sideBar{ 3 font-size:12px; 4 font-family:Arial, Helvetica, sans-serif; 5 text-align:left; 6 position:fixed;/*將div的位置固定到距離top:50px,right:0px的位置,這樣div就會處在最右邊的位置,距離頂部50px*/ 7 top:50px; 8 right:0px; 9 width: auto; 10 height: auto; 11 } 12 #sideBarTab{ 13 float:left; 14 width:30px; 15 border:1px solid #e5e5e5; 16 border-right:none; 17 text-align:center; 18 background:#ffffff; 19 } 20 21 #sideBarContents{ 22 float:left; 23 overflow:auto; 24 overflow-x:hidden;!important; 25 width:200px; 26 min-height:108px; 27 max-height:460px; 28 border:1px solid #e5e5e5; 29 border-right:none; 30 background:#ffffff; 31 } 32 #sideBarContents dl{ 33 margin:0; 34 padding:0; 35 } 36 37 #sideBarContents dt{ 38 margin-top:5px; 39 margin-left:5px; 40 } 41 42 #sideBarContents dd, dt { 43 cursor: pointer; 44 } 45 46 #sideBarContents dd:hover, dt:hover { 47 color:#A7995A; 48 }
1 <div id="sideBar" class="sideBar"> 2 <div id="sideBarTab"> 3 <h2>目錄導航</h2> 4 </div> 5 <div id="sideBarContents" style="display: none;"> 6 <dl> 7 <dt name="0">1、功能描述</dt> 8 <dd name="1">1.一、自動生成博文目錄</dd> 9 <dd name="2">1.二、點擊目錄標題定位到標題對應的具體內容</dd> 10 <dt name="3">2、源代碼</dt> 11 <dd name="4">2.1 js代碼</dd> 12 <dd name="5">2.2. CSS樣式代碼</dd> 13 <dt name="6">3、JS工具的使用</dt> 14 <dd name="7">3.一、後臺管理的相關設置</dd> 15 <dd name="8">3.二、博文標題樣式設置</dd> 16 <dt name="9">4、總結</dt> 17 </dl> 18 </div> 19 </div>
進入到博客後臺管理,點擊設置
而後在這裏添加CSS樣式代碼,將上面的CSS樣式代碼直接copy到這裏便可
在這裏添加javascript腳本,將上面的JS源代碼直接copy到這裏便可
點擊【保存】按鈕,完成設置
將博文標題的樣式設置成標題
例如:
將"3、JS工具的使用"這個一級標題的標題樣式設置成【標題2】
將"3.一、後臺管理的相關設置"這個二級標題的標題樣式設置成【標題3】
這樣執行這段代碼時就會生成以下圖所示的博客目錄了
1 window.onload=function(){ 2 /*頁面加載完成以後生成博客目錄*/ 3 BlogDirectory.createBlogDirectory("cnblogs_post_body","h2","h3",20); 4 }
生成的目錄導航在緊挨着頁面最右邊的滾動條,鼠標移動到【目錄導航】時就會顯示生成的目錄內容,鼠標離開就自動隱藏目錄
點擊目錄上的標題就會平滑跳轉到該標題對應的具體內容。
開發這個小工具的過程當中仍是遇到了很多的細節問題的,好在都解決了,目前作出來的效果整體來講還算比較滿意,如今將代碼分享出來給朋友們,能夠自由修改和使用,但願對廣大朋友們有所幫助吧!