Detailed explanation of example of quill editor inserting custom HTML record

Time:2020-9-10

In 2020, hungry human beings are no longer satisfied with simple text, so they have various styles of text. However, text is not enough. We also need to let users insert various custom message types when editing, so that the soft text we send out is more beautiful. Therefore, this article is available.

preface

Due to the rich text filtering provided by quill editor (most mainstream editors filter rich text), developers encounter a lot of trouble when they want to configure custom HTML templates.

1、 Logic analysis of quill rendering

In order to customize the content of HTML blocks in quill, first of all, you need to understand the rendering process inside quill. Here are several key concepts to understand:

1、Delta

Delta is a data format defined internally by quill, which is used to represent document content and document modification operations. It is easy to read and has a simple format. Document content is maintained in the form of delta. HTML content and delta can be transformed into each other.

for instance:

Such a rich text is represented in the following format:


{  
"ops":[ 
{"insert":"this is a simple text.\\nbut when "}, 
{"attributes":{"bold":true},"insert":"it is "}, 
{"insert":"not bold.\\nlet me try "}, 
{"attributes":{"italic":true},"insert":"italic "}, 
{"insert":"haha\\nwhat about "}, {"attributes": 
{"italic":true,"bold":true},"insert":"both"}, 
{"insert":" ?\\n"} ]  
}"

Ordinary text is defined as insert actions one by one. Each item represents this delta and is a description of the text content.

Similarly, if you modify or delete, the corresponding Delta will be generated. After that, the newly generated change Delta will be merged with the original delta to generate a new delta. (there are three operations in Delta: insert, delete and retain)

The delta of the first 10 characters and the subsequent 20 characters are bold as follows:


{
  "ops": [
    { "retain":  },
    { "retain": , "attributes": { "bold":  } }
  ]
}

Retain the first 10 characters and delete the next 20 characters as follows:


{
  "ops": [
    { "retain":  },
    { "delete":  }
  ]
}

2、Parchment

Parchment is an abstract document model, which manages the blog.

If parchment is understood as a complete DOM tree structure, then blot is a single node. In addition to the default in quill, blot also allows us to customize, giving us more space for expansion.

3、Blot

As a part of the parsing document, blot is equivalent to the abstraction of DOM node type. However, there are still other node information in a specific blot instance.

The global root node blot is a scroll type blot defined by quill, which manages all the blogs under it.

For the implementation and definition of blot, please refer to the following: https://github.com/quilljs/parchment#blots

The default defined blots in quill are as follows:

The common ones include TextBlock, inline, block, break, image and bold.

How does a piece of HTML build a blog? In quill, text nodes are first excluded according to the node type. If it is an element node, it will be judged again according to the node’s classname. If a matching blotname cannot be found, the following mapping relationship will be matched by default to find the corresponding blotclass.

4. The practical significance of delta

Now that we have a blog to represent our content structure, why do we need Delta? Delta itself is only the maintenance of content data, that is to say, HTML updates, whether user input or API operation, will be synchronously updated to delta. If delta is not used as the data source of HTML, what is the significance of maintaining a delta data?

If HTML = > Delta, but there is no delta = > HTML, what is the meaning of constantly maintaining a Delta?

1. In fact, HTML generated by delta exists. However, the application scenario is limited to the initialization of documents. Quill will parse and process the incoming initialization HTML string to generate the corresponding Delta. Secondly, DOM nodes are generated for display in the page by applying Delta.

2. You may not be satisfied with this. Why do you have to go through this step? When initializing, you should directly segment a string document.getElementById (‘container ‘). InnerHTML = Val can’t, yes, yes, but the existence of delta makes the user’s documents more granular, easy to maintain and traceable. If a and B are editing a document at the same time, a has deleted 10 characters in the second line, and does not need to update the document content in full. It only needs to submit an action operation to synchronize its own behavior, while B only needs to merge after conflict processing. Although the maintenance of delta makes the logic more complicated, the existence of delta also makes it possible for documents to be extended.

5. Editor rendering and update process

There are three ways to modify the content:

1. Initializing editor content: initializing calls quill.pasteHTML After HTML filtering and parsing, it will be displayed back to the edit box.

2. Input event: user input and edit operations, monitor and process through mutationobserver, and update Delta.

3, API calls: call the internal API, through the modify method, and then call the global Scroll instance method to modify.

2、 Insert custom HTML block

As the content of the article is becoming more and more diversified, the need to insert maps, music players, advertising panels and other needs in the article makes us need to expand more functions to the rich text editor. But at the same time, we should also do a good job in XSS defense attacks.

According to the first part, we need to insert a custom HTML block, and quill can recognize it. As you must have thought, we need to customize a blog. By defining the way of blot, quill can recognize our HTML block display when initializing, and at the same time, we will not be filtered by quill when inserting HTML blocks.

How to register a blog is as follows:

export default function (Quill) {
  //Introducing blockembed in source code
  const BlockEmbed = Quill.import('blots/block/embed');
  //Define a new blog type
  class AppPanelEmbed extends BlockEmbed {
    static create(value) {
      const node = super.create(value);
      node.setAttribute('contenteditable', 'false');
      node.setAttribute('width', '100%');
      //Set custom HTML
      node.innerHTML = this.transformValue(value)
      return node;
    }

    static transformValue(value) {
      let handleArr = value.split('\n')
      handleArr = handleArr.map(e => e.replace(/^[\s]+/, '')
        .replace(/[\s]+$/, ''))
      return handleArr.join('')
    }

    //Returns the value of the node itself for undo operation
    static value(node) {
      return node.innerHTML
    }
  }
  // blotName
  AppPanelEmbed.blotName = 'AppPanelEmbed';
  //The class name will be used to match the blot name
  AppPanelEmbed.className = 'embed-innerApp';
  //Label type customization
  AppPanelEmbed.tagName = 'div';
  Quill.register(AppPanelEmbed, true);
}

Next, you can insert a custom HTML block into the editor by calling this:

quill.insertEmbed(quill.getSelection().index || 0, 'AppPanelEmbed', `
          <div class="app_card_header">     
              Custom panel title
          </div>
          <div class="app_card_content">     
              Custom panel content
          </div>
          <div class="app_card_footer">     
              footer
          </div>
      `);

The format requirements of transmission parameters are as follows:


insertEmbed(index: Number, type: String, value: any, source: String \= 'api'): Delta

This is just a simple example. If you want to enrich the functions of custom blog, you can refer to: https://github.com/quilljs/parchment#blots

Since the contentditable attribute is released, in order to prevent XSS attacks, we need to do special filtering on this attribute. Here, we take the XSS module processing as an example:

handleWithXss(content) {
      const options = {
        whiteList: {
         ...
          div: ['class', 'style', 'data-id','contenteditable'],
         ...
        },
        css: {
          whiteList: {
            color: true,
            'background-color': true,
            'max-width': true,
          },
        },
        stripIgnoreTag: true,
        onTagAttr: (tag, name, value, isWhiteAttr) => {
          //Content ditable processing for div
          if (isWhiteAttr && tag === 'div' && name === 'contenteditable') {
            return 'contenteditable="false"';
          }
        },
      }// custom rules
      const myxss = new xss.FilterXSS(options)
      return myxss.process(content)
    }

This article about quill editor to insert custom HTML records will be introduced here. For more related quill editor custom HTML content, please search the previous articles of developeppaer or continue to browse the related articles below. I hope you can support developeppaer more in the future!