Quill編輯器插入自定義HTML記錄的示例詳解

轉(zhuǎn)眼已經(jīng)2020年,饑渴的人類不再滿足于簡單的文本,于是有了花里胡哨的攜帶各種樣式的文本,然而有文本還不夠,我們還需要讓用戶在編輯的時(shí)候,能夠插入各種自定義消息類型,讓我們發(fā)出去的軟文更加好看,因此有了這篇文章。
前言
由于Quill編輯器自帶的富文本過濾(大部分主流編輯器都會(huì)對(duì)富文本進(jìn)行過濾處理),導(dǎo)致開發(fā)者想要配置自定義HTML模板時(shí),遇到了不少麻煩。
一、Quill渲染邏輯分析
為了自定義Quill中的HTML塊內(nèi)容,首先需要了解Quill內(nèi)部的渲染流程,這里有幾個(gè)關(guān)鍵的概念需要了解:
1、Delta
Delta是Quill內(nèi)部定義的一個(gè)數(shù)據(jù)格式,用于表示文檔內(nèi)容以及文檔修改操作,易讀且格式簡單,通過Delta的形式來維護(hù)文檔內(nèi)容,HTML內(nèi)容和Delta兩者可以相互轉(zhuǎn)化。
舉個(gè)例子:
這樣一段富文本會(huì)被表示成以下的格式:
{ "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"} ] }"
普通的文本會(huì)被定義成一個(gè)個(gè)的insert動(dòng)作,每一項(xiàng)代表這一個(gè)delta,都是對(duì)文本內(nèi)容的描述。
類似的,如果修改和刪除也會(huì)生成對(duì)應(yīng)的delta,之后會(huì)將新生成的change delta,與原有的delta進(jìn)行合并操作,生成新的delta。(delta中一共包含三種操作:insert、delete、retain)
保留前10個(gè)字符,對(duì)后續(xù)的20個(gè)字符進(jìn)行加粗操作的delta如下:
{ "ops": [ { "retain": }, { "retain": , "attributes": { "bold": } } ] }
保留前10個(gè)字符,對(duì)后續(xù)的20個(gè)字符進(jìn)行刪除操作如下:
{ "ops": [ { "retain": }, { "delete": } ] }
2、Parchment
Parchment是抽象的文檔模型,對(duì)Blot進(jìn)行管理。
將Parchment理解成完整的DOM樹結(jié)構(gòu)的話,那么Blot就是其中一個(gè)個(gè)單一的節(jié)點(diǎn)。而Blot去了Quill中默認(rèn)的以外,還允許我們進(jìn)行自定義,給了更大的擴(kuò)展空間。
3、Blot
Blot是Parchment文檔的組成部分,相當(dāng)于對(duì)DOM節(jié)點(diǎn)類型的抽象,而一個(gè)具體的Blot實(shí)例里仍有其他的節(jié)點(diǎn)信息。
全局的根節(jié)點(diǎn)Blot是由Quill內(nèi)部自定義的Scroll類型Blot,管理其下面的所有Blot。
對(duì)于Blot的實(shí)現(xiàn)定義可以參照這里: https://github.com/quilljs/parchment#blots
Quill中默認(rèn)定義的Blot如下:
這其中常見的包括TextBlot(行內(nèi)普通文本)、Inline(行內(nèi)攜帶樣式的普通文本)、Block(塊級(jí)行,一般以段落p為單位)、Break(換行)、Image(圖片IMG插入)、Bold(加粗文本)。
而一段HTML如何構(gòu)建出Blot?Quill中會(huì)根據(jù)節(jié)點(diǎn)類型優(yōu)先排除文本節(jié)點(diǎn),如果是元素節(jié)點(diǎn)會(huì)根據(jù)節(jié)點(diǎn)的ClassName進(jìn)行再次判斷,如果仍然無法找到匹配的BlotName,則默認(rèn)匹配以下的映射關(guān)系,來找到對(duì)應(yīng)的BlotClass。
4、Delta的實(shí)際意義
既然已經(jīng)有Blot可以來表示我們的內(nèi)容結(jié)構(gòu)了,為什么還需要Delta?Delta本身只是一份內(nèi)容數(shù)據(jù)的維護(hù),也就是說HTML的更新,無論是用戶輸入,還是API操作,都會(huì)同步更新到Delta中,而Delta如果不作為HTML的數(shù)據(jù)源的話,那么維護(hù)一份Delta數(shù)據(jù)的意義又在哪里?
如果HTML => Delta,而不存在Delta=>HTML,那么不停地去維護(hù)一份delta的意義是什么?
1、由Delta生成HTML其實(shí)是存在的,只不過應(yīng)用場景只限于初始化文檔的時(shí)候,Quill會(huì)對(duì)傳入的初始化HTML字符串進(jìn)行解析處理,生成對(duì)應(yīng)的Delta,其次通過applyDelta的方式,生成DOM節(jié)點(diǎn)回顯與頁面中。
2、看到這里你可能還不滿意,為啥非要走這一步流程,初始化的時(shí)候直接一段字符串document.getElementById('container').innerHTML = val不行嗎,是的,可以,但是Delta的存在讓用戶的文檔變得粒度更細(xì)小,變得易維護(hù),變得可追溯。假如A和B同時(shí)編輯著一份文檔,A刪除了第二行的10個(gè)字符,不需要將文檔內(nèi)容全量更新,只需要提交action操作,同步自己的行為,而B這邊也只需要進(jìn)行沖突處理后merge即可。雖然Delta的維護(hù)讓邏輯變得復(fù)雜了不少,但它的存在也讓文檔有了更多擴(kuò)展的可能。
5、編輯器渲染與更新流程
對(duì)于內(nèi)容的修改一共有以下3種方式:
1、初始化編輯器內(nèi)容:初始化調(diào)用quill.pasteHTML,經(jīng)過HTML過濾和解析回顯到編輯框中。
2、Input Event:用戶輸入和編輯操作,通過MutationObserver監(jiān)聽處理,更新delta。
3、API調(diào)用:調(diào)用內(nèi)部提供API,通過modify方法,而后調(diào)用全局Scroll實(shí)例的方法去修改。
二、插入自定義HTML塊
由于文章內(nèi)容越來越多樣化,在文章插入地圖、音樂播放器、廣告面板等需求的存在,讓我們需要對(duì)富文本編輯器擴(kuò)展出更多的功能。但是同時(shí)也要做好xss防護(hù)攻擊。
按照第一部分的講述,我們需要插入一個(gè)自定義HTML塊,同時(shí)又要Quill能夠識(shí)別,聰明的你一定想到了,我們需要自定義一個(gè)Blot。通過定義好Blot的方式,讓Quill在初始化的時(shí)候能夠識(shí)別我們的HTML塊展示,同時(shí)也讓我們?cè)诓迦際TML塊的時(shí)候不會(huì)被Quill進(jìn)行臟HTML過濾。
注冊(cè)Blot方法如下:
export default function (Quill) { // 引入源碼中的BlockEmbed const BlockEmbed = Quill.import('blots/block/embed'); // 定義新的blot類型 class AppPanelEmbed extends BlockEmbed { static create(value) { const node = super.create(value); node.setAttribute('contenteditable', 'false'); node.setAttribute('width', '100%'); // 設(shè)置自定義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('') } // 返回節(jié)點(diǎn)自身的value值 用于撤銷操作 static value(node) { return node.innerHTML } } // blotName AppPanelEmbed.blotName = 'AppPanelEmbed'; // class名將用于匹配blot名稱 AppPanelEmbed.className = 'embed-innerApp'; // 標(biāo)簽類型自定義 AppPanelEmbed.tagName = 'div'; Quill.register(AppPanelEmbed, true); }
接下來你只需要這樣調(diào)用,便可以在編輯器中插入自定義的HTML塊:
quill.insertEmbed(quill.getSelection().index || 0, 'AppPanelEmbed', ` <div class="app_card_header"> 自定義面板標(biāo)題 </div> <div class="app_card_content"> 自定義面板內(nèi)容 </div> <div class="app_card_footer"> footer </div> `);
傳參格式要求如下:
insertEmbed(index: Number, type: String, value: any, source: String \= 'api'): Delta
這里僅僅這是個(gè)簡單的示例,如果想豐富自定義Blot的功能,可以參照: https://github.com/quilljs/parchment#blots
由于contenteditable屬性放開,為了防止造成xss攻擊,所以需要我們對(duì)該屬性做特殊的過濾處理,這里以xss模塊處理為例:
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) => { // 針對(duì)div的contenteditable 處理 if (isWhiteAttr && tag === 'div' && name === 'contenteditable') { return 'contenteditable="false"'; } }, } // 自定義規(guī)則 const myxss = new xss.FilterXSS(options) return myxss.process(content) }
到此這篇關(guān)于Quill編輯器插入自定義HTML記錄的文章就介紹到這了,更多相關(guān)Quill編輯器自定義HTML內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持腳本之家!
相關(guān)文章
- HTML表格用于在網(wǎng)頁上展示數(shù)據(jù),通過標(biāo)簽及其相關(guān)標(biāo)簽來創(chuàng)建,表格由行和列組成,每一行包含一個(gè)或多個(gè)單元格,單元格可以包含文本、圖像、鏈接等元素,本文將詳細(xì)介紹HTML表格2025-03-12
- 本文介紹了三種禁止HTML頁面滾動(dòng)的方法:通過CSS的overflow屬性、使用JavaScript的滾動(dòng)事件監(jiān)聽器以及使用CSS的position:fixed屬性,每種方法都有其適用場景和優(yōu)缺點(diǎn),感興2025-02-24
使用HTML和CSS實(shí)現(xiàn)文字鏤空效果的代碼示例
在 Web 開發(fā)中,文本的視覺效果是提升用戶體驗(yàn)的重要因素之一,通過 CSS 技巧,我們可以創(chuàng)造出許多獨(dú)特的效果,例如文字鏤空效果,本文將帶你一步一步實(shí)現(xiàn)一個(gè)簡單的文字鏤空2024-11-17Html去除a標(biāo)簽的默認(rèn)樣式的操作代碼
在Html中,a標(biāo)簽?zāi)J(rèn)的超鏈接樣式是藍(lán)色字體配下劃線,這可能不滿足所有設(shè)計(jì)需求,如需去除這些默認(rèn)樣式,可以通過CSS來實(shí)現(xiàn),本文給大家介紹Html去除a標(biāo)簽的默認(rèn)樣式的操作代碼2024-09-25HTML文本域如何設(shè)置為禁止用戶手動(dòng)拖動(dòng)
在HTML中,可以通過設(shè)置CSS的resize屬性為none,來禁止用戶手動(dòng)拖動(dòng)文本域(textarea)的大小,這種方法簡單有效,適用于大多數(shù)現(xiàn)代瀏覽器,但需要在老舊瀏覽器中進(jìn)行測試以確保2024-09-25如何通過HTML/CSS 實(shí)現(xiàn)各類進(jìn)度條的功能
本文詳細(xì)介紹了如何利用HTML和CSS實(shí)現(xiàn)多種風(fēng)格的進(jìn)度條,包括基礎(chǔ)的水平進(jìn)度條、環(huán)形進(jìn)度條以及球形進(jìn)度條等,還探討了如何通過動(dòng)畫增強(qiáng)視覺效果,內(nèi)容涵蓋了使用HTML原生標(biāo)簽2024-09-19HTML中Canvas關(guān)鍵知識(shí)點(diǎn)總結(jié)
Canvas 提供了一套強(qiáng)大的 2D 繪圖 API,適用于各種圖形繪制、圖像處理和動(dòng)畫制作,可以幫助你創(chuàng)建復(fù)雜且高效的網(wǎng)頁圖形應(yīng)用,這篇文章主要介紹了HTML中Canvas關(guān)鍵知識(shí)點(diǎn)總結(jié)2024-06-03html table+css實(shí)現(xiàn)可編輯表格的示例代碼
本文主要介紹了html table+css實(shí)現(xiàn)可編輯表格的示例代碼,主要使用HTML5的contenteditable屬性,文中通過示例代碼介紹的非常詳細(xì),需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)2024-03-06HTML中使用Flex布局實(shí)現(xiàn)雙行夾批效果
本文主要介紹了HTML中使用Flex布局實(shí)現(xiàn)雙行夾批效果,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)2024-02-22HTML+CSS實(shí)現(xiàn)炫酷登錄切換的項(xiàng)目實(shí)踐
在網(wǎng)站開發(fā)中,登錄頁面是必不可少的一部分,本文就來介紹一下HTML+CSS實(shí)現(xiàn)登錄切換,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需2024-02-02