html:

<template>
<div class="wrapper">
<div class="container">
<div class="menu">
<ul class="menu-list">
<li v-for="(nav, index) in navList"
              :key="index"
              :class="{on: activeIndex === index}"
              @click="currentClick(index)">
<a href="javascript:;" @click="pageJump(nav.index)">{{nav.title}}</a>
<div v-if="nav.children.length > 0 && activeIndex === index"
                class="menu-children-list">
<ul class="nav-list">
<li v-for="(item, idx) in nav.children"
                     :key="idx"
                     :class="{on: childrenActiveIndex === idx}"
                     @click.stop="childrenCurrentClick(idx)">
<a href="javascript:;" @click="pageJump(item.index)">
                        {{item.title}}
                      </a>
</li>
</ul>
</div>
</li>
</ul>
</div>
<div class="help-center-content" v-html="compiledMarkdown"
       ref="helpDocs" @scroll="docsScroll"></div>
</div>
</div>
</template>

js部分:

<script>

import marked from 'marked';

let rendererMD = new marked.Renderer();
marked.setOptions({
renderer: rendererMD,
gfm: true,
tables: true,
breaks: false,
pedantic: false,
sanitize: false,
smartLists: true,
smartypants: false
}); export default {
props: ['mdContent'],
data() {
return {
navList: [],
activeIndex: 0,
docsFirstLevels: [],
docsSecondLevels: [],
childrenActiveIndex: 0
}
},
mounted() {
this.navList = this.handleNavTree();
this.getDocsFirstLevels(0);
},
methods: {
childrenCurrentClick(index) {
this.childrenActiveIndex = index
},
getDocsFirstLevels(times) {
// 解决图片加载会影响高度问题
setTimeout(() => {
let firstLevels = [];
Array.from(document.querySelectorAll('h1'), element => {
firstLevels.push(element.offsetTop - 60)
})
this.docsFirstLevels = firstLevels; if (times < 8) {
this.getDocsFirstLevels(times + 1);
}
}, 500);
},
getDocsSecondLevels(parentActiveIndex) {
let idx = parentActiveIndex;
let secondLevels = [];
let navChildren = this.navList[idx].children if(navChildren.length > 0) {
secondLevels = navChildren.map((item)=>{
return this.$el.querySelector(`#data-${item.index}`).offsetTop - 60
})
this.docsSecondLevels = secondLevels;
}
},
docsScroll() {
if (this.titleClickScroll) {
return;
} let scrollTop = this.$refs.helpDocs.scrollTop
let firstLevelIndex = this.getLevelActiveIndex(scrollTop, this.docsFirstLevels)
this.currentClick(firstLevelIndex) let secondLevelIndex = this.getLevelActiveIndex(scrollTop, this.docsSecondLevels)
this.childrenCurrentClick(secondLevelIndex)
},
getLevelActiveIndex(scrollTop, docsLevels) {
let currentIdx = null;
let nowActive = docsLevels.some((currentValue, index) => {
if(currentValue >= scrollTop) {
currentIdx = index
return true
}
}) currentIdx = currentIdx - 1 if (nowActive && currentIdx === -1) {
currentIdx = 0
} else if (!nowActive && currentIdx === -1) {
currentIdx = docsLevels.length - 1
}
return currentIdx
},
pageJump(id) {
this.titleClickScroll = true;
this.$refs.helpDocs.scrollTop = this.$el.querySelector(`#data-${id}`).offsetTop - 40;
setTimeout(() => this.titleClickScroll = false, 100);
},
currentClick(index) {
this.activeIndex = index
this.getDocsSecondLevels(index)
},
getTitle(content) {
let nav = []; let tempArr = [];
content.replace(/(#+)[^#][^\n]*?(?:\n)/g, function(match, m1, m2) {
let title = match.replace('\n', '');
let level = m1.length;
tempArr.push({
title: title.replace(/^#+/, '').replace(/\([^)]*?\)/, ''),
level: level,
children: [],
});
}); // 只处理一级二级标题,以及添加与id对应的index值
nav = tempArr.filter(item => item.level <= 2);
let index = 0;
return nav = nav.map(item => {
item.index = index++;
return item;
});
},
// 将一级二级标题数据处理成树结构
handleNavTree() {
let navs = this.getTitle(this.content)
let navLevel = [1, 2];
let retNavs = [];
let toAppendNavList; navLevel.forEach(level => {
// 遍历一级二级标题,将同一级的标题组成新数组
toAppendNavList = this.find(navs, {
level: level
}); if (retNavs.length === 0) {
// 处理一级标题
retNavs = retNavs.concat(toAppendNavList);
} else {
// 处理二级标题,并将二级标题添加到对应的父级标题的children中
toAppendNavList.forEach(item => {
item = Object.assign(item);
let parentNavIndex = this.getParentIndex(navs, item.index);
return this.appendToParentNav(retNavs, parentNavIndex, item);
});
}
});
return retNavs;
},
find(arr, condition) {
return arr.filter(item => {
for (let key in condition) {
if (condition.hasOwnProperty(key) && condition[key] !== item[key]) {
return false;
}
}
return true;
});
},
getParentIndex(nav, endIndex) {
for (var i = endIndex - 1; i >= 0; i--) {
if (nav[endIndex].level > nav[i].level) {
return nav[i].index;
}
}
},
appendToParentNav(nav, parentIndex, newNav) {
let index = this.findIndex(nav, {
index: parentIndex
});
nav[index].children = nav[index].children.concat(newNav);
},
findIndex(arr, condition) {
let ret = -1;
arr.forEach((item, index) => {
for (var key in condition) {
if (condition.hasOwnProperty(key) && condition[key] !== item[key]) {
return false;
}
}
ret = index;
});
return ret;
},
},
computed: {
content() {
return this.mdContent
},
compiledMarkdown: function() {
let index = 0;
rendererMD.heading = function(text, level) {
if (level <= 2) {
return `<h${level} id="data-${index++}">${text}</h${level}>`;
} else {
return `<h${level}>${text}</h${level}>`;
}
};
rendererMD.code = function(code, language) {
code = code.replace(/\r\n/g,"<br>")
code = code.replace(/\n/g,"<br>");
return `<div class="text">${code}</div>`;
};
return marked(this.content);
}
}
}
</script>

参考链接:

https://github.com/markedjs/marked

https://www.jianshu.com/p/d182ea991609

https://hk.saowen.com/a/bf975e4296e33a14e2d0ad50aa7cbf24fbfb4a9fb851de171b4c71da54eb95e5

vue使用marked.js实现markdown转html并提取标题生成目录的更多相关文章

  1. Angular 利用 marked.js 添加 Markdown + HTML 同时渲染的 Pipe

    背景 最近在公司开发的一个项目需要在 Angular 上展示图文,并且需要同时支持 Markdown 和 HTML 对于同时支持 Markdown 和 HTML ,应该要分为编辑和渲染两部分考虑. 对 ...

  2. marked.js简易手册

    marked.js简易手册 本文介绍的是marked.js.秉持"来之即用"的原则,对它进行简要的翻译和归纳, 安装 在网上引用或者是引用本地文件即可.要么就用命令行: npm i ...

  3. 使用 Vue 和 epub.js 制作电子书阅读器

    ePub 简介 ePub 是一种电子书的标准格式,平时我看的电子书大部分是这种格式.在手机上我一般用"多看"阅读 ePub 电子书,在 Windows 上找不到用起来比较顺心的软件 ...

  4. js解析MarkDown语法

    1.问题描述: 我们使用MarkDown编辑器之后,比如我们写的MarkDown的语法是:  # 一级标题  ## 二级标题  ### 三级标题 这种语法我们最终要转换成HTML的格式最终要存入数据库 ...

  5. vue 将markdown字符串转html、修改主题、生成目录

    前言 将 markdown 字符串转成 html 显示出来,同时把目录也提取出来一起显示.可以使用 marked 来读取 markdown 字符串解析成 html marked官网:https://m ...

  6. Vue过渡效果之JS过渡

    前面的话 与CSS过渡不同,JS过渡主要通过事件进行触发.本文将详细介绍Vue过渡效果之JS过渡 事件钩子 JS过渡主要通过事件监听事件钩子来触发过渡,共包括如下的事件钩子 <transitio ...

  7. socket应用(vue、node.js、M站)

    socket应用(vue.node.js.M站) 前言:我们在做一些项目的时候需要做到实时变化, 比如我们有时候有需求会要求我们做一个类似于聊天室的页面 比如有些时候我们对某些东西进行点赞和刷票,需要 ...

  8. vue项目中主要文件的加载顺序(index.html、App.vue、main.js)

    先后顺序: index.html > App.vue的export外的js代码 > main.js > App.vue的export里面的js代码 > Index.vue的ex ...

  9. vue.runtime.esm.js:593 [Vue warn]: Invalid prop: custom validator check failed for prop "value".报错解决

    在uni中使用 picker组件,一直报错 vue.runtime.esm.js:593 [Vue warn]: Invalid prop: custom validator check failed ...

随机推荐

  1. 面对即将终止支持的server你还能做些什么

    watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaXF1c2hp/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/d ...

  2. 关于Javascript的forEach 和 map

    本篇博客转载自 https://blog.fundebug.com/2018/02/05/map_vs_foreach/ 如果你已经有使用JavaScript的经验,你可能已经知道这两个看似相同的方法 ...

  3. BZOJ 1577 贪心

    思路:同POJ3038 http://blog.csdn.net/qq_31785871/article/details/52953214 //By SiriusRen #include <se ...

  4. 负载均衡-lvs

    常用的负载均衡技术比较DNS 轮询DNS本身的机制不再赘述,这里主要看一看基于DNS的负载均衡,其大致原理很清楚,DNS系统本身支持同一个域名映射到多个ip (A记录),例如 这样每次向DNS系统询问 ...

  5. jdbc参数传递

    1.jdbc请求设置 将查询结果第一列coupon_id,存放在couponId中; 将查询结果第二列code,存放在coupCode中 2.参数解释: couponId_#:表示查询结果中coupo ...

  6. MessageFormat

    MessageFormat mf = new MessageFormat("{0,number,#.##}, {0,number,#.#}"); Object[] objs = { ...

  7. Huawei设备配置系统时钟

    系统时钟是设备上的系统时间戳.由于地域的不同,用户可以根据当地规定设置系统时钟.用户必须正确设置系统时钟以确保其与其他设备保持同步.华为设备出厂时默认采用了协调世界时(UTC),但是没有配置时区所有在 ...

  8. 通过域 组策略禁用U盘(只允许部份许可U盘可在客户端读取)

    U盘禁用设置步骤: 1.客户端 注册表修改,USBStor  的start改为4 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\UsbSto ...

  9. Codeforces Round #240 (Div. 2) 题解

    A: 1分钟题,往后扫一遍 int a[MAXN]; int vis[MAXN]; int main(){ int n,m; cin>>n>>m; MEM(vis,); ; i ...

  10. BFC 和 haslayout

    在解释 BFC 是什么之前,需要先介绍 Box.Formatting Context的概念. Box: CSS布局的基本单位 Box 是 CSS 布局的对象和基本单位, 直观点来说,就是一个页面是由很 ...