沐鳴代理:_Quill 實踐指南

quill 定製富文本編輯器

很多時候 <textarea> 並不能滿足我們對文本輸入的需求,當我們需要為輸入的文本添加格式時,我們需要使用像 quill 這樣的富文本編輯器來完成富文本的輸入。

本文將會詳細的講解如何使用 quill 定製一個自己的富文本編輯器。

這裏面定製了兩個特殊的功能(添加卡片、添加圖片牆),感興趣的同學可以先看一下實現后的效果 
線上效果
github 倉庫

接下來將會講解如何在 quill 的基礎上實現定製化的富文本編輯器。

quill 簡介

一個好的富文本編輯器的標誌就是它能夠支持多少種格式,而 quill 支持無限種類的格式。你可以在 quill 上定製任意種類的文本。並且,如果你不想自定義個任何功能,那麼 quill 也是極易上手的,如下的一段代碼就可以創建一個簡單的富文本編輯器了。

var quill = new Quill('#editor', {
  modules: { toolbar: true },
  theme: 'snow'
})

quill 自帶了一套數據系統方便我們自由的擴展想要的功能,Parchment 和 Delta

Parchment

Parchment 是抽象的文檔模型,是一種與 Dom 樹很類似的結構,Parchment 用於存儲我們的文檔結構,另外 Parchment 由 Blot 組成,關於 Blot 可以理解為 Parchment 的 Node 節點,Blot 中可以包含結構、內容、樣式等。

Delta

Delta 是一個 json 結構的數據,用來記錄編輯器產生的變化。Delta 中的每一項數據代表了一次操作。
同時我們也可以通過 Delta 操作編輯器中的內容。

new Delta().retain(2).delete(4)

retain(2) 表示跳過編輯器中前 2 個 Blot
delete(4) 表示刪除編輯器中的接下來的 4 個 Blot

Blot

Blot 是 Parchment 的組成部分,是一種類似於 Dom Node 的結構,Blot 有其自己的接口規範

在 Parchment 中主要有3種 Blot

Block Blot

塊級元素的基本實現(可以在其內部插入其他的 Blot)

Inline Blot

內聯元素的基本實現(可以在其內部插入其他 內聯 的 Blot)

Embed Blot

 非文本恭弘=叶 恭弘子節點的基本實現(這種 Blot 內部不允許再插入其他的 Blot,通常用來實現 Dom 中 <img> <video> 這類標籤對應的 Dom 結構)

Blot 一般通過調用 Parchment.create() 創建,我們可以覆蓋 create 方法,並在覆蓋的方法中通過 super 調用被我覆蓋的方法,來保留 Blot 的默認行為。

一般情況下我們還需要實現 value() 方法,value() 方法返回 create() 方法中的參數值

class MyBlot extends Parchment.Block {
    static create(value){
        const node = super.create();
        // 接下來自定義其他功能
        node.setAttribute('attribute',value)
    }
    value(node){
        return node.getAttribute('attribute')
    }
}

在我們自定義了一個 Blot 后,這時我們還不能在 quill 中使用它,還需要進行註冊。

MyBlot.blotName = 'MyBlot'
MyBlot.tagName= 'div'
MyBlot.className= "my-blot"
Quill.register(MyBlot)

關於 Quill 的前置知識介紹這麼多,下面會通過一個 Demo 來加深理解.

如何在 Quill 上定製功能

quill 提供了非常細粒度、定義良好的 API,我們可以在此基礎之上定製化開發自己的富文本編輯器。(環境為 vue + iview ,使用 iview 進行輔助樣式開發)

首先我們從最簡單的 demo 入手

<template>
  <div id="editor" class="editor"></div>
</template>
<script>
import Quill from "quill";
export default {
  mounted() {
    const quill = new Quill("#editor", {
      theme: "snow",
      placeholder: "請在此開始編輯..."
    });
  }
};
</script>

這是一個默認參數條件下的一個富文本編輯器,我們首先對 Toolbar 進行替換,Toolbar 是 modules 的一部分。

我們需要在新建 quill 實例的時候覆蓋原來的 toolbar,並使用的自己的 toolbar 元素。

<template>
  <div class="container">
    <div id="toolbar">
      <Tooltip content="加粗" placement="bottom">
        <button class="ql-bold"></button>
      </Tooltip>
      <Tooltip content="傾斜" placement="bottom">
        <button class="ql-italic"></button>
      </Tooltip>
      <Tooltip content="下劃線" placement="bottom">
        <button class="ql-underline"></button>
      </Tooltip>
      <Tooltip content="刪除線" placement="bottom">
        <button class="ql-strike"></button>
      </Tooltip>
    </div>
    <div id="editor" class="editor"></div>
  </div>
</template>
<script>
import Quill from "quill";
export default {
  mounted() {
    const quill = new Quill("#editor", {
      theme: "snow",
      modules: {
        toolbar: "#toolbar"
      },
      placeholder: "請在此開始編輯..."
    });
  }
};
</script>
<style lang="less" scoped></style>

會將我們的 toolbar 的樣式變成如下的樣子。

quill 在初始化時會讀取 #toolbar 元素,並獲取其子節點的 classNames,對於 ql-bold ,quill 會截取 ql-之後的部分,並且和已經註冊的 handlers 做匹配,上面的 bold italic underline strike 存在註冊好的 handler。當我們點擊 toolbar 中的元素時便會調用 handler 對富文本內容進行格式化。

當我們需要添加一個本不存在的格式化效果時我們還需要在 modules 中補充它的 handler,下面添加一個可以添加卡片的按鈕,並添加其對應的 handler。

<template>
  <div class="”container“">
    <div id="toolbar">
      <Tooltip content="添加卡片" placement="bottom">
        <button class="ql-card">
          <Icon type="md-card" />
        </button>
      </Tooltip>
    </div>
    <div id="editor" class="editor"></div>
  </div>
</template>
<script>
import Quill from "quill";
export default {
  mounted() {
    const quill = new Quill("#editor", {
      theme: "snow",
      modules: {
        toolbar: {
          container: "#toolbar",
          handlers: {
            card: () => { // 屬性名稱要與 ql-xxx 對應
              console.log(`點擊了 card`);
            }
          }
        }
      },
      placeholder: "請在此開始編輯..."
    });
  }
};
</script>
<style lang="less" scoped></style>

接下來我們為這個按鈕添加實際的效果。

在 Quill 中,使用 Blots 表示節點,我們可以認為 Blots 對應 Dom 中的 Node。當我們需要在 quill 中添加一自定義的 Blots 節點時,我們需要讓其繼承自 Blots 節點。

const BlockEmbed = Quill.import('blots/block/embed')
function customCard(node) {
  // 在一個節點中插入自定義的 dom
  const leftDiv = document.createElement('div')
  leftDiv.className = 'media-container'
  const mediaDiv = document.createElement('div')
  mediaDiv.style['background-image'] = `url(${value.image})`
  mediaDiv.className = 'media'
  leftDiv.appendChild(mediaDiv)

  const rightDiv = document.createElement('div')
  rightDiv.className = 'content-container'

  const titleP = document.createElement('p')
  titleP.className = 'title'
  titleP.innerText = value.title

  const authorP = document.createElement('p')
  authorP.className = 'author'
  authorP.innerText = value.author

  const contentP = document.createElement('p')
  contentP.className = 'content'
  contentP.innerText = value.content

  rightDiv.appendChild(titleP)
  rightDiv.appendChild(authorP)
  rightDiv.appendChild(contentP)
  node.appendChild(leftDiv)
  node.appendChild(rightDiv)
}
// 自定義卡片節點
class CardBlot extends BlockEmbed {
  static create(value) {
    const node = super.create()
    // 新建節點時傳入的數據
    node.dataset.title = value.title
    node.dataset.image = value.image
    node.dataset.content = value.content
    node.dataset.author = value.author

    customizeCard(node)

    node.setAttribute('contenteditable', false) // 設置該節點不可編輯

    return node
  }
  static value(node) {
    // 這裏需要返回 create 函數中傳入的數據
    return node.dataset
  }
}
CardBlot.blotName = 'card' // quill 中的標記名稱
CardBlot.tagName = 'div' // dom 節點名稱
CardBlot.className = 'card' // dom 中真實的 class 名稱
Quill.register(CardBlot)

同時我們還需要添加對應的 handler

const CARD_INFO = {
  title: 'Quill 編輯器',
  author: 'jhchen',
  content:
    'Quill是一種現代的富文本編輯器,旨在實現兼容性和可擴展性。它由Jason Chen和Byron Milligan創建,並由Slab积極維護。 ',
  image:
    'http://neau-lib-static.test.upcdn.net/quill/resource/1576812308405-0.0544z7sqq9au-quill.png'
}

new Quill('#editor', {
  theme: 'snow',
  modules: {
    toolbar: {
      container: '#toolbar',
      handlers: {
        card: () => {
          const addRange = this.quill.getSelection() || 0 // 獲取當前光標選中的位置
          this.quill.insertEmbed(
            addRange,
            'card',
            CARD_INFO,
            Quill.sources.USER
          ) // 插入 card blots
          this.$nextTick(() => {
            this.quill.setSelection(addRange + 1, Quill.sources.SILENT)
          }) // 插入完成后將光標向後移動一位
        }
      }
    }
  },
  placeholder: '請在此開始編輯...'
})

至此我們就可以通過點擊 card 圖標來添加一個 card 了,但是這個 card 還沒有添加樣式,我們手動在显示 card 頁面上添加樣式就大功告成了。

通過這種方式,我們可以便可以在 quill 添加任意類型的內容。

最後

文章中的代碼片段有時候看起來並不清晰,如果感興趣,可以去看一看這個小 demo: 線上效果,github 倉庫。
對 quill 的使用有個整體印象,甚至可以方便的 copy 代碼,在 demo 中實現了一個 圖片牆 的功能,幫助大家開闊思路。

原文:https://segmentfault.com/a/1190000021549175

站長推薦

1.雲服務推薦: 國內主流雲服務商,各類雲產品的最新活動,優惠券領取。地址:阿里雲騰訊雲華為雲

2.廣告聯盟: 整理了目前主流的廣告聯盟平台,如果你有流量,可以作為參考選擇適合你的平台點擊進入

鏈接: http://www.fly63.com/article/detial/7228