You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

移动端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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 01:15:13