1. 程式人生 > >JavaScript筆記 #08# 用Regex輔助生成文章目錄 V2.0

JavaScript筆記 #08# 用Regex輔助生成文章目錄 V2.0

索引

簡介

             * 用Regex輔助生成文章目錄 2.0
             * 1、提高了功能的通用性(假定的文章格式更加普遍,即按照h2h3h4分級)
             * 2、改善了程式碼的可讀性(稍微犧牲了一點點效能,不過也無關緊要。。)
             * 3、略微。。提高了擴充套件性(只需要重寫generateDirectory方法就可以
             * 生成自定義目錄)
             * 缺點:沒有處理比較髒的情況,需要人工確保html乾淨

測試用例

輸入:

        <h2>你好</h2>
        <p>dasjdalsjdlsasjdlsczxcnzxczxczxc00</p>
        <h3>dasda</h3>
        <h4>23981023812090839dajldjasldjalsjd</h4>
        <p>dasdasjdlasjdlasjdlassjdalsdjalsdj</p>
        <h4>21023812038129dajldjasldjalsjd</
h4> <p>dasdassjdlasjdlasjdlasjdalsdjalsdj</p> <p>dasdasjdlasjddlasjdlasjdalsdjalsdj</p> <p>dasdasjdlasjdlasjdlasjdalsdjalsdj</p> <h3>ddada</h3> <p>dasdasjdlasjdlasjdlasjdalsdjalsdj</p> <
p>daasjdlasjdlasjdlasjdlasjdalsdjalsdj</p> <p>dasdasjdlasjdlasdlasjdalsdjalsdj</p> <h2>hi</h2> <h3>dadasdasda</h3> <p>dasdasjdlasjdljdlasjdlasjdalsdjalsdj</p> <p>dasdasjdlasjdsjdlasjdalsdjalsdj</p> <p>dasdasjjdlasjdlasjdlasjdalsdjalsdj</p> <h3>dasasdassaddasda</h3> <p>dasdasjdlasasjdlasjdlasjdalsdjalsdj</p> <p>dasdasjdlalasjdlasjdlasjdalsdjalsdj</p> <p>dasdasjdlasjdlasjdlasjdlasjdalsdjalsdj</p> <h2>大家好</h2> <p>asjdlasjdlaasjdlasjsdjalsdj</p> <p>dsjdlasjdlasjdldlasjdalsdjalsdj</p> <p>dasdasjdlasjdlasjdalsdjalsdj</p>

輸出:

<div id="diy_right_menu">
<h2>索引</h2>
<ul>
<ol><li><a href="#anchor0">你好</a>
<ul><li><a href="#anchor1">dasda</a>
<ul><li><a href="#anchor2">23981023812090839dajldjasldjalsjd</a></li>
<li><a href="#anchor3">21023812038129dajldjasldjalsjd</a></li>
</ul>
</li>
<li><a href="#anchor4">ddada</a></li>
</ul>
</li>
<li><a href="#anchor5">hi</a>
<ul><li><a href="#anchor6">dadasdasda</a></li>
<li><a href="#anchor7">dasasdassaddasda</a></li>
</ul>
</li>
<li><a href="#anchor8">大家好</a></li>
</ol>
</ul>
</div>
                <h2><a name="anchor0"></a>你好</h2>
        <p>dasjdalsjdlsasjdlsczxcnzxczxczxc00</p>
        <h3><a name="anchor1"></a>dasda</h3>
        <h4><a name="anchor2"></a>23981023812090839dajldjasldjalsjd</h4>
        <p>dasdasjdlasjdlasjdlassjdalsdjalsdj</p>
        <h4><a name="anchor3"></a>21023812038129dajldjasldjalsjd</h4>
        <p>dasdassjdlasjdlasjdlasjdalsdjalsdj</p>
        <p>dasdasjdlasjddlasjdlasjdalsdjalsdj</p>
        <p>dasdasjdlasjdlasjdlasjdalsdjalsdj</p>
        <h3><a name="anchor4"></a>ddada</h3>
        <p>dasdasjdlasjdlasjdlasjdalsdjalsdj</p>
        <p>daasjdlasjdlasjdlasjdlasjdalsdjalsdj</p>
        <p>dasdasjdlasjdlasdlasjdalsdjalsdj</p>
        <h2><a name="anchor5"></a>hi</h2>
        <h3><a name="anchor6"></a>dadasdasda</h3>
        <p>dasdasjdlasjdljdlasjdlasjdalsdjalsdj</p>
        <p>dasdasjdlasjdsjdlasjdalsdjalsdj</p>
        <p>dasdasjjdlasjdlasjdlasjdalsdjalsdj</p>
        <h3><a name="anchor7"></a>dasasdassaddasda</h3>
        <p>dasdasjdlasasjdlasjdlasjdalsdjalsdj</p>
        <p>dasdasjdlalasjdlasjdlasjdalsdjalsdj</p>
        <p>dasdasjdlasjdlasjdlasjdlasjdalsdjalsdj</p>
        <h2><a name="anchor8"></a>大家好</h2>
        <p>asjdlasjdlaasjdlasjsdjalsdj</p>
        <p>dsjdlasjdlasjdldlasjdalsdjalsdj</p>
        <p>dasdasjdlasjdlasjdalsdjalsdj</p>
 Generate

程式碼

<!DOCTYPE html>
<html>

    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>

    <body>
        <textarea id="big-textarea" placeholder="paste your origin html text here..." rows="30" cols="150">
        </textarea>
        <button id="big-button">Generate</button>
        
        <script type="text/javascript">
            /**
             * 用Regex輔助生成文章目錄 2.0
             * 1、提高了功能的通用性(假定的文章格式更加普遍,即按照h2h3h4分級)
             * 2、改善了程式碼的可讀性(稍微犧牲了一點點效能,不過也無關緊要。。。)
             * 3、略微。。提高了擴充套件性(只需要重寫generateDirectory方法就可以
             * 生成自定義目錄)
             * 缺點:沒有處理比較髒的情況,需要人工確保html乾淨
             */
            let button = document.querySelector('#big-button');
            button.addEventListener('click', event => {
                let textArea = document.querySelector('#big-textarea');
                let inputHtml = textArea.value;
                textArea.value = addDirectoryTo(inputHtml);
            });
        </script>
                
        <script type="text/javascript">                    
            /**
             * 返回生成目錄後的inputHtml
             * @param {Object} inputHtml 原始html
             */
            function addDirectoryTo(inputHtml) {
                // 給h2 h3 h4加上錨點
                let modifiedHtml = addAnchorTo(inputHtml);
                // 提取h2 h3 h4標題
                let titles = extractTitles(inputHtml);
                // 利用h2 h3 h4標題生成目錄
                let directory = generateDirectory(titles);
                // 將目錄和修改後的html拼接後返回
                return directory + modifiedHtml;
            }
        </script>
 
         <script type="text/javascript">
            /**
             * Title類屬性↓
             * text:string,存該標題中的文字
             * level:Number,標題等級
             * subTitles:list,用來存子標題
             */
            function Title(h2h3h4) {
                /**
                 * 構造器呼叫示例:new Title("<h2>二級標題</h2>")
                 */
                if (h2h3h4 != undefined) {
                    let extractedInfo = this.patternOfh2h3h4.exec(h2h3h4);
                    this.text = extractedInfo[2];
                    this.level = Number(extractedInfo[1]);
                    this.subTitles = [];                    
                }
            }
            
            Title.prototype.patternOfh2h3h4 = /<h([234])>([^]+?)<\/h[234]>/;
        </script>
        
        <script type="text/javascript">
            /**
             * 返回給h2 h3 h4加上錨點後的html
             * @param {Object} inputHtml 原始html
             */
            function addAnchorTo(inputHtml) {
                let patternOfh2h3h4 = /(<h[234]>)/g;
                let indexOfAnchor = 0;
                let modifyh2h3h4 = (_, h2h3h4) => {
                    return `${h2h3h4}<a name="anchor${indexOfAnchor++}"></a>`;
                };
                let modifiedHtml = inputHtml.replace(patternOfh2h3h4, modifyh2h3h4);
                return modifiedHtml;
            }
            
            /**
             * 返回包含二級標題物件的list 
             * @param {Object} inputHtml 原始html
             */
            function extractTitles(inputHtml) {                        
                let titles = [];
                let patternOfh2h3h4 = /<h[234]>[^]+?<\/h[234]>/g; // 非貪婪匹配
                // 遍歷正則匹配項
                let match;
                while (match = patternOfh2h3h4.exec(inputHtml)) {
                    let title = new Title(match[0]);
                    properPostion(titles, title.level).push(title);
                }
                return titles;
            }
            
            const TOP_LEVEL = 2;
            /**
             * 返回某個標題的subTitles或者最終的titleList
             * 給標題找合適的插入位置,為了不讓extractTitles太長才抽象出來的。
             * @param {Object} titles 
             * @param {Object} level 待插入標題的level
             */
            function properPostion(titles, level) {
                for (let i = TOP_LEVEL; i != level; ++i) {
                    titles = titles[titles.length - 1].subTitles;                            
                }
                return titles;               
            }

            /**
             * 返回根據標題生成的目錄,這個方法可以根據需要自定義
             * @param {Object} titles 包含二級標題物件的list 
             */
            function generateDirectory(titles) {
                return generateDiy_right_menu(titles);
            }         
            
            function generateDiy_right_menu(titles) {                
                let indexOfAnchor = 0;
                let divBody = "";
                let visitTitle = function(title, first=false) {
                    if (!first) {
                        divBody += `<li><a href="#anchor${indexOfAnchor++}">${title.text}</a>`;                        
                    }                    
                    if (title.subTitles.length != 0) {
                        if (!first) {
                            divBody += '\n<ul>';
                        } else {
                            divBody += '\n<ol>';
                        }
                        for (let i = 0; i != title.subTitles.length; ++i) {
                            visitTitle(title.subTitles[i]);
                        }    
                        if (!first) {
                            divBody += '</ul>\n';
                        } else {
                            divBody += '</ol>\n';
                        }
                    }    
                    if (!first) {
                        divBody += '</li>\n';
                    }
                }            
                let root = new Title(); // 便於遍歷titles
                root.subTitles = titles;
                visitTitle(root, true);
                
                let divHead = '<div id="diy_right_menu">\n<h2>索引</h2>\n<ul>\n';
                let divTail = '</ul>\n</div>\n';
                return divHead + divBody + divTail;
            }
        </script>
    </body>

</html>