如何在Draft JS中自动聚焦新插入的原子块?
自定义Draft.js原子块插入后自动聚焦的解决方案
问题背景
已通过给定代码在Draft.js中插入自定义原子块,但尝试forceSelection、focus等方法均无法实现自动聚焦需求。
解决方案
Draft.js的原子块本身为不可编辑区块,因此需分两种场景处理自动聚焦:
场景1:聚焦到原子块后的编辑器光标位置
修改insertBlock函数,在插入原子块后,计算并设置光标到原子块后方的可编辑位置,同时手动触发编辑器聚焦:
- 首先给
Editor组件添加ref:
import { Editor } from 'draft-js'; import { useRef } from 'react'; // 组件内定义编辑器ref const editorRef = useRef(null); // 渲染Editor时绑定ref <Editor ref={editorRef} editorState={editorState} onChange={setEditorState} // 其他配置项 />
- 修改
insertBlock函数:
const insertBlock = () => { const contentState = editorState.getCurrentContent(); const contentStateWithEntity = contentState.createEntity( "EDITORELEMENT", "MUTABLE", { a: "b" } ); const entityKey = contentStateWithEntity.getLastCreatedEntityKey(); const newEditorState = EditorState.set(editorState, { currentContent: contentStateWithEntity }); // 插入原子块 const editorStateWithAtomicBlock = AtomicBlockUtils.insertAtomicBlock( newEditorState, entityKey, "text" ); const updatedContent = editorStateWithAtomicBlock.getCurrentContent(); const selection = editorStateWithAtomicBlock.getSelection(); const blockKey = selection.getAnchorKey(); const block = updatedContent.getBlockForKey(blockKey); let finalEditorState; // 原子块插入到区块末尾时,新建空段落并聚焦 if (selection.getAnchorOffset() === block.getLength()) { const newBlock = ContentBlock.createFromText("", "unstyled"); const newContent = updatedContent.merge({ blockMap: updatedContent.getBlockMap().set(newBlock.getKey(), newBlock), selectionAfter: new SelectionState({ anchorKey: newBlock.getKey(), anchorOffset: 0, focusKey: newBlock.getKey(), focusOffset: 0 }) }); finalEditorState = EditorState.push( editorStateWithAtomicBlock, newContent, "insert-fragment" ); finalEditorState = EditorState.forceSelection( finalEditorState, newContent.getSelectionAfter() ); } else { // 原子块插入到区块中间时,直接将光标移到原子块后方 const newSelection = selection.merge({ anchorOffset: selection.getAnchorOffset() + 1, focusOffset: selection.getAnchorOffset() + 1 }); finalEditorState = EditorState.forceSelection( editorStateWithAtomicBlock, newSelection ); } setEditorState(finalEditorState); // 延迟触发聚焦,确保DOM已更新 setTimeout(() => { editorRef.current?.focus(); }, 0); };
场景2:聚焦原子块内部的可编辑元素
如果自定义原子块内部包含可编辑元素(如输入框),可通过ref传递在组件挂载时触发聚焦:
- 修改
EditorElement和EditorBlock组件:
import { useEffect, useRef } from 'react'; const EditorElement: React.FC = (props: any) => { const editableRef = useRef<HTMLInputElement>(null); // 组件挂载时聚焦内部可编辑元素 useEffect(() => { editableRef.current?.focus(); }, []); return ( <div className="EditorElement"> <EditorBlock {...props} editableRef={editableRef} /> </div> ); }; // 示例EditorBlock组件,包含可编辑输入框 const EditorBlock: React.FC<any> = (props) => { return ( <div className="editor-block"> <input ref={props.editableRef} placeholder="请输入内容" /> </div> ); };
关键注意事项
- Draft.js的选区更新需在DOM渲染完成后执行,因此需用
setTimeout延迟触发focus方法 - 原子块本身不可编辑,若要聚焦编辑器,必须将光标定位到可编辑的文本区块中
- 内部可编辑元素的聚焦需确保ref正确传递,且在组件挂载生命周期触发
内容的提问来源于stack exchange,提问作者Adil Akhmetov
相关产品推荐
相关产品推荐

