国产成人精品无码青草_亚洲国产美女精品久久久久∴_欧美人与鲁交大毛片免费_国产果冻豆传媒麻婆精东

15158846557 在線咨詢 在線咨詢
15158846557 在線咨詢
所在位置: 首頁 > 營銷資訊 > 網(wǎng)站運營 > 怎么用HTML開發(fā)一個動態(tài)留言板?

怎么用HTML開發(fā)一個動態(tài)留言板?

時間:2024-02-17 23:10:02 | 來源:網(wǎng)站運營

時間:2024-02-17 23:10:02 來源:網(wǎng)站運營

怎么用HTML開發(fā)一個動態(tài)留言板?:

draftjs 簡介

draftjs 是用于 react 的富文本編輯器框架,它并不能開箱即用,但是它提供了很多用于開發(fā)富文本的 API。基于此,開發(fā)者能夠搭建出定制化的富文本編輯器。draftjs 有幾個重要的概念:EditorState、Entity、SelectionState、CompositeDecorator。

EditorState

EditorState 是編輯器的頂級狀態(tài)對象。它是一個不可變數(shù)據(jù),表示 Draft 編輯器的整個狀態(tài),包括:

draftjs 基于不可變(immutable)數(shù)據(jù),因此對編輯器的修改都需要新生成一個 EditorState 對象傳入編輯器,以實現(xiàn)數(shù)據(jù)更新。

Entity

Entity 用來描述帶有元數(shù)據(jù)的文本,使一段文本可以攜帶任意類型的數(shù)據(jù),提供了更加豐富的功能,鏈接、提及和嵌入的內(nèi)容都可以通過 Entity 來實現(xiàn)。

Entity的結(jié)構(gòu)

jsx { type: 'string', // 表示Entity的類型; eg:'LINK', 'TOKEN', 'PHOTO', 'IMAGE' mutability: 'MUTABLE' | 'IMMUTABLE' | 'SEGMENTED', // 此屬性表示在編輯器中編輯文本范圍時使用此實體對象注釋的文本范圍的行為。 data: 'object', // Entity的元數(shù)據(jù); 用于存儲你想要存儲在該Entity里的任何信息 } 其中 Mutability 這條屬性三個值的含義分別是:

SelectionState

SelectionState 表示編輯器中的選擇范圍。一個選擇范圍有兩點:錨點(起點)和焦點(終點)。

CompositeDecorator

Decorator 概念的基礎(chǔ)是掃描給定 ContentBlock 的內(nèi)容,根據(jù)定義的策略定位到匹配位置,然后用指定的 React 組件呈現(xiàn)它們。

實現(xiàn)一個留言框

首先明確需求:

  1. 有長度限制,暫定 200 個字;
  2. 提及(@)時高亮,當(dāng)用戶輸入 @ 符號后將 @ 符號后面的文字高亮;
  3. 插入鏈接;
先實現(xiàn)一個基礎(chǔ)的編輯器:

import React from 'react'import { Editor, EditorState } from 'draft-js';import 'draft-js/dist/Draft.css';import './App.css';function MyEditor() { const [editorState, setEditorState] = React.useState( () => EditorState.createEmpty(), ); const handleEditorChange = (newEditorState) => { setEditorState(newEditorState); } return ( <div className='box'> <Editor editorState={editorState} onChange={handleEditorChange} /> <button className='btn'>提交</button> </div> );}export default MyEditor;可以看到并沒有出現(xiàn)一個帶工具欄的文本框,而是生成一個可編輯區(qū)域,接下來我們將賦予他獨特的功能。

需求一:限制留言長度

編輯器的輸入形式有兩種:鍵盤錄入和粘貼,一般的 input 輸入框我們可以通過 maxLength 來限制,draftjs 沒有這個屬性,不過提供了 handleBeforeInputhandlePastedText 這兩種方法。

handleBeforeInput

handleBeforeInput?: ( chars: string, // 輸入的內(nèi)容 editorState: EditorState, // 編輯器的文本內(nèi)容狀態(tài) eventTimeStamp: number,) => 'handled' | 'not-handled'當(dāng) handleBeforeInput 返回 handled 的時候輸入的默認(rèn)行為會被阻止,handlePastedText 同理。

handlePastedText

handlePastedText?: ( text: string, html?: string, editorState: EditorState,) => 'handled' | 'not-handled'接下來修改我們的代碼:

const MAX_LENGTH = 200;function MyEditor() { const [editorState, setEditorState] = React.useState( () => EditorState.createEmpty(), ); const handleEditorChange = (newEditorState) => { setEditorState(newEditorState); } const handleBeforeInput = (_, editorState) => { // 獲取編輯器的文本內(nèi)容狀態(tài) const currentContent = editorState.getCurrentContent(); // 獲取編輯器文本長度,getPlainText返回當(dāng)前編輯器的文本內(nèi)容,字符串類型 const currentContentLength = currentContent.getPlainText('').length; if (currentContentLength > MAX_LENGTH - 1) { // 當(dāng)前文本長度大于最大長度的時候阻止輸入,反之允許輸入 return 'handled'; } return 'not-handled'; } return ( <div className='box'> <Editor editorState={editorState} onChange={handleEditorChange} handleBeforeInput={handleBeforeInput} /> <button className='btn'>提交</button> </div> );}這里可能有個疑惑:MAX_LENGTH 為什么要減一?

原因是 handleBeforeInput 觸發(fā)在輸入之前,所以 getPlainText 返回的是編輯器內(nèi)容變化之前的內(nèi)容。之前的內(nèi)容長度+輸入的內(nèi)容長度<最大長度,因為是鍵盤輸入,所以輸入的內(nèi)容長度始終為1。這還沒完,還有選擇文本內(nèi)容后再輸入的情況沒有處理。這就需要用到SelectionState了。

添加 getLengthOfSelectedText 函數(shù):

const getLengthOfSelectedText = () => { // 獲取編輯器的選擇狀態(tài) const currentSelection = editorState.getSelection(); // 返回選擇狀態(tài),錨點和焦點的偏移量相同(沒有選擇)和錨點和焦點的block_key相同時返回true const isCollapsed = currentSelection.isCollapsed(); let length = 0; if (!isCollapsed) { const currentContent = editorState.getCurrentContent(); // 獲取選擇范圍的起始位置block_key const startKey = currentSelection.getStartKey(); // 獲取選擇范圍的結(jié)束位置block_key const endKey = currentSelection.getEndKey(); if (startKey === endKey) { // 選擇范圍在同一個block,那么選擇長度=終點偏移量-起點偏移量 length += currentSelection.getEndOffset() - currentSelection.getStartOffset(); } else { const startBlockTextLength = currentContent.getBlockForKey(startKey).getLength(); // 起始block的選擇長度 = 起始block的長度-起點偏移量 const startSelectedTextLength = startBlockTextLength - currentSelection.getStartOffset(); // 終點在結(jié)束block中的偏移量 const endSelectedTextLength = currentSelection.getEndOffset(); // getKeyAfter返回指定key的block后面一個block的key const keyAfterEnd = currentContent.getKeyAfter(endKey); let currentKey = startKey; // 累加起始block到結(jié)束block中間的block的選擇長度 while (currentKey && currentKey !== keyAfterEnd) { if (currentKey === startKey) { length += startSelectedTextLength + 1; } else if (currentKey === endKey) { length += endSelectedTextLength; } else { length += currentContent.getBlockForKey(currentKey).getLength() + 1; } currentKey = currentContent.getKeyAfter(currentKey); } } } return length; };這個方法有些長,又涉及到 draftjs 的幾個 api 和 block 的概念,稍微復(fù)雜點,不過用途很簡單,就是獲取選擇的長度?,F(xiàn)在我們來改造下 handleBeforeInput:

const handleBeforeInput = (_, editorState) => { const currentContent = editorState.getCurrentContent(); const currentContentLength = currentContent.getPlainText('').length; // 實際長度 = 當(dāng)前內(nèi)容的長度-選擇的長度(被替換的長度) if (currentContentLength - getLengthOfSelectedText() > MAX_LENGTH - 1) { return 'handled'; } return 'not-handled'; }依葫蘆畫瓢,現(xiàn)在我們來添加 handlePastedText,如果是粘貼情況下,則多了個 pastedText(被粘貼的文本)參數(shù)。

const handlePastedText = (pastedText) => { const currentContent = editorState.getCurrentContent(); const currentContentLength = currentContent.getPlainText('').length; const selectedTextLength = getLengthOfSelectedText(); if (currentContentLength + pastedText.length - selectedTextLength > maxLength - 1) { return 'handled'; } return 'not-handled'; };為了有更好的使用體驗,可以在編輯器右下角加一個當(dāng)前內(nèi)容長度/最大長度的提示。改造一下 handleEditorChange 方法,把當(dāng)前文本長度用 state 存儲起來。

const handleEditorChange = (newEditorState) => { const currentContent = newEditorState.getCurrentContent(); const currentContentLength = currentContent.getPlainText('').length; setLength(currentContentLength); setEditorState(newEditorState); }調(diào)整一下樣式,看下效果:

至此我們就完成了第一個需求。

需求二:提及(@)時高亮

一般提及都會有把 @ 符號后面的文字改變顏色以示區(qū)別,我們可以用一個正則表達(dá)式來匹配 @ 符號和后面的文本,然后在替換成我們自定義的 ReactNode,就可以實現(xiàn)高亮,這正是 Decorator 的用武之地。

我們只需要創(chuàng)建一個 CompositeDecorator 實例,在編輯器初始化的時候傳入 createEmpty 中就可以了。

const HANDLE_REGEX = /@[/w]+/g; const compositeDecorator = new CompositeDecorator([ { strategy: (contentBlock, callback) => { // 編輯器每次change都會觸發(fā)此函數(shù),得到內(nèi)容文本。 const text = contentBlock.getText(); let matchArr, start; while ((matchArr = HANDLE_REGEX.exec(text)) !== null) { // 得到匹配值的起始位置和偏移量,callback之后就會被此decorator的component替換 start = matchArr.index; callback(start, start + matchArr[0].length); } }, component: (props) => { return ( <span className='mention' data-offset-key={props.offsetKey} > {props.children} </span> ); }, }, ]); const [editorState, setEditorState] = React.useState( () => EditorState.createEmpty(compositeDecorator), );看下效果:

需求三:插入鏈接

鏈接顯示文字,鼠標(biāo)移入提示 url。純文本已經(jīng)無法描述這段信息了,這就需要用到 Entity。添加 insertEntity 函數(shù):

const insertEntity = (entityData) => { let contentState = editorState.getCurrentContent(); // 創(chuàng)建實體 contentState = contentState.createEntity('LINK', 'IMMUTABLE', entityData); const entityKey = contentState.getLastCreatedEntityKey(); let selection = editorState.getSelection(); // 判斷是替換還是插入 if (selection.isCollapsed()) { contentState = Modifier.insertText( contentState, selection, entityData.name + ' ', undefined, entityKey, ); } else { contentState = Modifier.replaceText( contentState, selection, entityData.name + ' ', undefined, entityKey, ); } let end; // 獲取實體在編輯器中顯示的范圍,目的是讓光標(biāo)在插入實體后停留在實體尾部 contentState.getFirstBlock().findEntityRanges( (character) => character.getEntity() === entityKey, (_, _end) => { end = _end; }); let newEditorState = EditorState.set(editorState, { currentContent: contentState }); selection = selection.merge({ anchorOffset: end, focusOffset: end, }); newEditorState = EditorState.forceSelection(newEditorState, selection); handleEditorChange(newEditorState); };看下效果:

完成!

完整代碼

由于完整代碼占用篇幅較大,獲取完整代碼請關(guān)注公眾號“全象云低代碼”,回復(fù)“留言框完整代碼”即可獲取。

引用內(nèi)容

draftjs:https://draftjs.org/
公眾號:全象云低代碼

GitHub:GitHub - quanxiang-cloud/quanxiang: Quanxiang low-code platform deployment tool.

關(guān)鍵詞:動態(tài),留言

74
73
25
news

版權(quán)所有? 億企邦 1997-2025 保留一切法律許可權(quán)利。

為了最佳展示效果,本站不支持IE9及以下版本的瀏覽器,建議您使用谷歌Chrome瀏覽器。 點擊下載Chrome瀏覽器
關(guān)閉