反应内容可编辑和光标位置

问题描述 投票:0回答:4

我有简单的组件

class ContentEditable extends React.Component {
  constructor(props) {
    super(props);
    this.handleInput = this.handleInput.bind(this);
  }

  handleInput(event) {
    let html = event.target.innerHTML;
    if (this.props.onChange && html !== this.lastHtml) {
      this.props.onChange({ target: { value: html, name: this.props.name } });
      this.lastHtml = html;
    }
  }

  render() {
    return (
      <span
        contentEditable="true"
        onInput={this.handleInput}
        className={"auto " + this.props.className}
        dangerouslySetInnerHTML={{ __html: this.props.value }}
      />
    );
  }
}
export default ContentEditable;

<ContentEditable
  value={this.state.name}
  onChange={e => {
    this.setState({ name: e.target.value });
  }}
/>;

组件可以工作,但光标位置永远不会改变,它始终位于第一个位置,而不是渲染文本之后。

我测试了这个论坛的示例,但它对我不起作用。

我使用

React 15.6.1
并在
chrome (Os X)
上进行测试。

有什么提示我可以解决这个问题吗?

javascript reactjs
4个回答
15
投票

带有

useRef
的解决方案如下所示。

const ContentEditableWithRef = (props) => {
  const defaultValue = useRef(props.value);

  const handleInput = (event) => {
    if (props.onChange) {
      props.onChange(event.target.innerHTML);
    }
  };

  return (
    <span
      contentEditable
      onInput={handleInput}
      className="custom-textarea"
      dangerouslySetInnerHTML={{ __html: defaultValue.current }}
    />
  );
};

完整的代码和盒子可用这里,用它来看看它是如何工作的!

代码沙箱示例包含两个组件

  • 一个是
    ContentEditableWithRef
    ,它解决了
    useRef
    的问题,这是一个不受控制的组件,并且
  • 另一个组件是
    ContentEditable
    ,它使用
    useState
    来解决同样的问题。

这里

useRef
将保持默认值/初始值与组件渲染周期分开,因此它将保留原始值,而不会受到我们在React组件中执行的其他类型操作的影响。

这个组件做了两件事

  1. 这将使用
    onChange
    方法将用户输入发送到父组件
  2. 从父组件中获取名为
    prop
    的默认值,并在自定义输入框中呈现该值(使用
    value
    创建)
    
        

2
投票
contentEditable

修复即可。只需将 event.target 的

ref
分配给
textContent
ref



0
投票

所以我尝试了@rufatZZ解决方案,它解决了我的第二个问题(从父级更新值),但没有解决丢失光标位置的问题。

所以我混合了两种解决方案,主要是@rufatZZ解决方案,但使用@Lakshmaji提出的带有

const textareaEl = useRef<HTMLDivElement>(null); const handleChange = (e: React.ChangeEvent<HTMLDivElement>) => { textareaEl.current.textContent = e.target.textContent; onChange(e); // If you have change event for form/state }; /** If you're passing value from state, you can mutate it each change for not losing cursor position. */ useEffect(() => { if (value) { textareaEl.current.textContent = value; } }, [value]); return ( <div id="textarea-element" ref={textareaEl} contentEditable={true} suppressContentEditableWarning={true} onChange={handleChange} /> )

的值参考,它对我有用。

这里:

dangerouslySetInnerHTML



0
投票
// this was from @Lakshmaji and keeps me the cursor position const defaultValue = useRef(value); // this was from @rufatZZ and allows me to keep the value even with updates from the parent const textAreaElement = useRef<HTMLDivElement>(null); /** If you're passing value from state, you can mutate it each change for not losing cursor position. */ useEffect(() => { if (value && textAreaElement.current) { textAreaElement.current.textContent = value; } }, [value]); function handleInputChange(event) { if (textAreaElement.current) { textAreaElement.current.textContent = event.currentTarget.innerText; } // propagate the changed value to the parent onChange?.(event.currentTarget.innerText); } return ( <span contentEditable ref={textAreaElement} suppressContentEditableWarning onInput={handleInputChange} dangerouslySetInnerHTML={{ __html: defaultValue.current }} /> );

useRef
 的一个
dangerouslySetInnerHTML

如果您使用占位符或类似的内容,请使用 
function GrowingTextArea() { const [value, setValue] = useState('') return ( <span role="textbox" contentEditable style={{ whiteSpace: 'pre-wrap' }} onBlur={(event) => setValue(event.currentTarget.innerText)} > {value} </span> ) }

CSS 选择器

 添加此内容
:empty

© www.soinside.com 2019 - 2024. All rights reserved.