MarkdownPad2导出HTML支持[TOC]

MarkdownPad2导出HTML支持[TOC]

请点击MarkdownPad2_toc查看效果。

为什么&效果图

为什么

为什么要吃力不讨好的实现这个功能呢?

  • [TOC]语法挺有用的,特别是标题较多的时候
  • 百度上并未找到MarkdownPad2导出HTML支持[TOC]的完美方案
  • 顺便锻炼一下JavaScript

效果图

想要实现什么样的效果呢?No picture, no bibi.

impressionDrawing.png

最后测试用的Markdown语法栗子内容如下:

# 《Vue2.0开发去哪儿网App》知识点
[TOC]
## 深入理解Vue组件
### 4-8 动态组件与v-once指令
#### 动态组件
#### v-once指令
## Vue中的动画特效
### 5-1 Vue动画 - Vue中的CSS动画原理
#### 动画原理
### 5-2 在Vue中使用animate.css库
#### keyframes动画
#### 自定义类名
#### animate.css库

动手实现

开始动手实现吧~

实现的HTML效果

等等,莫急,方案都没想好,怎么编码...

例如,Markdown语法如下

# 一
[TOC]
## 二
### 三
## 二

我们期望展示的效果如下图,且发现Markdown语法中的###等标题最终转换为HTML中的

等元素。

[TOC]转换为

[TOC]

,显然

[TOC]

无法满足我们的期望,所以这里就是我们需要编码实现的地方。

htmlDom.png

那么,我们要将

[TOC]

,转换成什么样子呢?

为啥要转换成这样子呢?因为这样子就能显示我们想要的效果呀。

为啥ulli元素有一个data-level属性呢?看名字就知道是为了方便知道目前所在的层级。

那么我们先把上面的理解一下吧

首先,将p元素的[TOC]替换为div元素,且该div元素有一个叫toc的class

接着,要实现层次效果,我们需要用到ulli元素,搭建一级标题吧

放到chrome中看看效果呢

data-level=1.png

继续搭,不准停...

继续放到chrome中

data-level=2.png

那如果有两个标题二呢?再加个li

继续放到chrome中,很棒

data-level=2_2.png

如果又要在第一个标题二下加一个标题三呢?

data-level=3.png

想要的效果就完成啦,棒棒哒~

那么我们通过JavaScript要实现的过程大概也就是酱紫滴

编码

边编码,边讲解方案

初始HTML




    
    MarkdownPad2导出HTML支持[TOC]
    


    

[TOC]

引入jquery.js,加入h1ph2等元素,效果如下

codeInitial.png

整改p元素

步骤:

  1. 创建class为toc的div元素
  2. 找到p元素,怎么找通过[TOC]来找p元素
  3. 将p元素的文本元素清空,并将步骤1中的div元素append到该p元素上

如果Markdown文档中有多个[TOC]语法,我们只取第一个,其余的不做转换,谁没事搞这么多个[TOC]呢,是不?当然有人站出来说,一定要有多个[TOC],行行行,那你把:first出去就行了。

另外,这个代码还有点小缺陷,如果有些人习惯用[toc]呢?发现

[toc]

并没有变化,所以需要整改一下


新增了一个:Contains,与:contains功能一样增加忽略大小写功能,这样就算写成[toc]也是没问题了,效果如下

htmlP.png

实现hx元素转换

下一步当然就是得往

中append一些ulli元素了

处理第一个hx元素的步骤:

  1. 获取Markdown文档中所有hx元素$headers
  2. 获取第一个hx元素firstHeader,并获取第一个hx元素的层级firstLevel
  3. 对firstLevel循环处理,创建ul、li元素并按层级append
  4. ul、li元素append完成后,找到firstLevel对应的li元素,并append一个a元素用于显示标题内容
// 获取h1-h6元素
let $headers = $('h1,h2,h3,h4,h5,h6');

// 获取第一个hx元素,注意是dom对象,非jQuery对象
let firstHeader = $headers[0];

if (firstHeader) {
    // 获取第一个hx元素的层级
    let firstLevel = parseInt(firstHeader.tagName.
        replace('H', ''), 10);
    // 给a元素唯一的href属性值
    let id = `${firstHeader.textContent}-0`;

    for (let i = 1; i <= firstLevel; i++) {
        // 创建ul和li元素,并添加data-level属性
        let $ul = $('
    '), $li = $('
  • '); $li.attr('data-level', i); $ul.attr('data-level', i).append($li); // 获取data-level为i-1的li元素 let $pLi = $toc.find(`li[data-level=${i - 1}]`); if ($pLi.length > 0) { // 找到data-level为i-1的元素,直接append到该li元素上即可 $pLi.append($ul); } else { // 未找到data-level为i-1的元素,说明是顶层的ul, // 直接append到class为toc的div元素上 $toc.append($ul); } } // 找到所属层级的li元素,并添加a元素 $toc.find(`li[data-level=${firstLevel}]`).append( $(`${firstHeader.textContent}`)); // 与a元素的href对应,用于文档内跳转 $headers.eq(0).attr('id', id); }

    $headers内容如下:

    jqHeaders.png

    界面效果如下:

    htmlFirstHeader1.png

    如果Markdown文档中没有从#开始,直接用了##,那么我们试下效果(相当于把文档中的

    删除)

    htmlFirstHeader2.png

    效果ok,就是有点小问题,a元素在chrome中呈现下划线效果,建议去掉,所以增加以下代码,把原来的

    加回来,嗯,现在效果完美了

    
    
    htmlFirstHeader3.png

    处理后续hx元素的步骤:

    1. 从第二个hx元素开始,循环
    2. 获取当前hx的层级以及上一个hx的层级
    3. 当当前层级大于上一个层级,则从上一个层级+1到当前层级做循环添加ul和li元素
    4. 如果当前层级和上一个层级相等,则找到相同层级的li元素的父元素,创建li元素并添加上去
    5. // 如果当前层级小于上一个层级,则找到最后一个相同层级的li元素的父元素,添加$l
    6. 设置hx元素的id,与a的href保持一致
    // 从第二个hx元素开始,循环$headers
    for (let i = 1; i < $headers.length; i++) {
        // 获取当前的hx元素的层级以及上一个hx元素的层级
        let curLevel = parseInt($headers[i].tagName.replace('H', ''),
            10),
            lastLevel = parseInt($headers[i - 1].tagName.
                replace('H', ''), 10);
        // 当前hx元素的文本元素
        let textContent = $headers[i].textContent,
            id = `${textContent}-i`;
        // 给a元素唯一的href属性值,与hx元素的id对应
        let $a = $(`${textContent}`);
    
        if (lastLevel < curLevel) {
            // 如果当前层级比上一个层级大,则从上一个层级+1到当前层级循环,
            // 循环添加ul和li元素
            for (let j = lastLevel + 1; j <= curLevel; j++) {
                // 创建ul和li元素,并添加data-level属性
                let $u = $('
      '), $l = $('
    • '); $l.attr('data-level', j); $u.attr('data-level', j).append($l); // 找到data-level为j-1的最后一个li元素,并添加$u $toc.find(`li[data-level=${j - 1}]:last`).append($u); } // 找到data-level为curLevel的最后一个li,并添加$a $toc.find(`li[data-level=${curLevel}]:last`).append($a); } else if (lastLevel === curLevel) { // 如果当前层级和上一个层级相等,则找到相同层级的li元素的父元素, // 创建li元素并添加上去 // 已经存在data-level为curLevel的ul,只需要创建li即可 let $l = $('
    • '); // 给li元素添加data-level树形,并添加$a // 找到data-level为curLevel的最后一个li元素的父元素,添加$l $toc.find(`li[data-level=${curLevel}]:last`).parent(). append($l.attr('data-level', curLevel).append($a)); } else { // 如果当前层级小于上一个层级,则找到最后一个相同层级的li元素的 // 父元素,添加$l // 找到data-level为curLevel的最后一个li元素,必然存在 let $sublingLi = $toc. find(`li[data-level=${curLevel}]:last`); let $l = $('
    • '); // 找到$sublingLi的父元素,添加li元素即可 $sublingLi.parent().append($l.attr('data-level', curLevel). append($a)); } $headers.eq(i).attr('id', id); }

      全部代码如下:

      
      
      
          
          MarkdownPad2导出HTML支持[TOC]
          
          
      
      
          

      [TOC]

      效果如下:

      htmlHeaders.png

      MarkdownPad2效果

      以上代码编写完毕,但只是一个demo,我们的目的是为了让MarkdownPad2导出HTML时支持[TOC],所以,先删除不必要的代码

      
      
      

      然后打开MarkdownPad2,工具-->选项-->高级-->HTML Head编辑器,将上述代码拷贝至代码编辑器中,保存并关闭-->保存并关闭

      MarkdownPad2左侧填入一下内容:

      # 《Vue2.0开发去哪儿网App》知识点
      [TOC]
      ## 深入理解Vue组件
      ### 4-8 动态组件与v-once指令
      #### 动态组件
      #### v-once指令
      ## Vue中的动画特效
      ### 5-1 Vue动画 - Vue中的CSS动画原理
      #### 动画原理
      ### 5-2 在Vue中使用animate.css库
      #### keyframes动画
      #### 自定义类名
      #### animate.css库
      

      文件-->导出-->导出HTML,填写文件名,保存,效果如下:

      result.png

      GitHub项目

      GitHub项目地址:https://github.com/02954/markdownpad2_toc

      你可能感兴趣的:(MarkdownPad2导出HTML支持[TOC])