React.js:contentEditable的onChange事件

如何监听contentEditable基于控件的更改事件

var Number = React.createClass({
    render: function() {
        return <div>
            <span contentEditable={true} onChange={this.onChange}>
                {this.state.value}
            </span>
            =
            {this.state.value}
        </div>;
    },
    onChange: function(v) {
        // Doesn't fire :(
        console.log('changed', v);
    },
    getInitialState: function() {
        return {value: '123'}
    }    
});

React.renderComponent(<Number />, document.body);

http://jsfiddle.net/NV/kb3gN/1621/

SamStafan十三2020/03/12 15:15:13

这是由lovasoa整合的大部分组件:https : //github.com/lovasoa/react-contenteditable/blob/master/index.js

他在emitChange中填充事件

emitChange: function(evt){
    var html = this.getDOMNode().innerHTML;
    if (this.props.onChange && html !== this.lastHtml) {
        evt.target = { value: html };
        this.props.onChange(evt);
    }
    this.lastHtml = html;
}

我正在成功使用类似的方法

十三古一2020/03/12 15:15:13

由于完成编辑后,总是会失去元素的焦点,因此您可以简单地使用onBlur挂钩。

<div onBlur={(e)=>{console.log(e.currentTarget.textContent)}} contentEditable suppressContentEditableWarning={true}>
     <p>Lorem ipsum dolor.</p>
</div>
西门樱前端2020/03/12 15:15:13

编辑:请参阅Sebastien Lorber的答案,该答案修复了我的实现中的错误。


使用onInput事件,也可以使用onBlur作为后备。您可能需要保存以前的内容,以防止发送额外的事件。

我个人将其作为渲染功能。

var handleChange = function(event){
    this.setState({html: event.target.value});
}.bind(this);

return (<ContentEditable html={this.state.html} onChange={handleChange} />);

jsbin

它使用围绕contentEditable的简单包装器。

var ContentEditable = React.createClass({
    render: function(){
        return <div 
            onInput={this.emitChange} 
            onBlur={this.emitChange}
            contentEditable
            dangerouslySetInnerHTML={{__html: this.props.html}}></div>;
    },
    shouldComponentUpdate: function(nextProps){
        return nextProps.html !== this.getDOMNode().innerHTML;
    },
    emitChange: function(){
        var html = this.getDOMNode().innerHTML;
        if (this.props.onChange && html !== this.lastHtml) {

            this.props.onChange({
                target: {
                    value: html
                }
            });
        }
        this.lastHtml = html;
    }
});
Davaid阳光2020/03/12 15:15:13

编辑2015

有人用我的解决方案在NPM上做了一个项目:https : //github.com/lovasoa/react-contenteditable

编辑06/2016:我刚刚遇到了一个新问题,当浏览器尝试“重新格式化”刚刚给他的html时,会导致组件始终重新呈现,这会出现一个新问题。看到

编辑07/2016:这是我的生产contentEditable实现。它有一些react-contenteditable您可能想要的其他选项,包括:

  • 锁定
  • 命令式API允许嵌入html片段
  • 重新格式化内容的能力

摘要:

在我遇到新问题之前,FakeRainBrigand的解决方案对我来说已经工作了一段时间。ContentEditables令人痛苦,并且很难与React轻松应对。

这个JSFiddle演示了这个问题。

如您所见,当您键入一些字符并单击时Clear,不会清除内容。这是因为我们尝试将contenteditable重置为最后一个已知的虚拟dom值。

因此,似乎:

  • 您需要shouldComponentUpdate防止插入符位置跳动
  • 如果使用shouldComponentUpdate这种方式,则不能依赖React的VDOM差异算法

因此,您需要额外的一行,以便每当shouldComponentUpdate返回yes时,就可以确保DOM内容实际上已更新。

因此,此处的版本添加了componentDidUpdate,变为:

var ContentEditable = React.createClass({
    render: function(){
        return <div id="contenteditable"
            onInput={this.emitChange} 
            onBlur={this.emitChange}
            contentEditable
            dangerouslySetInnerHTML={{__html: this.props.html}}></div>;
    },

    shouldComponentUpdate: function(nextProps){
        return nextProps.html !== this.getDOMNode().innerHTML;
    },

    componentDidUpdate: function() {
        if ( this.props.html !== this.getDOMNode().innerHTML ) {
           this.getDOMNode().innerHTML = this.props.html;
        }
    },

    emitChange: function(){
        var html = this.getDOMNode().innerHTML;
        if (this.props.onChange && html !== this.lastHtml) {
            this.props.onChange({
                target: {
                    value: html
                }
            });
        }
        this.lastHtml = html;
    }
});

虚拟dom仍然过时,并且它可能不是最有效的代码,但至少它确实有效:) 我的错误已解决


细节:

1)如果放置了shouldComponentUpdate以避免插入符号跳动,则contenteditable永不放弃(至少在击键时)

2) If the component never rerenders on key stroke, then React keeps an outdated virtual dom for this contenteditable.

3) If React keeps an outdated version of the contenteditable in its virtual dom tree, then if you try to reset the contenteditable to the value outdated in the virtual dom, then during the virtual dom diff, React will compute that there are no changes to apply to the DOM!

This happens mostly when:

  • you have an empty contenteditable initially (shouldComponentUpdate=true,prop="",previous vdom=N/A),
  • the user types some text and you prevent renderings (shouldComponentUpdate=false,prop=text,previous vdom="")
  • after user clicks a validation button, you want to empty that field (shouldComponentUpdate=false,prop="",previous vdom="")
  • as both the newly produced and old vdom are "", React does not touch the dom.