溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

怎么使用Web富文本輸入框

發(fā)布時間:2021-11-06 16:15:25 來源:億速云 閱讀:176 作者:iii 欄目:web開發(fā)

這篇文章主要講解了“怎么使用Web富文本輸入框”,文中的講解內(nèi)容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“怎么使用Web富文本輸入框”吧!

輸入框富文本化

傳統(tǒng)的輸入框都是使用 <textarea> 來制作的,它的優(yōu)勢是非常簡單,但***的缺陷卻是無法展示圖片。為了能夠讓輸入框能夠展示圖片(富文本化),我們可以采用設置了 contenteditable="true" 屬性的 <div> 來實現(xiàn)這里面的功能。

簡單創(chuàng)建一個 index.html 文件,然后寫入如下內(nèi)容:

<div class="editor" contenteditable="true">    <img src="https://static.easyicon.net/preview/121/1214124.gif" alt="">  </div>

打開瀏覽器,就能看到一個默認已經(jīng)帶了一張圖片的輸入框:

怎么使用Web富文本輸入框

光標可以在圖片前后移動,同時也可以輸入內(nèi)容,甚至通過退格鍵刪除這張圖片&mdash;&mdash;換句話說,圖片也是可編輯內(nèi)容的一部分,也意味著輸入框的富文本化已經(jīng)體現(xiàn)出來了。

接下來的任務,就是思考如何直接通過 control + v 把圖片粘貼進去了。

處理粘貼事件

任何通過“復制”或者 control + c 所復制的內(nèi)容(包括屏幕截圖)都會儲存在剪貼板,在粘貼的時候可以在輸入框的 onpaste 事件里面監(jiān)聽到。

<div class="editor" contenteditable="true">    <img src="https://static.easyicon.net/preview/121/1214124.gif" alt="">  </div>

而剪貼板的的內(nèi)容則存放在 DataTransferItemList 對象中,可以通過 e.clipboardData.items 訪問到:

怎么使用Web富文本輸入框

細心的讀者會發(fā)現(xiàn),如果直接在控制臺點開 DataTransferItemList 前的小箭頭,會發(fā)現(xiàn)對象的 length 屬性為0。說好的剪貼板內(nèi)容呢?其實這是 Chrome 調(diào)試的一個小坑。在開發(fā)者工具里面,console.log 出來的對象是一個引用,會隨著原始數(shù)據(jù)的改變而改變。由于剪貼板的數(shù)據(jù)已經(jīng)被“粘貼”進輸入框了,所以展開小箭頭以后看到的 DataTransferItemList 就變成空的了。為此,我們可以改用 console.table 來展示實時的結(jié)果。

怎么使用Web富文本輸入框

在明白了剪貼板數(shù)據(jù)的存放位置以后,就可以編寫代碼來處理它們了。由于我們的富文本輸入框比較簡單,所以只需要處理兩類數(shù)據(jù)即可,其一是普通的文本類型數(shù)據(jù),包括 emoji 表情;其二則是圖片類型數(shù)據(jù)。

新建 paste.js 文件:

const onPaste = (e) => {    // 如果剪貼板沒有數(shù)據(jù)則直接返回    if (!(e.clipboardData && e.clipboardData.items)) {      return    }    // 用Promise封裝便于將來使用    return new Promise((resolve, reject) => {      // 復制的內(nèi)容在剪貼板里位置不確定,所以通過遍歷來保證數(shù)據(jù)準確      for (let i = 0, len = e.clipboardData.items.length; i < len; i++) {        const item = e.clipboardData.items[i]        // 文本格式內(nèi)容處理        if (item.kind === 'string') {          item.getAsString((str) => {            resolve(str)          })        // 圖片格式內(nèi)容處理        } else if (item.kind === 'file') {          const pasteFile = item.getAsFile()          // 處理pasteFile          // TODO(pasteFile)        } else {          reject(new Error('Not allow to paste this type!'))        }      }    })  }  export default onPaste

然后就可以在 onPaste 事件里面直接使用了:

document.querySelector('.editor').addEventListener('paste', async (e) => {      const result = await onPaste(e)      console.log(result)  })

上面的代碼支持文本格式,接下來就要對圖片格式進行處理了。玩過 <input type="file"> 的同學會知道,包括圖片在內(nèi)的所有文件格式內(nèi)容都會儲存在 File 對象里面,這在剪貼板里面也是一樣的。于是我們可以編寫一套通用的函數(shù),專門來讀取 File 對象里的圖片內(nèi)容,并把它轉(zhuǎn)化成 base64 字符串。

粘貼圖片

為了更好地在輸入框里展示圖片,必須限制圖片的大小,所以這個圖片處理函數(shù)不僅能夠讀取 File 對象里面的圖片,還能夠?qū)ζ溥M行壓縮。

新建一個 chooseImg.js 文件:

/**   * 預覽函數(shù)   *   * @param {*} dataUrl base64字符串   * @param {*} cb 回調(diào)函數(shù)   */  function toPreviewer (dataUrl, cb) {    cb && cb(dataUrl)  }  /**   * 圖片壓縮函數(shù)   *   * @param {*} img 圖片對象   * @param {*} fileType  圖片類型   * @param {*} maxWidth 圖片***寬度   * @returns base64字符串   */  function compress (img, fileType, maxWidth) {    let canvas = document.createElement('canvas')    let ctx = canvas.getContext('2d')    const proportion = img.width / img.height    const width = maxWidth    const height = maxWidth / proportion    canvas.width = width    canvas.height = height    ctx.fillStyle = '#fff'    ctx.fillRect(0, 0, canvas.width, canvas.height)    ctx.drawImage(img, 0, 0, width, height)    const base64data = canvas.toDataURL(fileType, 0.75)    canvas = ctx = null    return base64data  }  /**   * 選擇圖片函數(shù)   *   * @param {*} e input.onchange事件對象   * @param {*} cb 回調(diào)函數(shù)   * @param {number} [maxsize=200 * 1024] 圖片***體積   */  function chooseImg (e, cb, maxsize = 200 * 1024) {    const file = e.target.files[0]    if (!file || !/\/(?:jpeg|jpg|png)/i.test(file.type)) {      return    }    const reader = new FileReader()    reader.onload = function () {      const result = this.result      let img = new Image()      if (result.length <= maxsize) {        toPreviewer(result, cb)        return      }      img.onload = function () {        const compresscompressedDataUrl = compress(img, file.type, maxsize / 1024)        toPreviewer(compressedDataUrl, cb)        img = null      }       img.src = result    }    reader.readAsDataURL(file)  }  export default chooseImg

關于使用 canvas 壓縮圖片和使用 FileReader 讀取文件的內(nèi)容在這里就不贅述了,感興趣的讀者可以自行查閱。

回到上一步的 paste.js 函數(shù),把其中的 TODO() 改寫成 chooseImg() 即可:

const imgEvent = {    target: {      files: [pasteFile]    }  }  chooseImg(imgEvent, (url) => {    resolve(url)  })

回到瀏覽器,如果我們復制一張圖片并在輸入框中執(zhí)行粘貼的動作,將可以在控制臺看到打印出了以 data:image/png;base64 開頭的圖片地址。

輸入框中插入內(nèi)容

經(jīng)過前面兩個步驟,我們后已經(jīng)可以讀取剪貼板中的文本內(nèi)容和圖片內(nèi)容了,接下來就是把它們正確的插入輸入框的光標位置當中。

對于插入內(nèi)容,我們可以直接通過 document.execCommand 方法進行。關于這個方法詳細用法可以在MDN文檔里面找到,在這里我們只需要使用 insertText 和 insertImage 即可。

document.querySelector('.editor').addEventListener('paste', async (e) => {      const result = await onPaste(e)      const imgRegx = /^data:image\/png;base64,/      const command = imgRegx.test(result) ? 'insertImage': 'insertText'       document.execCommand(command, false, result)  })

但是在某些版本的 Chrome 瀏覽器下,insertImage 方法可能會失效,這時候便可以采用另外一種方法,利用 Selection 來實現(xiàn)。而之后選擇并插入 emoji 的操作也會用到它,因此不妨先來了解一下。

當我們在代碼中調(diào)用 window.getSelection() 后會獲得一個 Selection 對象。如果在頁面中選中一些文字,然后在控制臺執(zhí)行 window.getSelection().toString(),就會看到輸出是你所選擇的那部分文字。

與這部分區(qū)域文字相對應的,是一個 range 對象,使用 window.getSelection().getRangeAt(0) 即可以訪問它。range 不僅包含了選中區(qū)域文字的內(nèi)容,還包括了區(qū)域的起點位置 startOffset 和終點位置 endOffset。

我們也可以通過 document.createRange() 的辦法手動創(chuàng)建一個 range,往它里面寫入內(nèi)容并展示在輸入框中。

對于插入圖片來說,要先從 window.getSelection() 獲取 range ,然后往里面插入圖片。

document.querySelector('.editor').addEventListener('paste', async (e) => {    // 讀取剪貼板的內(nèi)容    const result = await onPaste(e)    const imgRegx = /^data:image\/png;base64,/    // 如果是圖片格式(base64),則通過構(gòu)造range的辦法把<img>標簽插入正確的位置    // 如果是文本格式,則通過document.execCommand('insertText')方法把文本插入    if (imgRegx.test(result)) {      const sel = window.getSelection()      if (sel && sel.rangeCount === 1 && sel.isCollapsed) {        const range = sel.getRangeAt(0)        const img = new Image()        img.src = result        range.insertNode(img)        range.collapse(false)        sel.removeAllRanges()        sel.addRange(range)      }    } else {      document.execCommand('insertText', false, result)    }  })

這種辦法也能很好地完成粘貼圖片的功能,并且通用性會更好。接下來我們還會利用 Selection,來完成 emoji 的插入。

插入 emoji

不管是粘貼文本也好,還是圖片也好,我們的輸入框始終是處于聚焦(focus)狀態(tài)。而當我們從表情面板里選擇 emoji 表情的時候,輸入框會先失焦(blur),然后再重新聚焦。由于 document.execCommand 方法必須在輸入框聚焦狀態(tài)下才能觸發(fā),所以對于處理 emoji 插入來說就無法使用了。

上一小節(jié)講過,Selection 可以讓我們拿到聚焦狀態(tài)下所選文本的起點位置 startOffset 和終點位置 endOffset,如果沒有選擇文本而僅僅處于聚焦狀態(tài),那么這兩個位置的值相等(相當于選擇文本為空),也就是光標的位置。只要我們能夠在失焦前記錄下這個位置,那么就能夠通過 range 把 emoji 插入正確的地方了。

首先編寫兩個工具方法。新建一個 cursorPosition.js 文件:

/**   * 獲取光標位置   * @param {DOMElement} element 輸入框的dom節(jié)點   * @return {Number} 光標位置   */  export const getCursorPosition = (element) => {    let caretOffset = 0    const doc = element.ownerDocument || element.document    const win = doc.defaultView || doc.parentWindow    const sel = win.getSelection()    if (sel.rangeCount > 0) {      const range = win.getSelection().getRangeAt(0)      const preCaretRange = range.cloneRange()      preCaretRange.selectNodeContents(element)      preCaretRange.setEnd(range.endContainer, range.endOffset)      caretOffset = preCaretRange.toString().length    }    return caretOffset  }  /**   * 設置光標位置   * @param {DOMElement} element 輸入框的dom節(jié)點   * @param {Number} cursorPosition 光標位置的值   */  export const setCursorPosition = (element, cursorPosition) => {    const range = document.createRange()    range.setStart(element.firstChild, cursorPosition)    range.setEnd(element.firstChild, cursorPosition)    const sel = window.getSelection()    sel.removeAllRanges()    sel.addRange(range)  }

有了這兩個方法以后,就可以放入 editor 節(jié)點里面使用了。首先在節(jié)點的 keyup 和 click 事件里記錄光標位置:

let cursorPosition = 0  const editor = document.querySelector('.editor')  editor.addEventListener('click', async (e) => {    cursorPosition = getCursorPosition(editor)  })  editor.addEventListener('keyup', async (e) => {    cursorPosition = getCursorPosition(editor)  })

記錄下光標位置后,便可通過調(diào)用 insertEmoji() 方法插入 emoji 字符了。

insertEmoji (emoji) {    const text = editor.innerHTML    // 插入 emoji    editor.innerHTML = text.slice(0, cursorPosition) + emoji + text.slice(cursorPosition, text.length)    // 光標位置后挪一位,以保證在剛插入的 emoji 后面    setCursorPosition(editor, this.cursorPosition + 1)    // 更新本地保存的光標位置變量(注意 emoji 占兩個字節(jié)大小,所以要加1)    cursorPosition = getCursorPosition(editor) + 1 //  emoji 占兩位  }

感謝各位的閱讀,以上就是“怎么使用Web富文本輸入框”的內(nèi)容了,經(jīng)過本文的學習后,相信大家對怎么使用Web富文本輸入框這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

web
AI