Quill 實踐指南

quill 定製富文本編輯器

不少時候 <textarea> 並不能知足咱們對文本輸入的需求,當咱們須要爲輸入的文本添加格式時,咱們須要使用像 quill 這樣的富文本編輯器來完成富文本的輸入。vue

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

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

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

quill 簡介

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

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

quill 自帶了一套數據系統方便咱們自由的擴展想要的功能,ParchmentDeltaapp

Parchment

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

Delta

Delta 是一個 Json 結構的數據,用來記錄編輯器產生的變化。Delta 中的每一項數據表明了一次操做。
同時咱們也能夠經過 Delta 操做編輯器中的內容。iview

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

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

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 的一部分。

avatar

咱們須要在新建 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 的樣式變成以下的樣子。

avatar

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 中實現了一個 圖片牆 的功能,幫助你們開闊思路。

相關文章
相關標籤/搜索