您当前的位置:首页 > 网站建设 > javascript
| php | asp | css | H5 | javascript | Mysql | Dreamweaver | Delphi | 网站维护 | 帝国cms | React | 考试系统 | ajax | jQuery | 小程序 |

分享一个基于Ace的Markdown编辑器

51自学网 2022-02-21 13:38:50
  javascript

我认为的编辑器分成两类,一种是分为左右两边实现即时渲染;一种是先写语法,然后通过按钮实现渲染。

其实即时渲染也不难,共同需要考虑的问题就是xss,因为渲染库能自定义第三方的xss过滤(之前是通过设置来实现,也就是本身自带,不过在某个版本后被取消了),所以xss就用官方推荐的dompurify。即时渲染可以通过编辑器本身api实现文本变动监听来实现,还有一个需要考虑的问题就是代码与渲染区域的对应。但因为这与我的需求相悖,在这里就不介绍了,相信小老板们都能轻松实现

统一惯例,我们来看看效果图

图一
图二

上面的工具栏其实就是添加事件然后往光标插入对应的语句而已,emoji暂时没有实现,貌似需要第三方库支持。

整体来说并没有难点,只不过对于这些东西来说,要么是文档分散讲得不清楚,要么就是找不到什么文档。要是真没有文档的话,或者官方简陋的文档,你可能真的想问候一下他,哈哈哈。这个时候一个能用的代码就显得尤为重要,尽管它可能没什么注释,但相信聪明的你肯定能理解其中的意思。话不多说,上代码吧~

<template>  <div>    <div class="section-ace">      <el-row>        <el-col :span="6">          <el-row>            <el-col :span="12">              <a class="editor-tab-content" :class="isEditActive"  @click="showEdit">                <i class="fa fa-pencil-square-o" aria-hidden="true"></i>                编辑              </a>            </el-col>            <el-col :span="12">              <a class="preview-tab-content" :class="isPreviewActive" @click="showPreview">                <i class="fa fa-eye" aria-hidden="true"></i>                预览              </a>            </el-col>          </el-row>        </el-col>        <el-col :push="8" :span="18">          <el-row>            <div class="toolbar">              <el-col :span="1">                <div>                  <i @click="insertBoldCode" class="fa fa-bold" aria-hidden="true"></i>                </div>              </el-col>              <el-col :span="1">                <div>                  <i @click="insertItalicCode" class="fa fa-italic" aria-hidden="true"></i>                </div>              </el-col>              <el-col :span="1">                <div>                  <i @click="insertMinusCode" class="fa fa-minus" aria-hidden="true"></i>                </div>              </el-col>              <el-col :span="1">                <el-popover placement="bottom"                            width="125"                            transition="fade-in-linear"                            trigger="click"                            content="">                  <i slot="reference" class="fa fa-header" aria-hidden="true"></i>                  <div>                    <div class="header1-btn" :class="isHeader1Active" @click="insertHeader1Code">                      标题 1 (Ctrl+Alt+1)                    </div>                    <div class="header2-btn" :class="isHeader2Active" @click="insertHeader2Code">                      标题 2 (Ctrl+Alt+2)                    </div>                    <div class="header3-btn" :class="isHeader3Active" @click="insertHeader3Code">                      标题 3 (Ctrl+Alt+3)                    </div>                  </div>                </el-popover>              </el-col>              <el-col :span="1">                <el-popover placement="bottom"                            width="125"                            transition="fade-in-linear"                            trigger="click"                            content="">                  <i slot="reference" class="fa fa-code" aria-hidden="true"></i>                  <div>                    <div class="text-btn" :class="isTextActive" @click="insertText">                      文本 (Ctrl+Alt+P)                    </div>                    <div class="code-btn" :class="isCodeActive" @click="insertCode">                      代码 (Ctrl+Alt+C)                    </div>                  </div>                </el-popover>              </el-col>              <el-col :span="1">                <div>                  <i @click="insertQuoteCode" class="fa fa-quote-left" aria-hidden="true"></i>                </div>              </el-col>              <el-col :span="1">                <div>                  <i @click="insertUlCode" class="fa fa-list-ul" aria-hidden="true"></i>                </div>              </el-col>              <el-col :span="1">                <div>                  <i @click="insertOlCode" class="fa fa-list-ol" aria-hidden="true"></i>                </div>              </el-col>              <el-col :span="1">                <div>                  <i @click="insertLinkCode" class="fa fa-link" aria-hidden="true"></i>                </div>              </el-col>              <el-col :span="1">                <div>                  <i @click="insertImgCode" class="fa fa-picture-o" aria-hidden="true"></i>                </div>              </el-col>              <el-col :span="1">                <div>                  <el-upload                    class="upload-demo"                    action="https://jsonplaceholder.typicode.com/posts/"                    :limit="1">                    <i class="fa fa-cloud-upload" aria-hidden="true"></i>                  </el-upload>                </div>              </el-col>              <el-col :span="1">                <div>                  <i @click="selectEmoji" class="fa fa-smile-o" aria-hidden="true"></i>                </div>              </el-col>              <el-col :span="1">                <div>                  <i @click="toggleMaximize" class="fa fa-arrows-alt" aria-hidden="true"></i>                </div>              </el-col>              <el-col :span="1">                <i @click="toggleHelp" class="fa fa-question-circle" aria-hidden="true"></i>                <el-dialog :visible.sync="dialogHelpVisible"                           :show-close="false"                           top="5vh"                           width="60%"                           :append-to-body="true"                           :close-on-press-escape="true">                  <el-card class="box-card" style="margin: -60px -20px -30px -20px">                    <div slot="header" class="helpHeader">                      <i class="fa fa-question-circle" aria-hidden="true"><span>Markdown Guide</span></i>                    </div>                    <p>This site is powered by Markdown. For full documentation,                      <a href="http://commonmark.org/help/" rel="external nofollow"  target="_blank">click here</a>                    </p>                    <el-table                      :data="tableData"                      stripe                      border                      :highlight-current-row="true"                      style="width: 100%">                      <el-table-column                        prop="code"                        label="Code"                        width="150">                        <template slot-scope="scope">                          <p v-html='scope.row.code'></p>                        </template>                      </el-table-column>                      <el-table-column                        prop="or"                        label="Or"                        width="180">                        <template slot-scope="scope">                          <p v-html='scope.row.or'></p>                        </template>                      </el-table-column>                      <el-table-column                        prop="devices"                        label="Linux/Windows">                      </el-table-column>                      <el-table-column                        prop="device"                        label="Mac OS"                        width="180">                      </el-table-column>                      <el-table-column                        prop="showOff"                        label="... to Get"                      width="200">                        <template slot-scope="scope">                          <p v-html='scope.row.showOff'></p>                        </template>                      </el-table-column>                    </el-table>                  </el-card>                </el-dialog>              </el-col>            </div>          </el-row>        </el-col>      </el-row>    </div>    <br>    <div id="container">      <div class="show-panel">        <div ref="markdown" class="ace" v-show="!isShowPreview"></div>        <div class="panel-preview" ref="preview" v-show="isShowPreview"></div>      </div>    </div>  </div></template><script>import ace from 'ace-builds'// 在 webpack 环境中使用必须要导入import 'ace-builds/webpack-resolver';import marked  from 'marked'import highlight from "highlight.js";import "highlight.js/styles/foundation.css";import katex from 'katex'import 'katex/dist/katex.css'import DOMPurify from 'dompurify';const renderer = new marked.Renderer();function toHtml(text){  let temp = document.createElement("div");  temp.innerHTML = text;  let output = temp.innerText || temp.textContent;  temp = null;  return output;}function mathsExpression(expr) {  if (expr.match(/^/$/$[/s/S]*/$/$$/)) {    expr = expr.substr(2, expr.length - 4);    return katex.renderToString(expr, { displayMode: true });  } else if (expr.match(/^/$[/s/S]*/$$/)) {    expr = toHtml(expr); // temp solution    expr = expr.substr(1, expr.length - 2);    //Does that mean your text is getting dynamically added to the page? If so, someone must be calling KaTeX to render    // it, and that call needs to have the strict flag set to false as well. 即控制台警告,比如%为转义或者中文    // link: https://katex.org/docs/options.html    return katex.renderToString(expr, { displayMode: false , strict: false});  }}const unchanged = new marked.Renderer()renderer.code = function(code, language, escaped) {  console.log(language);  const isMarkup = ['c++', 'cpp', 'golang', 'java', 'js', 'javascript', 'python'].includes(language);  let hled = '';  if (isMarkup) {    const math = mathsExpression(code);    if (math) {      return math;    } else {      console.log("highlight");      hled = highlight.highlight(language, code).value;    }  } else {    console.log("highlightAuto");    hled = highlight.highlightAuto(code).value;  }  return `<pre class="hljs ${language}"><code class="${language}">${hled}</code></pre>`;  // return unchanged.code(code, language, escaped);};renderer.codespan = function(text) {  const math = mathsExpression(text);  if (math) {    return math;  }  return unchanged.codespan(text);};export default {  name: "abc",  props: {    value: {      type: String,      required: true    }  },  data() {    return {      tableData: [{        code: ':emoji_name:',        or: '
下载地址:
JS原生双栏穿梭选择框的实现示例
vue实现全选功能
万事OK自学网:51自学网_软件自学网_CAD自学网自学excel、自学PS、自学CAD、自学C语言、自学css3实例,是一个通过网络自主学习工作技能的自学平台,网友喜欢的软件自学网站。