JavaScript自動生成博文目錄導航

  咱們在寫博客的時候,若是博文裏面有目錄,會給人結構清晰、一種一目瞭然的感受,看目錄就知道這篇博文要講解的內容,而且點擊目錄標題就能夠跳轉到具體的內容,這樣園友們在看博客的時候就能夠很方便地瀏覽本身感興趣的內容,可是遺憾的是博客園不支持博文目錄的生成,好像也有園友給博客園提建議,但願可以像CSDN那樣可以自動生成博文目錄,可是不知道是什麼緣由,博客園一直都沒有把這個功能加上去,既然沒有,那我就本身作吧,研究了2天,總算是按照本身的設想作出來了,最終效果以下:javascript

    

下面來介紹一下這個小工具的實現。html

1、功能描述

  這個小工具要實現的核心功能只有兩個:java

    1.自動生成博文目錄。node

    2.點擊目錄標題定位到標題對應的具體內容,就像使用word生成的目錄那樣。正則表達式

1.一、自動生成博文目錄

  小工具的第一個核心功能,就是經過代碼自動抽取標題,而後再包裝一下插入文檔中。基本實現原理:首先要求博主在寫博文的時候,將主標題和次級標題用 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>

  若是不加任何樣式,它在瀏覽器中的默認顯示效果以下:函數

    

1.二、點擊目錄標題定位到標題對應的具體內容

常規實現方式:使用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;">&nbsp;</div>
80     <div style="height:130px;">&nbsp;</div>
81     <div style="height:130px;">&nbsp;</div>
82  </body>
83 </html>

  這是使用HTML錨點連接的解決方案,也是一種常規的實現方式,這種方案我以爲用戶體驗不怎麼好,跳轉到具體內容的時候速度太快了,沒有一種平滑過分的效果。

JavaScript實現方式

  實現原理:首先經過調用 DOM 方法,判斷出瀏覽器滾動條(scroll bar)的當前位置,記爲 currentPos;而後計算出目標標題(target title)的距頁面頂端的距離,記爲 finalPos;最後經過必定的算法實現平滑過分。

2、源代碼

  下面是這個JS工具的相關源代碼:

2.1 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(/&nbsp;/ig, "");//替換掉全部的&nbsp;
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 }

2.2. CSS樣式代碼

 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 }

2.3.由JavaScript動態生成的HTML代碼結構以下

 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>

3、JS工具的使用

3.一、後臺管理的相關設置

  進入到博客後臺管理,點擊設置

  

而後在這裏添加CSS樣式代碼,將上面的CSS樣式代碼直接copy到這裏便可

  

在這裏添加javascript腳本,將上面的JS源代碼直接copy到這裏便可

  

點擊【保存】按鈕,完成設置

3.二、博文標題樣式設置

將博文標題的樣式設置成標題

  

例如:

將"3、JS工具的使用"這個一級標題的標題樣式設置成【標題2】

  

將"3.一、後臺管理的相關設置"這個二級標題的標題樣式設置成【標題3】

  

  這樣執行這段代碼時就會生成以下圖所示的博客目錄了

1 window.onload=function(){
2     /*頁面加載完成以後生成博客目錄*/
3     BlogDirectory.createBlogDirectory("cnblogs_post_body","h2","h3",20);
4 }

    

  生成的目錄導航在緊挨着頁面最右邊的滾動條,鼠標移動到【目錄導航】時就會顯示生成的目錄內容,鼠標離開就自動隱藏目錄

    

  點擊目錄上的標題就會平滑跳轉到該標題對應的具體內容。

4、總結

  開發這個小工具的過程當中仍是遇到了很多的細節問題的,好在都解決了,目前作出來的效果整體來講還算比較滿意,如今將代碼分享出來給朋友們,能夠自由修改和使用,但願對廣大朋友們有所幫助吧!

相關文章
相關標籤/搜索