如何在Draft.js中实现分段固定且不可编辑删除的只读标题?
我之前在项目里刚好折腾过类似的需求,Draft.js要实现这种分Section且Header只读不可删的功能,得结合自定义Block和变更拦截来搞,一步步给你拆解:
在Draft.js中实现只读Section Header的方案
核心思路是:把每个Section Header标记为特殊的不可编辑Block类型,然后通过自定义渲染、变更拦截、光标限制这几层,彻底锁死Header的修改/删除操作。
1. 给Header注册专属Block类型
首先得让Draft.js识别哪些是Header,我们可以给Header创建一个专属的Block类型(比如叫'section-header'),和普通的可编辑段落区分开。初始化编辑器内容时,把每个Section的Header作为这个类型的Block插入:
import { EditorState, ContentBlock, ContentState } from 'draft-js'; // 初始化带Section的编辑器内容 const initialContentBlocks = [ // History Header + 内容 new ContentBlock({ key: 'header-history', type: 'section-header', text: 'History', characterList: Array('History'.length).fill({}), }), new ContentBlock({ key: 'content-history', type: 'unstyled', text: '此处为文本...', characterList: Array('此处为文本...'.length).fill({}), }), // Examination Header + 内容 new ContentBlock({ key: 'header-examination', type: 'section-header', text: 'Examination', characterList: Array('Examination'.length).fill({}), }), new ContentBlock({ key: 'content-examination', type: 'unstyled', text: '此处为文本...', characterList: Array('此处为文本...'.length).fill({}), }), ]; const initialEditorState = EditorState.createWithContent( ContentState.createFromBlockArray(initialContentBlocks) );
2. 自定义Header的渲染组件
接下来要告诉Draft.js怎么渲染这个'section-header'类型的Block——直接渲染成不可编辑的元素,还要加样式区分,同时阻止用户选中它:
const CustomBlockRenderer = (props) => { const { block } = props; if (block.getType() === 'section-header') { return ( <div style={{ fontSize: '1.2rem', fontWeight: 'bold', margin: '1rem 0 0.5rem', color: '#2c3e50', background: '#f8f9fa', padding: '0.3rem 0.8rem', borderRadius: '4px' }} contentEditable={false} pointerEvents="none" // 彻底阻止点击/选中 > {block.getText()} </div> ); } // 普通文本Block用默认渲染逻辑 return null; }; // 把这个渲染函数传给Editor组件 <Editor editorState={editorState} onChange={handleEditorChange} blockRendererFn={CustomBlockRenderer} />
3. 拦截非法变更,禁止修改/删除Header
光渲染只读还不够,用户可能通过Backspace删除Header、或者粘贴内容覆盖Header,所以必须在onChange回调里拦截这类操作:
const handleEditorChange = (newEditorState) => { const oldContent = editorState.getCurrentContent(); const newContent = newEditorState.getCurrentContent(); // 检查有没有Header被修改(内容/类型变了) const hasHeaderModified = oldContent.getBlocksAsArray().some((oldBlock, idx) => { const newBlock = newContent.getBlocksAsArray()[idx]; if (!newBlock) return true; // 原Header被删除了 if (oldBlock.getType() === 'section-header') { return oldBlock.getText() !== newBlock.getText() || oldBlock.getType() !== newBlock.getType(); } return false; }); // 检查有没有Header被整段删除 const oldHeaderCount = oldContent.getBlocksAsArray().filter(b => b.getType() === 'section-header').length; const newHeaderCount = newContent.getBlocksAsArray().filter(b => b.getType() === 'section-header').length; const hasHeaderRemoved = oldHeaderCount > newHeaderCount; // 只要有非法操作,就拒绝这次变更 if (hasHeaderModified || hasHeaderRemoved) { return; } // 合法操作才更新编辑器状态 setEditorState(newEditorState); };
4. 额外优化:不让光标进入Header
最后再加一层保险——阻止光标定位到Header里,彻底断了用户编辑Header的可能:
// 阻止在Header里输入内容 const handleBeforeInput = (chars, editorState) => { const selectedBlock = editorState.getCurrentContent().getBlockForKey( editorState.getSelection().getStartKey() ); if (selectedBlock.getType() === 'section-header') { return 'handled'; // 告诉Draft.js我们处理了这个输入,不用执行默认逻辑 } return 'not-handled'; }; // 阻止在Header里执行编辑快捷键(比如复制/剪切) const handleKeyCommand = (command, editorState) => { const selectedBlock = editorState.getCurrentContent().getBlockForKey( editorState.getSelection().getStartKey() ); if (selectedBlock.getType() === 'section-header') { return 'handled'; } return 'not-handled'; }; // 把这两个回调加到Editor组件 <Editor editorState={editorState} onChange={handleEditorChange} blockRendererFn={CustomBlockRenderer} onBeforeInput={handleBeforeInput} handleKeyCommand={handleKeyCommand} />
这样一套组合拳下来,Section Header就彻底变成了只读、不可删除、用户碰都碰不到的固定元素,而每个Section里的内容则可以正常编辑。
内容的提问来源于stack exchange,提问作者David Thorisson
相关产品推荐
相关产品推荐

