移动端React contenteditable div获焦后自动失焦问题求助
解决移动端contenteditable div首次点击失焦问题
你的问题根源在于首次聚焦时直接修改DOM内容并触发组件重渲染:当onFocus里清空textContent后调用setText,组件会重新渲染,原来的div被新的DOM节点替换,导致焦点丢失。再次点击时,DOM内容已经是空的,不会触发重渲染,所以能正常输入。
下面是修复后的代码,核心是用state管理实际输入内容,通过ref操作DOM处理占位符,避免不必要的重渲染:
import { useState, useRef, useEffect } from 'react'; const CustomTextEntry = ({ placeholder }) => { const [text, setText] = useState(''); const editableRef = useRef(null); const handleFocus = () => { // 聚焦时如果没有输入内容,清空占位符并定位光标 if (!text) { editableRef.current.textContent = ''; setTimeout(() => { const range = document.createRange(); const selection = window.getSelection(); range.selectNodeContents(editableRef.current); range.collapse(true); selection.removeAllRanges(); selection.addRange(range); }, 0); } }; const handleBlur = () => { // 失焦时如果为空,恢复占位符 if (!text.trim()) { editableRef.current.textContent = placeholder; } }; const handleInput = (e) => { setText(e.target.textContent); // 阻止换行(按需保留) if (e.nativeEvent.data === '\n') { e.preventDefault(); } }; // 初始渲染设置占位符 useEffect(() => { if (!text) { editableRef.current.textContent = placeholder; } }, []); return ( <div ref={editableRef} contentEditable={true} suppressContentEditableWarning={true} onFocus={handleFocus} onBlur={handleBlur} onInput={handleInput} style={{ minHeight: '40px', padding: '8px', border: '1px solid #ccc', borderRadius: '4px', whiteSpace: 'pre-wrap', // 实现自动换行 }} /> ); };
关键改动说明:
- 用ref操作DOM:通过
useRef获取div节点,直接修改DOM内容而不触发组件重渲染,避免焦点丢失。 - 分离状态与占位符:state仅存储用户实际输入的内容,占位符通过DOM操作控制,减少不必要的state更新。
- 聚焦后光标定位:添加
setTimeout确保光标在DOM更新后正确定位,避免移动端光标消失的问题。 - 简化逻辑:移除原代码中直接修改DOM后同步state的操作,改为在
onInput中同步输入内容到state。
另外推荐一种更简洁的占位符实现方式,用CSS伪元素代替JS逻辑:
[contenteditable][data-placeholder]:empty:before { content: attr(data-placeholder); color: #aaa; pointer-events: none; /* 避免点击占位符触发额外事件 */ }
对应组件只需修改:
// 移除handleBlur和useEffect中的占位符逻辑,div添加data-placeholder属性 return ( <div ref={editableRef} contentEditable={true} suppressContentEditableWarning={true} onFocus={handleFocus} onInput={handleInput} data-placeholder={placeholder} style={/* 样式保留 */} /> );
这种方式完全通过CSS控制占位符显示,避免了JS操作DOM可能引发的焦点问题,兼容性更好。
内容的提问来源于stack exchange,提问作者szammyboi
相关产品推荐
相关产品推荐

