This was originally a rich text editor written for VM-Manager, but later I thought it was more convenient to maintain it independently, so I separated it and put it into NPM. Everyone can write because this component is really simple to implement, doesn’t require a lot of Js proficiency, and is basically a binding to the document.execcommand () directive. I will share the process with you here

IO/vM-editor /

Source address github.com/luosijie/vm…

The installation

npm install --save vm-editorCopy the code

use

The //upload binding event passes compiled HTML characters to the parent component < vmeditor@upload ="showHtml"></VmEditor>

<script>
import VmEditor from 'vm-editor'
export default {
  ...
  methods: {
    showHtml: function(data){
      console.log(data)
    }
  }
}
</script>Copy the code

To prepare

Since it is a Vue component, it is necessary to know how to write such a component:

  1. Vue single file component development
  2. ExecCommand instruction

Component structure

Component structure drawing

The component is mainly composed of a menu part and content area 2, in which the menu area is composed of various instruction buttons, and some instruction buttons have drop-down options

Command button

The command button is a loader for execConmand and needs to do the following

  1. Gray across the background
  2. Display button icon
  3. Part of the buttons need to achieve click to expand the drop-down menu
<template>
  <button class="vm-editor-button" :class="{ active: slot }"> // Display button icon <img: SRC ="require('.. /assets/iconimg/' + icon + '.png')" height="16" width="16" alt="" @click="showSlot"> <! -- <i :class="icon" @click="showSlot"></ I > --> // Some buttons need to be clicked to expand the drop-down menu <slot V-if ="slot"></slot> </button> </template> <style> ... Button. Vm-editor -button:hover{background-color:#eee;}... </style>export default {
  name: 'VmEditorButton',
  props: {
    icon: {
      type: String,
      default: 'heading'
    }
  },
  data: function () {
    return {
      slot: false
    }
  },
  methods: {
    showSlot () {
      this.slot === false ? this.slot = true : this.slot = false
    }
  }
}
</script>Copy the code

The menu area

The menu area


  1. Bind the execCommand directive to the button
  2. Click upload button to compile HTML
<template>
  <div class="vm-editor-menu"> // Introduces the button assembly, wrapped by the **click event ** bindingexecThe Command method implements style changes <VmEditorButton icon="paragraph" @click.native="execCommand('formatBlock', '<p>')">
    </VmEditorButton>
    <VmEditorButton icon="heading"> <VmEditorDropdown> // This is the part of the button that requires the dropdown menu function <ul class="vm-editor-ul">
          <li @click="execCommand('formatBlock', '<h1>')">
            <h1>H1</h1>
          </li>
          <li @click="execCommand('formatBlock', '<h2>')">
            <h2>H2</h2>
          </li>
          <li @click="execCommand('formatBlock', '<h3>')">
            <h3>H3</h3>
          </li>
          <li @click="execCommand('formatBlock', '<h4>')">
            <h4>H4</h4>
          </li>
          <li @click="execCommand('formatBlock', '<h5>')"> <h5> h5 </h5> </li> </ul> </VmEditorButton> <slot></slot> </div> </template> <style> ... </style> <script> ...export default {
  name: 'VmEditorMenu', components: { VmEditorButton, VmEditorDropdown, VmEditorAddlink, VmEditorAddimage, VmEditorFontcolor }, methods: {// Encapsulate the document.execCommand directiveexecCommand: function (commandName, valueArgument) {
      // let body = document.querySelector('.body');
      if(! valueArgument) { valueArgument = null } document.execCommand('styleWithCSS', null, true)
      document.execCommand(commandName, false, valueArgument)}, // Insert image functionsetImage: function (evt) {
      let reader = new FileReader()
      let file = evt.target.files[0]
      reader.readAsDataURL(file)
      reader.onload = function (evt) {
        let base64Image = evt.target.result
        document.execCommand('insertImage'.false, base64Image)
      }
    }
  }
}
</script>Copy the code

The main components

The main components



The menu component
The content area

In addition to the implementation of HTML export function

<div class="vm-editor">
  <VmEditorMenu>
    <div class="global-control"<VmEditorButton icon= <VmEditorButton icon="upload" @click.native="uploadHtml"></VmEditorButton> </div> </VmEditorMenu> // Content area just set **contenteditable="true"<div class= <div class= <div class="content" contenteditable="true" v-html="html">
  </div>
</div>
<style>
 ...
</style>
<script>
  name: 'VmEditor',
  components: {
    VmEditorMenu,
    VmEditorButton
  },
  data: function () {
    return {
      html: 'Please Enter ... '}}, methods: {export HTML data // current content area style is **CSS style **, export to ** inline ** uploadHtml:function() {// Get the CSS style of each modulelet style = {
        ul: `
              margin: 10px 20px;
              list-style-type: square;
              padding: 0;
            `,
        ol: `
              margin: 10px 20px;
              list-style-type: decimal;
              padding: 0;
            `,
        li: `
              display: list-item;
              padding: 0;
            `,
        hr: `
              margin: 15px 0;
              border-top: 1px solid #eeeff1;
            `,
        pre: `
              display: block;
              margin: 10px 0;
              padding: 8px;
              border-radius: 4px;
              background-color: #f2f2f2;
              color: # 656565;
              font-size: 14px;
             `,
        blockquote: `
                      display: block;
                      border-left: 4px solid #ddd;
                      margin: 15px 0;
                      padding: 0 15px;
                    `,
        img: `
               margin: 20px 0;
             `,
        a: `
            color: #41b883;`}let html = document.getElementsByClassName('content') [0]let htmlContainerParent = document.createElement('div')
      let htmlContainer = document.createElement('div')
      letTagNames = object.keys (style) // Iterate over the HTML node and insert the corresponding inline stylefor (let i = 0; i < tagNames.length; i++) {
        let _tagNames = html.getElementsByTagName(tagNames[i])
        if (_tagNames.length > 0) {
          for (letj = 0; j < _tagNames.length; j++) { _tagNames[j].style = style[tagNames[i]] } } } htmlContainer.style = ` text-align: left; padding: 15px; font-size: 16px; ` htmlContainer. InnerHTML = HTML. InnerHTML htmlContainerParent. The appendChild (htmlContainer) / / registered upload * * * * this custom events.$emit('upload', htmlContainerParent.innerHTML)
    }
  }
}
</script>Copy the code

Other components

The other components are mostly push-down menus, and because each is different, they have to stand alone

  1. Vm-editor-addimage: Inserts the image
  2. Vm-editor-addlink: Inserts links
  3. Vm-editor-fontcolor: Modify the color

Deficiency in

Because of the short development time of the rich text editor, there was no serious research into the source code of such good plug-ins and no in-depth research into the needs of the rich text editor. I just referenced the implementation and UI style of some of the same editors, such as Simditor, and implemented the functionality briefly.

So there’s definitely a lot of room for improvement, some of the obvious ones are:

  1. Browser compatibility
  2. Table function implementation

Anyway, just for your learning purposes, right

The original address

That’s it. Welcome star