简介
* 用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>
代码
追加,偶尔会出现BUG,有序列表与无序列表有时候会乱用。仍需人工检查、修正。
<!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 += ' <ul>'; } else { divBody += ' <ol>'; } for (let i = 0; i != title.subTitles.length; ++i) { visitTitle(title.subTitles[i]); } if (!first) { divBody += '</ul> '; } else { divBody += '</ol> '; } } if (!first) { divBody += '</li> '; } } let root = new Title(); // 便于遍历titles root.subTitles = titles; visitTitle(root, true); let divHead = '<div id="diy_right_menu"> <h2>索引</h2> <ul> '; let divTail = '</ul> </div> '; return divHead + divBody + divTail; } </script> </body> </html>