没啥用的纯前端打造一个实时 markdown 编辑器

通过纯前端打造一个实时 markdown 编辑器,用到的库或框架主要有 marked,Ace,highlight.js 及 Bootstrap。

请添加图片描述



简要步骤

  1. 使用 HTML+CSS 完成简单页面布局
  2. 使用 JavaScript 进行编辑器初始化
  3. 使用localStorage进行数据的缓存
  4. 使用highlight.js库实现代码高亮

一、开发准备

  • 下载bootstrap:
    使用 bootstrap 需要jQuery,所以也要下载jQuery
    https://webide.hz-iframe.simplelab.cn/webide-eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJob3N0IjoiMTcyLjE2LjU2LjI1IiwicG9ydCI6IjQwNzYwIn0.faEhgCQTx3NR3NT_2IWlivoo1nP0ZVavEI-bwGnP2Hw/files/download/?id=a936f0ba-b9ad-498c-9781-1b35db5e5c25

  • 下载Jquery:
    https://webide.hz-iframe.simplelab.cn/webide-eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJob3N0IjoiMTcyLjE2LjU2LjI1IiwicG9ydCI6IjQwNzYwIn0.faEhgCQTx3NR3NT_2IWlivoo1nP0ZVavEI-bwGnP2Hw/files/download/?id=bbb1f0e5-6c3b-41ab-b6d8-7eede8fe136b

  • 下载 marked
    https://webide.hz-iframe.simplelab.cn/webide-eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJob3N0IjoiMTcyLjE2LjU2LjI1IiwicG9ydCI6IjQwNzYwIn0.faEhgCQTx3NR3NT_2IWlivoo1nP0ZVavEI-bwGnP2Hw/files/download/?id=3a48c4f7-cd02-4ca0-a558-97d35ab889d4

  • 下载 highlightjs:
    https://webide.hz-iframe.simplelab.cn/webide-eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJob3N0IjoiMTcyLjE2LjU2LjI1IiwicG9ydCI6IjQwNzYwIn0.faEhgCQTx3NR3NT_2IWlivoo1nP0ZVavEI-bwGnP2Hw/files/download/?id=bef71564-3fbb-4905-8743-aea87f466496

  • 下载 Ace:
    https://webide.hz-iframe.simplelab.cn/webide-eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJob3N0IjoiMTcyLjE2LjU2LjI1IiwicG9ydCI6IjQwNzYwIn0.faEhgCQTx3NR3NT_2IWlivoo1nP0ZVavEI-bwGnP2Hw/files/download/?id=70ff95b3-f842-4dcc-aded-f5a9482354ba

  • 最终文件结构
    在这里插入图片描述

二、开始编码

准备工作已经完成了,下面开始正式的编码。从页面布局开始,逐步完成页面样式、初始化、解析 markdown、代码高亮等功能模块。

1.页面布局

HTML 很简单,主要定义了两个 div,一个作为 markdown 编辑器,一个作为 markdown 预览框。创建index.html,输入如下代码:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>markdown editor</title>

    <!-- 引入CSS文件 -->
    <link rel="stylesheet" href="libs/bootstrap/css/bootstrap.min.css" />
    <link rel="stylesheet" href="libs/highlightjs/default.min.css" />
    <link rel="stylesheet" href="libs/highlightjs/monokai_sublime.min.css" />
    <link rel="stylesheet" href="style.css" />

    <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
    <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
    <!--[if lt IE 9]>
    <script src="http://cdn.bootcss.com/html5shiv/3.7.2/html5shiv.min.js"></script>
    <script src="http://cdn.bootcss.com/respond.js/1.4.2/respond.min.js"></script>
    <![endif]-->
</head>

<body>
<div class="container-fluid">
    <div class="row">
        <!-- markdown编辑器div -->
        <div class="col-md-6" id="md-editor"></div>
        <!-- markdown预览框div -->
        <div class="col-md-6" id="md-viewer"></div>
    </div>
</div>
<!-- 引入JavaScript文件 -->
<script src="libs/jquery.min.js"></script>
<script src="libs/bootstrap/js/bootstrap.min.js"></script>
<script src="libs/ace/ace.js"></script>
<script src="libs/marked.min.js"></script>
<script src="libs/highlightjs/highlight.min.js"></script>
<script src="main.js"></script>
</body>
</html>

2.页面样式

然后我们来加一点简单的 CSS,创建 style.css,设置编辑器和预览框各占 50% 宽度,编辑器在左边,预览框在右边。输入如下代码:

/* 编辑器 */
#md-editor {
      
      
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    font-size: 16px;
}

/* 预览框 */
#md-viewer {
      
      
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 50%;
    overflow-y: scroll;
}

3.初始化

编辑器是使用的 Ace 编辑器,创建 main.js,输入如下代码:

  • 这里我们创建了一个初始化编辑器的函数,并且设置了编辑器的主题样式以及模式等。ace.edit(‘md-editor’) 返回的是 Class: Editor 的一个实例,通过 Class: Editor 的 setTheme() 方法设置编辑器主题。
  • editor.getSession() 返回的是 Class: EditSession 的一个实例,EditSession 有一系列的方法,可以设置编辑器模式,Tab 键转化为 4 个空格,自动换行等等,更多详细设置可以去 Ace 的官网看文档。
initEditor();

/**
 * 初始化编辑框
 *
 */
function initEditor() {
    
    
    // 初始化编辑器
    var editor = ace.edit('md-editor');

    editor.setTheme('ace/theme/monokai'); // 设置主题样式
    editor.getSession().setMode('ace/mode/markdown'); // 设置编辑器模式
    editor.getSession().setTabSize(4); // 设置 Tab 为4个空格
    editor.getSession().setUseWrapMode(true); // 自动换行
}

现在可以用浏览器打开 index.html ,可以看到编辑器了,而且可以输入内容。
在这里插入图片描述

4.解析markdown

下面我们来编写解析 markdown 的方法,输入如下代码:

/**
 * 解析markdown
 *
 * @params {object} editor 编辑器
 * @return {object} 预览框
 */
function parseMarkdown(editor) {
    
    
    var viewer = $('#md-viewer'); // 文档预览框
    var data = editor.getValue(); // 获取编辑器数据

    // 保存数据到本地
    localStorage.localData = data;
    // 解析 markdown
    data = marked(data);
    viewer.html(data);
}
  • 解析 markdown 使用的是 marked.js 库,看上面的代码就知道使用的方法很简单,调用 marked(data) 就可以了。
  • 但是,我们要实现实时解析 markdown,也就是说要在编辑器中每输入一个字符,就会自动解析并在预览框中显示出来。
  • 那么我们只需要给编辑器绑定一个 change 事件就可以了,当编辑器中的内容发生变化时就调用 parseMarkdown() 方法解析一次,这样就是实时的了。

main.js:

// 初始化编辑器
initEditor();

/**
 * 初始化编辑框
 *
 */
function initEditor() {
    
    
    // 初始化编辑器
    var editor = ace.edit('md-editor');

    editor.setTheme('ace/theme/monokai'); // 设置主题样式
    editor.getSession().setMode('ace/mode/markdown'); // 设置编辑器模式
    editor.getSession().setTabSize(4); // 设置 Tab 为4个空格
    editor.getSession().setUseWrapMode(true); // 自动换行

    // 加载本地缓存数据
    editor.setValue(localStorage.localData || '');

    // 解析从本地加载的缓存数据
    parseMarkdown(editor);

    // 绑定 change 事件
    // 即时解析 markdown
    editor.getSession().on('change', function (e) {
    
    
        parseMarkdown(editor);
    });
}

/**
 * 解析markdown
 *
 * @params {object} editor 编辑器
 * @return {object} 预览框
 */
function parseMarkdown(editor) {
    
    
    var viewer = $('#md-viewer'); // 文档预览框
    var data = editor.getValue(); // 获取编辑器数据

    // 保存数据到本地
    localStorage.localData = data;
    // 解析 markdown
    data = marked(data);
    viewer.html(data);
}

我们用 localStorage 把每次修改后的数据都保存在了本地缓存中,这样即使刷新浏览器数据也不会丢失了。

现在已经可以实时解析 markdown 了,可以打开浏览器试试。
在这里插入图片描述

5.代码高亮

下面我们来美化一下 markdown 中的代码块, 这样预览框中的会更加好看。
我们使用 highlight.js 库来高亮代码,

highlight.js
主要有两个常用方法:hljs.initHighlightingOnLoad(); 和 hljs.highlightBlock();

  • 第一个方法:用于页面加载完后,需要高亮的代码文档不会被改变的情况。
  • 第二个方法:主要用于需要高亮的代码随时会被改变的情况。所以我们这里使用第二个方法。

修改 parseMarkdown() 方法代码:

/**
 * 解析markdown
 *
 * @params {object} editor 编辑器
 * @return {object} 预览框
 */
function parseMarkdown(editor) {
    
    
    var viewer = $('#md-viewer'); // 文档预览框
    var data = editor.getValue(); // 获取编辑器数据

    // 保存数据到本地
    localStorage.localData = data;
    // 解析 markdown
    data = marked(data);
    viewer.html(data);

    // 高亮 markdown 文档中的代码
    $('pre > code', viewer).each(function () {
    
    
        hljs.highlightBlock(this);
    });
}

到这里,一个简单的 markdown 编辑器已经成型了。

6.滚动条同步

为了让体验更好,我们再加一个功能,让两边的滚动条同步,也就是滚动左边或者右边的文档时,另一边的也会跟着滚动。
在 main.js 中添加如下函数:

/*
 * 控制滚动条
 * 使编辑器和预览框同时滚动
 *
 * @params {object} editor 编辑器
 * @params {object} viewer 预览框
 */
function fixScrollBar(editor, viewer) {
    
    
    var session = editor.getSession();

    // 第一次加载页面时
    // 默认滚动到第一行
    session.setScrollTop(0);

    // 编辑器绑定滚动事件
    session.on('changeScrollTop', function () {
    
    
        var sTop = session.getScrollTop();
        // 设置预览框的滚动条
        viewer.scrollTop(sTop);
    });

    // 预览框定滚动事件
    viewer.on('scroll', function () {
    
    
        var sTop = viewer.scrollTop();
        // 设置编辑器的滚动条
        session.setScrollTop(sTop);
    });
}
  • 在这个函数中,我们可以看到,给编辑器和预览框都绑定了 scroll 方法,滚动的时候,会改变另一边的 scrollTop 值,这样两边的滚动条就基本同步了。这个函数需要传入两个参数:编辑器对象和预览框对象。
  • 调用的时候,按逻辑是在 initEditor() 中调用,但是这个函数里面没有预览框对象,为了不重复通过 jQuery 获取预览框,我们可以在 parseMarkdown() 函数中 return viewer,然后传给 fixScrollBar()。

7.最终main.js代码

// 初始化编辑器
initEditor();

/**
 * 初始化编辑框
 *
 */
function initEditor() {
    
    
    // 初始化编辑器
    var editor = ace.edit('md-editor');

    editor.setTheme('ace/theme/monokai'); // 设置主题样式
    editor.getSession().setMode('ace/mode/markdown'); // 设置编辑器模式
    editor.getSession().setTabSize(4); // 设置 Tab 为4个空格
    editor.getSession().setUseWrapMode(true); // 自动换行

    // 加载本地缓存数据
    editor.setValue(localStorage.localData || '');

    // 解析从本地加载的缓存数据
    // 并获取其返回的 viewer
    var viewer = parseMarkdown(editor);
    // 控制滚动条
    fixScrollBar(editor, viewer);

    // 即时解析 markdown
    editor.getSession().on('change', function (e) {
    
    
        parseMarkdown(editor);
    });
}

/**
 * 解析markdown
 *
 * @params {object} editor 编辑器
 * @return {object} 预览框
 */
function parseMarkdown(editor) {
    
    
    var viewer = $('#md-viewer'); // 文档预览框
    var data = editor.getValue(); // 获取编辑器数据

    // 保存数据到本地
    localStorage.localData = data;
    // 解析 markdown
    data = marked(data);
    viewer.html(data);

    // 高亮 markdown 文档中的代码
    $('pre > code', viewer).each(function () {
    
    
        hljs.highlightBlock(this);
    });

    // 返回 viewer
    return viewer;
}

/*
 * 控制滚动条
 * 使编辑器和预览框同时滚动
 *
 * @params {object} editor 编辑器
 * @params {object} viewer 预览框
 */
function fixScrollBar(editor, viewer) {
    
    
    var session = editor.getSession();

    // 默认滚动到第一行
    session.setScrollTop(0);

    // 编辑器绑定滚动事件
    session.on('changeScrollTop', function () {
    
    
        var sTop = session.getScrollTop();
        // 设置预览框的滚动条
        viewer.scrollTop(sTop);
    });

    // 预览框定滚动事件
    viewer.on('scroll', function () {
    
    
        var sTop = viewer.scrollTop();
        // 设置编辑器的滚动条
        session.setScrollTop(sTop);
    });
}


总结

一个基础的纯前端 markdown 实时编辑器就完成了。大家可以自己扩展更丰富的功能,比如在编辑器上方添加一个工具条,列一些 markdown 的快捷键等等。

初次投稿,主要分享下自己的学习过程,有什么意见可以提哈,感谢!

猜你喜欢

转载自blog.csdn.net/weixin_44009656/article/details/124508214