Let’s start with the results

Making github.com/li328250157…




Page layout

The web layout is divided into three parts, respectively

Header, fixed position

Aside,margin-top is the height of the header

Contain, static position, margin-top = header height, margin-left = aside width, router-view exit Divided into two parts:

Main content, display md converted HTML page, margin-right value is md directory width value

Extract markdown’s H1 and H2 directories for title navigation, fixed positioning

function

In order for MD to be well displayed on web pages, it should have the following functions:

Click the menu on the left to get the corresponding MD content (string format), convert the MD content into HTML, and add anchor point ID for the first and second level titles

To add md format to HTML, introduce a CSS, refer to the url

Extract the first and second level titles in MD and display the article table of contents on the right

Click on the right side of the article catalog, the left content can be located to the corresponding position, but also do smooth scrolling processing, enhance the user experience

As the content on the left scrolls, the activation items in the right directory change dynamically


Md into HTML

I use marked. Js to convert md to HTML and add id values for H1 and H2 here as anchor points

// Install marked. Js locally

npm install marked –save

// Introduce marked inside the component

import marked from ‘marked’;

// Marked basic Settings

marked.setOptions({

    renderer: rendererMD,

    gfm: true,

    tables: true,

    breaks: false,

    pedantic: false,

    sanitize: false,

    smartLists: true,

    smartypants: false

});


/ / instantiate

let rendererMD = new marked.Renderer();

// In the calculated attributes, process the MD h1, H2, add the id value, and marked to HTML

computed: {

    compiledMarkdown: function() {

        let index = 0;

        rendererMD.heading = function(text, level) {

            if (level < 3) {

                return `<h${level} id=”${index++}” class=”jump” >${text}</h${level}>`;

            } else {

                return `<h${level}>${text}</h${level}>`;

            }

        };

        return marked(this.content);

    }

}

In HTML, you can bind the calculated property with V-HTML

<div class=”markdown-body” ref=”content” id=”content” v-html=”compiledMarkdown”>


Extract the title

getTitle(content) {

    let nav = [];

    let navLevel = [1, 2];

    let tempArr = [];

    content

        .replace(/“`/g, function(match) {

            return ‘\f’;

        })

.replace(/\f[^\f]*? \f/g, function(match) {

            return ”;

        })

        .replace(/\r|\n+/g, function(match) {

            return ‘\n’;

        })

// Start with at least one #, followed by lazy matching of any character other than a non-newline character, followed by a newline character

.replace(/(#+)[^#][^\n]*? (? :\n)/g, function(match, m1) {

            let title = match.replace(‘\n’, ”);

            let level = m1.length;

            tempArr.push({

title: title.replace(/^#+/, ”).replace(/\([^)]*? \)/”),

                level: level,

                children: []

            });

        });

// tempArr gets all the 1-6 level titles, filtering out the first and second levels

    nav = tempArr.filter(_ => _.level <= 2);

    let index = 0;

// Add index here, which corresponds to the id bound in the tag

    nav = nav.map(_ => {

        _.index = index++;

        return _;

    });

    let retNavs = [];

    let toAppendNavList;

    navLevel.forEach(level => {

// Iterate over the first and second level headings to form a new array with the elements of the same level

        toAppendNavList = this.find(nav, {

            level: level

        });

        if (retNavs.length === 0) {

// Handle level 1 headings

            retNavs = retNavs.concat(toAppendNavList);

        } else {

// Process the secondary title and add the secondary title to the children of the corresponding parent node

            toAppendNavList.forEach(_ => {

                _ = Object.assign(_);

                let parentNavIndex = this.getParentIndex(nav, _.index);

                return this.appendToParentNav(retNavs, parentNavIndex, _);

            });

        }

    });

// retNavs are processed trees

    return retNavs;

},

// Process the headings belonging to the same level to form an array

find(arr, condition) {

    return arr.filter(_ => {

        for (let key in condition) {

if (condition.hasOwnProperty(key) && condition[key] ! == _[key]) {

                return false;

            }

        }

        return true;

    });

},

// Get the parent of this node

getParentIndex(nav, endIndex) {

// Select * from index where level = 1; // Select * from index where level = 1;

    for (var i = endIndex – 1; i >= 0; i–) {

        if (nav[endIndex].level > nav[i].level) {

            return nav[i].index;

        }

    }

},

// Find all children of the same parent node

appendToParentNav(nav, parentIndex, newNav) {

// Find the index of each secondary heading

    let index = this.findIndex(nav, {

        index: parentIndex

    });

    if (index === -1) {

// We are dealing with level 3 and below headings

// If the parent node is not found in the first level header, go to children in each of the first level headers

        let subNav;

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

// Handle the case where there is no parent node

            subNav = nav[i];

            subNav.children.length && this.appendToParentNav(subNav.children, parentIndex, newNav);

        }

    } else {

        nav[index].children = nav[index].children.concat(newNav);

    }

},

// Find a member of the array that matches the criteria

findIndex(arr, condition) {

    let ret = -1;

    arr.forEach((item, index) => {

        for (var key in condition) {

if (condition.hasOwnProperty(key) && condition[key] ! == item[key]) {// no deep comparison

                return false;

            }

        }

        ret = index;

    });

    return ret;

},

Md directory display and anchor location

<div id=”menu”>

    <ul class=”nav-list”>

        <li v-for=”(nav, index) in contentMenu” :key=”index”>

            <a :href=”‘#’ + nav.index” :class=”{‘active’: highlightIndex === nav.index}” @click=”handleHighlight(nav.index)” :key=”nav.index”>{{nav.title}}

            </a>

            <template v-if=”nav.children.length > 0″>

                <ul class=”nav-list”>

                    <li v-for=”(item, index) in nav.children” :key=”index”>

                        <a :href=”‘#’ + item.index” :class=”{active: highlightIndex === item.index}” :key=”item.index” @click=”handleHighlight(item.index)”>{{item.title}}

                        </a>

                    </li>

                </ul>

            </template>

        </li>

    </ul>

</div>

Smooth scrolling

In the directory of MD, the a tag has already set the href value, conducted anchor location, and smoothed the event of clicking the directory binding

handleHighlight(item) {

    this.highlightIndex = item;

    let jump = document.querySelectorAll(‘.jump’);

// 60 is the height of the header

    let total = jump[item].offsetTop – 60;

    let distance = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop;

// Smooth roll, length 500ms, one hop every 10ms, total 50 hops

    let step = total / 50;

    if (total > distance) {

        smoothDown();

    } else {

        let newTotal = distance – total;

        step = newTotal / 50;

        smoothUp();

    }

    function smoothDown() {

        if (distance < total) {

            distance += step;

            document.body.scrollTop = distance;

            document.documentElement.scrollTop = distance;

            setTimeout(smoothDown, 10);

        } else {

            document.body.scrollTop = total;

            document.documentElement.scrollTop = total;

        }

    }

    function smoothUp() {

        if (distance > total) {

            distance -= step;

            document.body.scrollTop = distance;

            document.documentElement.scrollTop = distance;

            setTimeout(smoothUp, 10);

        } else {

            document.body.scrollTop = total;

            document.documentElement.scrollTop = total;

        }

    }

}

The main contents scroll and the contents are highlighted

As the scroll bar changes as you read the MD content, the highlighted items in the table of contents change

mounted() {

    this.$nextTick(function() {

        window.addEventListener(‘scroll’, this.onScroll);

    });

},

methods: {

    onScroll() {

        let top = document.documentElement ? document.documentElement.scrollTop : document.body.scrollTop;

        let items = document.getElementById(‘content’).getElementsByClassName(‘jump’);

        let currentId = ”;

        for (let i = 0; i < items.length; i++) {

            let _item = items[i];

            let _itemTop = _item.offsetTop;

            if (top > _itemTop – 75) {

                currentId = _item.id;

            } else {

                break;

            }

        }

        if (currentId) {

// The currentOId is a string that must be converted to a number otherwise the congruence of the highlighted item will not match

            this.highlightIndex = parseInt(currentId);

        }

    }

}

Summary

This is how to make md well displayed on the web page all the features, Google many times, thanks to share experience of Ariba Dei, open source long live ~!

Points to be improved include:

How do I avoid regular matches for non-heading #, such as // #, which is not in the heading format

The processing of H1 and H2 tags in MD, which allows HTML, now does not satisfy tags matching H1 and H2