zoukankan      html  css  js  c++  java
  • 【vue】使用marked.js实现markdown转html并提取标题生成目录

    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(/(#+)[^#][^
    ]*?(?:
    )/g, function(match, m1, m2) {
                        let title = match.replace('
    ', '');
                        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(/
    /g,"<br>")
                    code = code.replace(/
    /g,"<br>");
                    return `<div class="text">${code}</div>`;
                };
                return marked(this.content);
            }
        }
    }
    </script>
    

    github地址: https://github.com/markedjs/marked

  • 相关阅读:
    在前端眼中pc端和移动的开发区别
    两个对象的合并
    关于后台传来的json是含英文字母的string
    关于jquery的取消阻止默认事件
    练习:模态对话框
    JS之DOM节点增删改查与属性设置
    JS之DOM Event(事件)
    JS的DOM对象之DOM节点
    JS的history对象和location对象
    JS的BOM对象之window对象,利用window对象方法实现定时器
  • 原文地址:https://www.cnblogs.com/richerdyoung/p/13942664.html
Copyright © 2011-2022 走看看