前言
富文本編輯器在很多互聯(lián)網(wǎng)領(lǐng)域被廣泛應(yīng)用,特別是各種Web端的程序,比如我們常用的電子郵件、社交平臺、博客編輯平臺等等。目前,有很多針對富文本編輯器的一些基本功能和拓展封裝成一個開源的富文本編輯器,比如最開始百度的Ueditor
,現(xiàn)在比較流行的Quill
、BraftEditor
。對于一些簡單的編輯功能,很多都是直接使用這些開源富文本編輯器的基礎(chǔ)API,如果有其他的額外需求,還會在此基礎(chǔ)上做一些拓展,而且這些開源的富文本編輯器都有開放的拓展API供我們使用。
那么這些富文本編輯器到底是如何實(shí)現(xiàn)的呢?
富文本編輯器介紹
富文本編輯器的基本結(jié)構(gòu)通常是一個容器元素(例如 <div>
), 這個容器元素的 contenteditable
屬性被設(shè)置為 true
,以允許用戶對其內(nèi)部的內(nèi)容進(jìn)行編輯。這個屬性是實(shí)現(xiàn)富文本編輯器的基礎(chǔ)。
富文本編輯器利用 JavaScript
的 DOM
操作來實(shí)現(xiàn)各種功能。編輯器通過獲取編輯區(qū)域的 DOM
引用,并利用 DOM API
來操作文本內(nèi)容和樣式。
編輯器通常會附帶一些工具欄,用于設(shè)置文本樣式、插入圖片等功能。這些功能會要處理各種用戶交互,比如鍵盤輸入、鼠標(biāo)點(diǎn)擊、文本選擇等,需要通過為編輯器元素添加事件監(jiān)聽器,并在相應(yīng)事件發(fā)生時執(zhí)行特定的操作。
富文本編輯器可以將編輯區(qū)域內(nèi)的 HTML 結(jié)構(gòu)轉(zhuǎn)換為其他格式,如純文本、Markdown 等。這是為了適應(yīng)不同的場景和需求,比如將編輯的文本內(nèi)容保存到數(shù)據(jù)庫、發(fā)送電子郵件等。
富文本編輯器最后輸入的內(nèi)容需要存儲到后臺,比如HTML字符串存儲,后續(xù)獲取進(jìn)行編輯的時候也是拿到之前保存的HTML進(jìn)行編輯器內(nèi)部渲染。
光標(biāo)位置和選擇范圍管理:富文本編輯器需要跟蹤光標(biāo)的位置以及用戶的選擇范圍。這使得編輯器能夠準(zhǔn)確地應(yīng)用樣式和執(zhí)行操作,比如插入鏈接、刪除文本等。
總的來說,富文本編輯器其實(shí)就是HTML、CSS 和 JavaScript三者結(jié)合,只要好好使用這三樣API,就能實(shí)現(xiàn)一個富文本編輯器。
要實(shí)現(xiàn)富文本編輯器需要了解哪些API
contenteditable
屬性:該屬性用于使元素可編輯。通過將其設(shè)置為"true",可以使元素具備編輯功能。
innerHTML
屬性:這個屬性用于獲取或設(shè)置元素的 HTML 內(nèi)容。通過讀取或修改該屬性,可以獲取或更改編輯區(qū)域的內(nèi)容。
execCommand()
方法:這個方法用于執(zhí)行命令,比如設(shè)置字體樣式、插入鏈接、插入圖像等。使用該方法可以通過命令名稱來執(zhí)行編輯操作。例如:通過執(zhí)行document.execCommand('bold')
,可以將選定的文本設(shè)置為粗體.
Selection
對象:這個對象提供了與文本選擇相關(guān)的信息和操作??梢允褂?Selection
對象獲取光標(biāo)位置、選擇范圍以及進(jìn)行文本插入、刪除等操作。
Range
對象:Range
對象表示文檔中的一個范圍區(qū)域,可以用于精確地控制選定的文本范圍。它提供了一系列的方法和屬性,可以進(jìn)行文本選取、插入、刪除等操作。
Selection對象
Selection 對象表示用戶當(dāng)前選擇的文本范圍,它提供了獲取和操作選中文本的方法和屬性。
屬性和方法
anchorNode
: 返回當(dāng)前選擇范圍的起始節(jié)點(diǎn)(節(jié)點(diǎn)可以是元素節(jié)點(diǎn)或文本節(jié)點(diǎn))。
anchorOffset
: 返回當(dāng)前選擇范圍的起始偏移量,即在 anchorNode 中的偏移位置。
focusNode
: 返回當(dāng)前選擇范圍的結(jié)束節(jié)點(diǎn)。
focusOffset
: 返回當(dāng)前選擇范圍的結(jié)束偏移量。
isCollapsed
: 判斷當(dāng)前選擇范圍是否是折疊的(即光標(biāo)狀態(tài))。
removeAllRanges()
: 清除所有選擇范圍。
addRange(range)
: 添加一個 Range 對象到當(dāng)前選擇中。
toString()
: 返回選中文本的字符串表示。
Range對象
Range 對象表示文檔中的一個范圍區(qū)域,可以用于精確地控制選定的文本范圍
屬性和方法
startContainer
: 返回當(dāng)前范圍的起始節(jié)點(diǎn)(元素節(jié)點(diǎn)或文本節(jié)點(diǎn))。
startOffset
: 返回當(dāng)前范圍在 startContainer 中的起始偏移量。
endContainer
: 返回當(dāng)前范圍的結(jié)束節(jié)點(diǎn)。
endOffset
: 返回當(dāng)前范圍在 endContainer 中的結(jié)束偏移量。
commonAncestorContainer
: 返回包含當(dāng)前范圍內(nèi)所有節(jié)點(diǎn)的最深的共同祖先節(jié)點(diǎn)。
selectNode(node)
: 選中指定的節(jié)點(diǎn)。
selectNodeContents(node)
: 選中指定節(jié)點(diǎn)的內(nèi)容。
setStart(startNode, startOffset)
: 設(shè)置范圍的起始位置。
setEnd(endNode, endOffset)
: 設(shè)置范圍的結(jié)束位置。
insertNode(node)
: 在當(dāng)前范圍內(nèi)插入一個節(jié)點(diǎn)。
deleteContents()
: 刪除當(dāng)前范圍內(nèi)的內(nèi)容。
Selection和Range在針對文本的相關(guān)操作是很有用的兩個API,這里我主要是總結(jié)幾個重要的一些屬性和方法,具體的一些使用場景后續(xù)有機(jī)會再出一篇相關(guān)文章。
這種應(yīng)用場景目前就有一個很好的例子:劃線分享。簡單說明一下,劃線分享就是確定開始光標(biāo)位置和結(jié)束位置,然后確定選定文本的范圍,最后給選定的文本段設(shè)置一個特殊的樣式區(qū)分顯示,彈出對應(yīng)的功能按鈕分享。這里不就主要用Selection和Range嘛。
實(shí)現(xiàn)一個簡單的富文本編輯器
創(chuàng)建編輯區(qū)域
首先需要一個編輯區(qū)域,創(chuàng)建一個div
元素,元素設(shè)置contenteditable=true
<div id="editor" contenteditable="true" style="border: 1px solid #ccc; min-height: 200px;"></div>
創(chuàng)建編輯功能Bar
其次,創(chuàng)建一個編輯器工具欄,我們就寫一個最簡單的,設(shè)置字體粗細(xì)。
<select id="fontStyle">
<option value="normal">Normal</option>
<option value="bold">Bold</option>
<option value="italic">Italic</option>
</select>
<button onclick="setFontStyle()">Set Style</button>
function setFontStyle() {
const fontStyle = document.getElementById('fontStyle').value;
document.execCommand('fontName', false, fontStyle);
}
在 JavaScript 中,我們可以通過獲取選中元素的值和編輯區(qū)域的引用,使用 document.execCommand()
方法來設(shè)置文本樣式。
內(nèi)容輸出
通過一個按鈕進(jìn)行保存,保存的內(nèi)容主要是通過innerHTML
拿到。
<button onclick="saveContent()">Save Content</button>
function saveContent() {
const editor = document.getElementById('editor');
const content = editor.innerHTML;
console.log(content); // 這里的內(nèi)容可以通過后臺接口保存到數(shù)據(jù)庫中
}
以上其實(shí)就已經(jīng)實(shí)現(xiàn)了一個特別簡單基礎(chǔ)的富文本編輯器,還有一部分比如一些媒體功能包括插入圖片、插入視頻等,可以按照上面一樣的邏輯添加不同的功能。整體的實(shí)現(xiàn)思路大致就是這些。
但是實(shí)現(xiàn)過程中有需要注意的地方⚠️:
contenteditable=true
可編輯元素,默認(rèn)回車會自動生成p
標(biāo)簽,但是不同瀏覽器的表現(xiàn)不一樣,火狐和谷歌瀏覽器默認(rèn)新建p
標(biāo)簽,safari默認(rèn)新建div
標(biāo)簽,且第一個初始生成的p
標(biāo)簽會被砍掉只保留了文字部分。這里需要手動去創(chuàng)建一個特定的標(biāo)簽。
刪除內(nèi)容,需要注意刪除到最后要保留之前初始化的空標(biāo)簽,不然會默認(rèn)把初始化的空標(biāo)簽刪掉,這樣生成的html就不一致了。
最后
這次主要是介紹一下實(shí)現(xiàn)簡單富文本編輯器的一個基礎(chǔ)流程,其實(shí)他的核心就是contenteditable=true
,如果需要實(shí)現(xiàn)一個更好的富文本編輯器,就需要結(jié)合各個API進(jìn)行事件處理和樣式處理。目前也有好的開源富文本編輯器,我們可以參考他們的源碼實(shí)現(xiàn)一個自己的富文本編輯器,這樣不僅能夠讓自己對于一些API理解更深刻,而且關(guān)于一個完整的開源功能也有一個很好的認(rèn)知。
作者:Betty97
鏈接:https://juejin.cn/post/7240751116702122021
來源:稀土掘金
著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請注明出處。
該文章在 2023/12/28 11:00:34 編輯過