不少時候 <textarea>
並不能知足咱們對文本輸入的需求,當咱們須要爲輸入的文本添加格式時,咱們須要使用像 quill 這樣的富文本編輯器來完成富文本的輸入。vue
本文將會詳細的講解如何使用 quill 定製一個本身的富文本編輯器。node
這裏面定製了兩個特殊的功能(添加卡片、添加圖片牆),感興趣的同窗能夠先看一下實現後的效果
線上效果
github 倉庫git
接下來將會講解如何在 quill 的基礎上實現定製化的富文本編輯器。github
一個好的富文本編輯器的標誌就是它可以支持多少種格式,而 quill 支持無限種類的格式。你能夠在 quill 上定製任意種類的文本。而且,若是你不想自定義個任何功能,那麼 quill 也是極易上手的,以下的一段代碼就能夠建立一個簡單的富文本編輯器了。api
var quill = new Quill('#editor', { modules: { toolbar: true }, theme: 'snow' })
quill 自帶了一套數據系統方便咱們自由的擴展想要的功能,Parchment 和 Deltaapp
Parchment 是抽象的文檔模型,是一種與 Dom 樹很相似的結構,Parchment 用於存儲咱們的文檔結構,另外 Parchment 由 Blot 組成,關於 Blot 能夠理解爲 Parchment 的 Node 節點,Blot 中能夠包含結構、內容、樣式等。less
Delta 是一個 Json 結構的數據,用來記錄編輯器產生的變化。Delta 中的每一項數據表明了一次操做。
同時咱們也能夠經過 Delta 操做編輯器中的內容。iview
new Delta().retain(2).delete(4)
retain(2) 表示跳過編輯器中前 2 個 Blot
delete(4) 表示刪除編輯器中的接下來的 4 個 Blotdom
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 提供了很是細粒度、定義良好的 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 中實現了一個 圖片牆 的功能,幫助你們開闊思路。