原创 使用 JavaScript 生成HTML目录(一)

2018-3-8 17:30 3859 25 4 分类: 软件与OS 文集: 软件

使用 JavaScript 生成HTML目录(一)


阅读帮助与感想:
1. 似乎编辑栏找不到 Markdown 语法方式, 也没有整个 html 文件上传的支持. 明明已经完成的 html 笔记, 为在博客发表而不得不在此重新编辑, 为了共享知识, 要付出作者们这么大的努力啊!
2. 网页效果似无法表现. 因为我的这份博客文章, 就是为 javacript 对网页效果的一些应用而写就的, 能够直接在 html 显示效果. 唔, 那么我尽量试试用图片来示意吧.
3. 代码段没有专门的 Tool 的ICON 指示, 帮助代码显示.


1. Markdown File 的 Export Html 的限制

作为轻易上手的 markdown 语言写作编辑器, 其 Html 的 Export 功能也往往相当简易. 似乎某些相对复杂些的 MD Editor 能够使用高级的 [TOC] 语法, 而某些则能够在编辑环境自定义的 CSS 中获得自行产生目录(TOC) 的能力.

但使用类似 MarkdownPad 的 markdown editor 时, 则往往不能相当简易获得自行产生上述 TOC 之实现.

因此, 我们需要作一些稍微麻烦点儿的工作, 去实现我们的目的. 我们可能会作笔记记录项目大致框架, 项目实现, 功能需求实现. 可能在博客上共享这些 story 或 knowledge. 因此, TOC 实现方式应该是足够的"轻", 它应无需外部的 CSS, 或 JavaScript 文件(或者至少它能放在 html 的 head 中).


2. 在文章头部添加 TOC
2.1 可展开的头部目录

引用来自 MarkdownPad2添加目录(输出为HTML时可用) 的代码(如有任何禁止引用的意见请通知我停止引用):


<script type="text/javascript">
  document.addEventListener("DOMContentLoaded", function() {

// 生成目录列表
var div1 = document.createElement("div");
div1.style.cssText = "clear:both";
var outline = document.createElement("div");
outline.setAttribute("id", "outline-list");
outline.style.cssText = "border:solid 1px #ccc; background:#eee; min-width:200px;padding:4px 10px;";

var ele_p = document.createElement("p");
ele_p.style.cssText = "text-align: left; margin: 0;";
outline.appendChild(ele_p);

var ele_span = document.createElement("span");
// ele_span.style.cssText = "float: left;";
var ele_text=document.createTextNode("目录");
ele_span.appendChild(ele_text);

var ele_a = document.createElement("a");
ele_a.appendChild(document.createTextNode("[+]"));
ele_a.setAttribute("href", "#");
ele_a.setAttribute("onclick", "javascript:return openct(this);");
ele_a.setAttribute("title", "点击打开目录");

ele_span.appendChild(ele_a);
ele_p.appendChild(ele_span);

var ele_ol = document.createElement("ol");
ele_ol.style.cssText = "display:none;margin-left:14px;padding-left:14px;line-height:160%;";
ele_ol.setAttribute("id", "outline_ol");
outline.appendChild(ele_ol);
var div1 = document.createElement("div");
div1.style.cssText = "clear:both";

document.body.insertBefore(outline, document.body.childNodes[0]);
// 获取所有标题
var headers = document.querySelectorAll('h1,h2,h3,h4,h5,h6');
if (headers.length < 2)
  return;

// -----
var old_h = 0, ol_cnt = 0;
// -----

for (var i = 0; i < headers.length; i++) {

  var ele_ols = null;
  var ele_Current = ele_ol;
  // 找出它是H几,为后面前置空格准备
  var header = headers;
  header.setAttribute("id", "t" + i + header.tagName);
  var h = parseInt(header.tagName.substr(1), 10);

  // -----
  if (!old_h){
    old_h = h;

  }

  if (h > old_h) {

    ele_ols = document.createElement("ol");
    ele_Current = ele_ol;
    if(ele_Current && ol_cnt > 0){
      var temp = ol_cnt;
      while(temp > 0){
        ele_Current = ele_Current.lastChild;
        temp--;
      }
    }
    ele_Current.lastChild.appendChild(ele_ols);
    ol_cnt++;
  } else if (h < old_h && ol_cnt > 0) {

    if (h == 1) {
      while (ol_cnt > 0) {
        ol_cnt--;
      }
    } else {
      ele_ols = document.createElement("ol");
      ele_Current = ele_ol;
      if(ele_Current && ol_cnt > 0){
        var temp = ol_cnt;
        while(temp > 1){
          ele_Current = ele_Current.lastChild;
          temp--;
        }
      }
    // var ele_Parent = ele_Current.parentNode();
    //ele_Current.appendChild(ele_ols);
    ol_cnt--;

    }
  } else if (h == old_h && ol_cnt > 0) {

    ele_Current = ele_ol;
    if(ele_Current && ol_cnt > 0){
      var temp = ol_cnt;
      while(temp > 0){
        ele_Current = ele_Current.lastChild;
        temp--;
      }
    }
    ele_Current = ele_Current.lastChild;
  }
  if (h == 1) {
    while (ol_cnt > 0) {
      ol_cnt--;
    }
  }
  if (h < old_h && ol_cnt > 0 && h != 1){
    ele_li = document.createElement("li")
    ele_Current.lastChild.appendChild(ele_li);
    old_h = h;
    var a = document.createElement("a");
    // 为目录项设置链接
    a.setAttribute("href", "#t" + i + header.tagName);
    // 目录项文本前面放置对应的空格
    a.innerHTML = header.textContent;
    ele_li.appendChild(a);
    continue;
  }

  old_h = h;
  // -----
  if (ele_ols){
    ele_li = document.createElement("li")
    ele_ols.appendChild(ele_li);
  } else {
    ele_li = document.createElement("li")
    ele_Current.appendChild(ele_li);
  }
  var a = document.createElement("a");
  // 为目录项设置链接
  a.setAttribute("href", "#t" + i + header.tagName);
  // 目录项文本前面放置对应的空格
  a.innerHTML = header.textContent;
  ele_li.appendChild(a);
}
// -----
while (ol_cnt > 0) {
  ol_cnt--;
}
// -----
});
function openct(e) {
  if (e.innerHTML == '[+]') {
// createTextNode
e.setAttribute('title', '收起');
e.innerHTML = '[-]';
var element = document.getElementById("outline_ol");
element.style.cssText = "margin-left:14px;padding-left:14px;line-height:160%;";
  } else {
e.setAttribute('title', '展开');
e.innerHTML = '[+]';
var element = document.getElementById("outline_ol");
element.style.cssText = "display:none;margin-left:14px;padding-left:14px;line-height:160%;";
  }
  e.blur();
  return false;
}
</script>

使用方式: copy 这段代码到: Tool -> Options -> Advanced -> Edit your html head(或类似button名) 即可.
实现后效果是这样:

上传时提示 SERVER(IO) ERROR 

展开后效果是这样:

上传时提示 SERVER(IO) ERROR 

2.2 细节调整
  • 对我而言, 尚有几个使用上的小问题, 首先在 MarkdownPad 编辑界面的直接预览中看不到这个目录.
  • 其次, 在 call 外部 browser 打开预览时, 倒是可以看到预览生成的目录, 但点击链接导航会失败: 显示错误是"XXX file 调用提示". 解决的方式是: 需要 Export Html, 然后点击生成TOC, 则展开的TOC 的任何一个标题, 都可顺利导航至相应的标题处。-- 如果我们很少写 JS 脚本(或者懒得去跟代码), 那么这个不便, 看起来也并非不可接受.
  • 还有, 这段脚本中, 贴心地为我们的 h1 标题栏(标题1), 增加了序号; 对 h2 标题栏增加了 'i' 字符作为标识. 但是对我这样的逻辑强迫症重度综合症来说, 根本用不着 -- 在稍长点文章后, 我自个儿就有强迫加序号, 或直接命名为"章节X", 或给一个 "$x" 标识等习惯.
  • 解决方式: 简单跟进这段代码 --大致上这段 JS 脚本分析了标题后, 以标题名做了一个目录 list, 每个item 也作了链接. 继续检查 ol, li 的格式, 注意到这是加序号的 list 风格. 因此, 我们需要加一行代码, 使用实心圆 type 替代序号 type 即可, 添加一行代码:

// 目录项文本前面放置对应的空格
a.innerHTML = header.textContent;
ele_li.appendChild(a);
ele_li.type = 'disc';    // add here with new disc type for the list item;

这是现在展开头部目录的样子:
上传时提示 SERVER(IO) ERROR 

跳转至: 使用JavaScript 生成目录(二)


后记: 这里的代码显示太痛苦了, 由于没有背景渲染, 左侧边亮色标识, 以及标识行数, 使得阅读较困难. 那么我暂时不再对这个讨论课题更新了.

计划中的写作, 应包括:

使用JavaScript 生成目录(二): 更简洁的头部目录的默认展开显示

使用JavaScript 生成目录(三): 左侧目录树的显示


其他讨论课题: JavaScript 实现左侧边代码行号标识

注意到面包板正在不断变化中, 待完善些我再来补充.

文章评论1条评论)

登录后参与讨论

ev711 2018-3-9 08:39

建议收到,我们会不断改进的~感谢分享
相关推荐阅读
allen_zhan 2023-02-27 19:08
对"三极管"译名由来的探讨
想讨论一个有意思的话题:今天中国大陆的电子业界, 为何将 BJT 称呼为 "三极管"? 或因其象形, 前辈自行进行随意的不严谨定义么? 带着疑问我们做了一下延伸查阅, 或得出这样的结论, 即中译名"三...
allen_zhan 2023-02-19 18:15
对知乎提问"为何三极管的一个PN结工作在反偏"的回复
将这个回复, 也发表在博文中, 作为自己的一个学习笔记叭.知乎问题: "三极管里面的PN结相当于二极管,为什么里面PN结加反向电压也能导通?"我的回复:首先, 二极管的"反向"概念, 容易给初学者某种...
allen_zhan 2023-02-18 10:17
从肖特基二极管到PN结与三极管
最近数个工作日的兴趣是回顾电子基础器件的发明/发展历史, 期待夯实技术基础的底蕴. 在学习与搜索资料的过程中, 顺便对知乎的一个同学的基础问题, 进行了回复. 不小心回复一下就成了千字文, 觉得挺有趣...
allen_zhan 2023-01-28 11:53
微功率 ISM 频率探讨相关文档组总结
不知不觉, 自开启关于微功率频率的话题起, 即从第一份文章写就到今天总结之日, 已经接近 10 个工作日左右. 早先的想法是对工程界未来的微功率设备相关项目, 从项目规划开始, 对选择系统, 频率, ...
allen_zhan 2023-01-27 22:50
关于 LoRa 应用场景的讨论
说明: 本文中斜体部分表示来自公告文件的部分内容剪贴或合并整理.1. "第52号文" 对 470MHz 的约束引自 如下:(四)民用计量仪表限在建筑楼宇、住宅小区及村庄等小范围内组网应用,任意时刻限...
allen_zhan 2023-01-25 13:24
ISM 频段中 2.4G 与 5.8GHz 设备的使用与限制
说明: 本文中斜体部分表示来自公告文件的部分内容剪贴或合并整理.1. ISM 频段定义中的 2.4G 与 5.8GHz正如同 文中确定的, 2.4G, 5.8GHz 属于中国大陆 ISM 频段的定义...
我要评论
1
25
关闭 站长推荐上一条 /2 下一条