时间:2024-02-17 23:10:02 | 来源:网站运营
时间:2024-02-17 23:10:02 来源:网站运营
怎么用HTML开发一个动态留言板?:jsx { type: 'string', // 表示Entity的类型; eg:'LINK', 'TOKEN', 'PHOTO', 'IMAGE' mutability: 'MUTABLE' | 'IMMUTABLE' | 'SEGMENTED', // 此属性表示在编辑器中编辑文本范围时使用此实体对象注释的文本范围的行为。 data: 'object', // Entity的元数据; 用于存储你想要存储在该Entity里的任何信息 }
其中 Mutability 这条属性三个值的含义分别是: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;
可以看到并没有出现一个带工具栏的文本框,而是生成一个可编辑区域,接下来我们将赋予他独特的功能。handleBeforeInput?: ( chars: string, // 输入的内容 editorState: EditorState, // 编辑器的文本内容状态 eventTimeStamp: number,) => 'handled' | 'not-handled'
当 handleBeforeInput 返回 handled 的时候输入的默认行为会被阻止,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) => { // 获取编辑器的文本内容状态 const currentContent = editorState.getCurrentContent(); // 获取编辑器文本长度,getPlainText返回当前编辑器的文本内容,字符串类型 const currentContentLength = currentContent.getPlainText('').length; if (currentContentLength > MAX_LENGTH - 1) { // 当前文本长度大于最大长度的时候阻止输入,反之允许输入 return 'handled'; } return 'not-handled'; } return ( <div className='box'> <Editor editorState={editorState} onChange={handleEditorChange} handleBeforeInput={handleBeforeInput} /> <button className='btn'>提交</button> </div> );}
这里可能有个疑惑:MAX_LENGTH 为什么要减一?const getLengthOfSelectedText = () => { // 获取编辑器的选择状态 const currentSelection = editorState.getSelection(); // 返回选择状态,锚点和焦点的偏移量相同(没有选择)和锚点和焦点的block_key相同时返回true const isCollapsed = currentSelection.isCollapsed(); let length = 0; if (!isCollapsed) { const currentContent = editorState.getCurrentContent(); // 获取选择范围的起始位置block_key const startKey = currentSelection.getStartKey(); // 获取选择范围的结束位置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(); // 终点在结束block中的偏移量 const endSelectedTextLength = currentSelection.getEndOffset(); // getKeyAfter返回指定key的block后面一个block的key const keyAfterEnd = currentContent.getKeyAfter(endKey); let currentKey = startKey; // 累加起始block到结束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 的概念,稍微复杂点,不过用途很简单,就是获取选择的长度。现在我们来改造下 handleBeforeInput:const handleBeforeInput = (_, editorState) => { const currentContent = editorState.getCurrentContent(); const currentContentLength = currentContent.getPlainText('').length; // 实际长度 = 当前内容的长度-选择的长度(被替换的长度) if (currentContentLength - getLengthOfSelectedText() > MAX_LENGTH - 1) { return 'handled'; } return 'not-handled'; }
依葫芦画瓢,现在我们来添加 handlePastedText,如果是粘贴情况下,则多了个 pastedText(被粘贴的文本)参数。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'; };
为了有更好的使用体验,可以在编辑器右下角加一个当前内容长度/最大长度的提示。改造一下 handleEditorChange 方法,把当前文本长度用 state 存储起来。const handleEditorChange = (newEditorState) => { const currentContent = newEditorState.getCurrentContent(); const currentContentLength = currentContent.getPlainText('').length; setLength(currentContentLength); setEditorState(newEditorState); }
调整一下样式,看下效果:const HANDLE_REGEX = /@[/w]+/g; const compositeDecorator = new CompositeDecorator([ { strategy: (contentBlock, callback) => { // 编辑器每次change都会触发此函数,得到内容文本。 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), );
看下效果:const insertEntity = (entityData) => { let contentState = editorState.getCurrentContent(); // 创建实体 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; // 获取实体在编辑器中显示的范围,目的是让光标在插入实体后停留在实体尾部 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); };
看下效果:draftjs:https://draftjs.org/公众号:全象云低代码
关键词:动态,留言