收藏本站腾讯微博新浪微博

经典论坛

 找回密码
 注册

QQ登录

只需一步,快速开始

蓝色理想 最新研发动态 网站开通淘帖功能 - 蓝色理想插件 论坛内容导读一页看论坛 - 给官方提建议

论坛活动及任务 地图和邮件任务 请多用悬赏提问 热夏来袭,选一款蓝色理想的个性T恤吧!

手机上论坛,使用APP获得更好体验 急需前端攻城狮,获得内部推荐机会 论坛开通淘帖功能,收藏终于可以分类了!

搜索
查看: 1908|回复: 8

[作品展示] AutocJS - 为你的文章自动创建目录导航菜单

[复制链接]
发表于 2016-4-6 09:18:31 | 显示全部楼层 |阅读模式
最近有空写点自己的小程序了,于是为自己的BLOG写了一个小程序,为文章生成目录导航。我想这也并不是什么新鲜玩意,不过自己动手写一个还是一个不错的学习过程。

演示地址:http://www.yaohaixiao.com/github/autocjs/

gihhub: https://github.com/yaohaixiao/AutocJS



重点一:判断文章标题之间的关系(标题序号:h1~h6,即1~6,标题层级:1~6),而标题之间的层级只有3种:

1. (父标题,子标题):当前标题的序号 > 前一个标题的序号
2. (同级标题,同级标题):
      A. 当前标题的序号 === 前一个标题的序号,例如:h2, h2 前后两个都是h2,就是同级的
      B. 当前标题的序号 < 前一个标题的序号 && 当前标题的序号 > 等级,例如:异常情况,h2,h4,h3,h2,虽然 h4,h3 的序号不同,但是实际h4,h3都是之前的h2下的子集
3.  (子标题,父级标题):当前标题的序号 < 前一个标题的序号


  1. /**
  2.      * 获得文章的段落索引
  3.      *
  4.      * @returns {Array}
  5.      */
  6.     function getChapters(){
  7.         var chapters = [],
  8.             prevNum = 1,
  9.             level = 0;

  10.         // 获得目录索引信息
  11.         $anchors.each(function(i, anchor) {
  12.             var $anchor = $(anchor),
  13.                 curNum = parseInt($anchor[0].tagName.toUpperCase().replace(/[H]/ig, ''),10),
  14.                 pid = -1;

  15.             $anchor.attr('id', guid(attributes.prefix));

  16.             // 1.(父标题,子标题):当前标题的序号 > 前一个标题的序号
  17.             if (curNum > prevNum) {
  18.                 level += 1;

  19.                 // 第一层级的 pid 是 -1
  20.                 if (level === 1) {
  21.                     pid = -1;
  22.                 }
  23.                 else {
  24.                     pid = i - 1;
  25.                 }
  26.             } else {
  27.                 // 2.(同级标题,同级标题)
  28.                 // A. 当前标题的序号 === 前一个标题的序号
  29.                 // B. 当前标题的序号 < 前一个标题的序号 && 当前标题的序号 > 等级
  30.                 if (curNum === prevNum || (curNum < prevNum && curNum > level)) {

  31.                     // H1 的层级肯定是 1
  32.                     if (curNum === 1) {
  33.                         level = 1;

  34.                         pid = -1;
  35.                     }
  36.                     else {
  37.                         pid = chapters[i - 1].pid;
  38.                     }
  39.                 } else {
  40.                     // 3.(子标题,父级标题):当前标题的序号 < 前一个标题的序号
  41.                     if (curNum <= level) {

  42.                         // H1 的层级肯定是 1
  43.                         if(curNum === 1){
  44.                             level = 1;
  45.                         }
  46.                         else {
  47.                             level = level - (prevNum - curNum);
  48.                         }

  49.                         // 第一级的标题
  50.                         if (level === 1) {
  51.                             pid = -1
  52.                         }
  53.                         else {
  54.                             // 最大只有5系的差距
  55.                             // 虽然看上去差点,不过能工作啊
  56.                             switch (prevNum - curNum) {
  57.                                 case 1:
  58.                                     pid = chapters[chapters[i - 1].pid].pid;
  59.                                     break;
  60.                                 case 2:
  61.                                     pid = chapters[chapters[chapters[i - 1].pid].pid].pid;
  62.                                     break;
  63.                                 case 3:
  64.                                     pid = chapters[chapters[chapters[chapters[i - 1].pid].pid].pid].pid;
  65.                                     break;
  66.                                 case 4:
  67.                                     pid = chapters[chapters[chapters[chapters[chapters[i - 1].pid].pid].pid].pid].pid;
  68.                                     break;
  69.                                 case 5:
  70.                                     pid = chapters[chapters[chapters[chapters[chapters[chapters[i - 1].pid].pid].pid].pid].pid].pid;
  71.                                     break;
  72.                             }
  73.                         }
  74.                     }
  75.                 }
  76.             }

  77.             prevNum = curNum;

  78.             chapters.push({
  79.                 // 唯一ID
  80.                 id: i,
  81.                 // 层级
  82.                 level: level,
  83.                 // 标题文本
  84.                 text: $anchor.html(),
  85.                 // 标题链接的锚点ID
  86.                 value: $anchor.attr('id'),
  87.                 // 标题的 HTML 标签名
  88.                 tag: anchor.tagName,
  89.                 // 父亲层级的ID, 第一级的为 -1
  90.                 pid: pid
  91.             });
  92.         });

  93.         return chapters;
  94.     }
复制代码


通过上述方法,可以获得一个数组 chapters,数据结构大致如下:


  1. [
  2.             {
  3.                 "id": 0,
  4.                 "level": 1,
  5.                 "text": "创作的灵感",
  6.                 "value": "anchor-0",
  7.                 "tag": "H2",
  8.                 "pid": -1
  9.             }, {
  10.                 "id": 1,
  11.                 "level": 1,
  12.                 "text": "What is AutocJS?",
  13.                 "value": "anchor-1",
  14.                 "tag": "H2",
  15.                 "pid": -1
  16.             }, {
  17.                 "id": 2,
  18.                 "level": 1,
  19.                 "text": "Why AutocJS?",
  20.                 "value": "anchor-2",
  21.                 "tag": "H2",
  22.                 "pid": -1
  23.             },
  24.             {
  25.                 "id": 3,
  26.                 "level": 2,
  27.                 "text": "AutocJS 的特点",
  28.                 "value": "anchor-3",
  29.                 "tag": "H3",
  30.                 "pid": 2
  31.             }, {
  32.                 "id": 4,
  33.                 "level": 1,
  34.                 "text": "DEMO",
  35.                 "value": "anchor-4",
  36.                 "tag": "H2",
  37.                 "pid": -1
  38.             }, {
  39.                 "id": 5,
  40.                 "level": 1,
  41.                 "text": "API Documentation",
  42.                 "value": "anchor-5",
  43.                 "tag": "H2",
  44.                 "pid": -1
  45.             }, {
  46.                 "id": 6,
  47.                 "level": 2,
  48.                 "text": "语法",
  49.                 "value": "anchor-6",
  50.                 "tag": "H3",
  51.                 "pid": 5
  52.             }, {
  53.                 "id": 7,
  54.                 "level": 2,
  55.                 "text": "参数说明",
  56.                 "value": "anchor-7",
  57.                 "tag": "H3",
  58.                 "pid": 5
  59.             }, {
  60.                 "id": 8,
  61.                 "level": 2,
  62.                 "text": "调用方法",
  63.                 "value": "anchor-8",
  64.                 "tag": "H3",
  65.                 "pid": 5
  66.             }, {
  67.                 "id": 9,
  68.                 "level": 3,
  69.                 "text": "第一步:引用 CSS 样式",
  70.                 "value": "anchor-9",
  71.                 "tag": "H4",
  72.                 "pid": 8
  73.             }, {
  74.                 "id": 10,
  75.                 "level": 3,
  76.                 "text": "第二步:引用 JS 脚本",
  77.                 "value": "anchor-10",
  78.                 "tag": "H4",
  79.                 "pid": 8
  80.             }, {
  81.                 "id": 11,
  82.                 "level": 3,
  83.                 "text": "第三步:调用 autoc() 方法",
  84.                 "value": "anchor-11",
  85.                 "tag": "H4",
  86.                 "pid": 8
  87.             }, {
  88.                 "id": 12,
  89.                 "level": 4,
  90.                 "text": "指定文章内容的 DOM 节点",
  91.                 "value": "anchor-12",
  92.                 "tag": "H5",
  93.                 "pid": 11
  94.             }, {
  95.                 "id": 13,
  96.                 "level": 4,
  97.                 "text": "指定要记录的标题",
  98.                 "value": "anchor-13",
  99.                 "tag": "H5",
  100.                 "pid": 11
  101.             }, {
  102.                 "id": 14,
  103.                 "level": 4,
  104.                 "text": "指定标题锚点的 id 的前缀",
  105.                 "value": "anchor-14",
  106.                 "tag": "H5",
  107.                 "pid": 11
  108.             }, {
  109.                 "id": 15,
  110.                 "level": 1,
  111.                 "text": "License",
  112.                 "value": "anchor-15",
  113.                 "tag": "H2",
  114.                 "pid": -1
  115.             }
  116.         ]
复制代码


重点二:根据chapters的数据生成树形层次的导航菜单,还有就是要生成章节的子章节的层次索引

1. 首先生成第一层级的(li)菜单,根据li在当前层级的ul中的索引index()获得当前标题在当前层级中的章节位置
2. 然后再从第一层级开始创建子层级的ul菜单和菜单项。当前的层级完成索引= 有父节点的层级,就是父节点层级的index() + 当前自己的index()


  1. /**
  2.      * 根据段落索引绘制完整的导航
  3.      */
  4.     function renderChapters() {
  5.         var chapters = getChapters();

  6.         $(chapters).each(function (i, chapter) {
  7.             var $item = $(ITEM),
  8.                 $link = $(LINK),
  9.                 chapterText = '',
  10.                 chapterCount = 0,
  11.                 $chapter = $(CHAPTER),
  12.                 $sublist = $('#toc-list-' + chapter.pid);

  13.             $link.attr({
  14.                 id: 'toc-link-' + chapter.id,
  15.                 href: '#' + chapter.value
  16.             }).html(chapter.text);

  17.             $item.attr({
  18.                 'id': 'toc-item-' + chapter.id,
  19.                 'title': chapter.text
  20.             }).append($link);

  21.             if (chapter.pid === -1) {
  22.                 $list.append($item);
  23.                 chapterCount = $item.index() + 1;
  24.                 chapterText = chapterCount;
  25.             }
  26.             else {
  27.                 if (!$sublist[0]) {
  28.                     $sublist = $(SUB_LIST).attr('id', 'toc-list-' + chapter.pid);

  29.                     $('#toc-item-' + chapter.pid).append($sublist);
  30.                 }

  31.                 $sublist.append($item);

  32.                 chapterCount = $item.index() + 1;
  33.                 // 关键是这里,生成当前的索引
  34.                 chapterText = $sublist.parent().find('.toc-chapter').html() + '.' + chapterCount;
  35.             }

  36.             $chapter.attr('data-chapter', chapterCount).html(chapterText);
  37.             $chapter.insertBefore($link);
  38.         });
  39.     }
复制代码


其余就没有什么特别的,我当前的代码由于是绝对定位的,所以通过创建文档碎片把整个菜单缓存后一次添加到页面,可能性能稍差,不过由于绝对定位的关系,repain,reflow的影响要稍小,所以感觉还好。

不知道有没有其他朋友有更好的生成索引的算法,欢迎拍砖啊!
 楼主| 发表于 2016-4-15 23:02:23 | 显示全部楼层
本帖最后由 yaohaixiao 于 2016-4-16 06:02 编辑

已经发布到npmjs.org上了,现在只AMD和CMD的调用,同时也支持jquery插件形式调用了

https://www.npmjs.com/package/autocjs
回复 支持 反对

使用道具 举报

发表于 2016-4-15 23:59:03 | 显示全部楼层
谢谢楼主无私分享,收藏下来慢慢学习。
回复 支持 反对

使用道具 举报

发表于 2016-4-16 00:01:58 | 显示全部楼层
snap0470.jpg
回复 支持 反对

使用道具 举报

 楼主| 发表于 2016-4-16 06:04:23 | 显示全部楼层

https://github.com/yaohaixiao/AutocJS

地址没有错误,估计是GITHUB偶尔访问不到吧
回复 支持 反对

使用道具 举报

发表于 2016-4-16 09:12:47 | 显示全部楼层
感谢楼主的分享!收藏下来好好学习!赞一个
回复 支持 反对

使用道具 举报

 楼主| 发表于 2016-4-16 21:25:35 | 显示全部楼层
今天把代码发布到bower上了

bower install AutocJS
回复 支持 反对

使用道具 举报

 楼主| 发表于 2016-4-19 12:57:25 | 显示全部楼层
发布到了oschine.net,试了试推荐,结果还真上了首页
1.png
回复 支持 反对

使用道具 举报

发表于 2016-4-19 15:30:29 | 显示全部楼层
支持AMD,我喜欢~
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

QQ|小黑屋|Archiver|手机版|blueidea.com ( 湘ICP备12001430号 )  

GMT+8, 2020-9-24 01:07 , Processed in 0.124687 second(s), 10 queries , Gzip On, Memcache On.

Powered by Discuz! X3.2 Licensed

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表