如何监听基于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);
答案 0 :(得分:65)
修改:请参阅Sebastien Lorber's answer修复我的实施中的错误。
使用onInput事件,并可选择使用onBlur作为后备。您可能希望保存以前的内容以防止发送额外的事件。
我个人认为这是我的渲染功能。
var handleChange = function(event){
this.setState({html: event.target.value});
}.bind(this);
return (<ContentEditable html={this.state.html} onChange={handleChange} />);
在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;
}
});
答案 1 :(得分:60)
编辑2015
有人用我的解决方案在NPM上制作了一个项目:https://github.com/lovasoa/react-contenteditable
编辑06/2016:我刚刚遇到了一个新问题,当浏览器尝试“重新格式化”您刚给他的html时,会出现组件始终重新渲染的问题。 See
编辑07/2016:这是我的生产内容可编辑的实现。除react-contenteditable
之外,它还有一些您可能需要的其他选项,包括:
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;
}
});
Virtual dom保持过时,它可能不是最有效的代码,但至少它确实有效:) My bug is resolved
<强>详细信息:强>
1)如果你把shouldComponentUpdate放在以避免插入符号跳转,那么contenteditable永远不会重新渲染(至少在击键时)
2)如果组件永远不会在击键时重新渲染,那么React会保留一个过时的虚拟dom。
3)如果React在其虚拟dom树中保留了一个过时的contenteditable版本,那么如果你试图将contenteditable重置为虚拟dom中过时的值,那么在虚拟dom diff期间,React会计算出没有更改适用于DOM!
这主要发生在:
答案 2 :(得分:14)
这可能并不是你正在寻找的答案,但是我自己也在努力解决这个问题并且遇到建议的答案,我决定让它不受控制。
当editable
道具为false
时,我按原样使用text
道具,但是当true
时,我切换到编辑模式text
没有效果(但至少浏览器并没有吓坏)。在此期间,控件会触发onChange
。最后,当我将editable
更改回false
时,它会使用text
中传递的内容填充HTML:
/** @jsx React.DOM */
'use strict';
var React = require('react'),
escapeTextForBrowser = require('react/lib/escapeTextForBrowser'),
{ PropTypes } = React;
var UncontrolledContentEditable = React.createClass({
propTypes: {
component: PropTypes.func,
onChange: PropTypes.func.isRequired,
text: PropTypes.string,
placeholder: PropTypes.string,
editable: PropTypes.bool
},
getDefaultProps() {
return {
component: React.DOM.div,
editable: false
};
},
getInitialState() {
return {
initialText: this.props.text
};
},
componentWillReceiveProps(nextProps) {
if (nextProps.editable && !this.props.editable) {
this.setState({
initialText: nextProps.text
});
}
},
componentWillUpdate(nextProps) {
if (!nextProps.editable && this.props.editable) {
this.getDOMNode().innerHTML = escapeTextForBrowser(this.state.initialText);
}
},
render() {
var html = escapeTextForBrowser(this.props.editable ?
this.state.initialText :
this.props.text
);
return (
<this.props.component onInput={this.handleChange}
onBlur={this.handleChange}
contentEditable={this.props.editable}
dangerouslySetInnerHTML={{__html: html}} />
);
},
handleChange(e) {
if (!e.target.textContent.trim().length) {
e.target.innerHTML = '';
}
this.props.onChange(e);
}
});
module.exports = UncontrolledContentEditable;
答案 3 :(得分:4)
我建议使用mutationObserver来执行此操作。它可以让您更好地控制正在发生的事情。它还为您提供有关浏览如何解释所有击键的更多详细信息
这里是TypeScript
import * as React from 'react';
export default class Editor extends React.Component {
private _root: HTMLDivElement; // Ref to the editable div
private _mutationObserver: MutationObserver; // Modifications observer
private _innerTextBuffer: string; // Stores the last printed value
public componentDidMount() {
this._root.contentEditable = "true";
this._mutationObserver = new MutationObserver(this.onContentChange);
this._mutationObserver.observe(this._root, {
childList: true, // To check for new lines
subtree: true, // To check for nested elements
characterData: true // To check for text modifications
});
}
public render() {
return (
<div ref={this.onRootRef}>
Modify the text here ...
</div>
);
}
private onContentChange: MutationCallback = (mutations: MutationRecord[]) => {
mutations.forEach(() => {
// Get the text from the editable div
// (Use innerHTML to get the HTML)
const {innerText} = this._root;
// Content changed will be triggered several times for one key stroke
if (!this._innerTextBuffer || this._innerTextBuffer !== innerText) {
console.log(innerText); // Call this.setState or this.props.onChange here
this._innerTextBuffer = innerText;
}
});
}
private onRootRef = (elt: HTMLDivElement) => {
this._root = elt;
}
}
答案 4 :(得分:2)
以下是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;
}
我成功使用了类似的方法
答案 5 :(得分:1)
这是最适合我的解决方案。
<div
contentEditable='true'
onInput = {e => {console.log('text of div', e.currentTarget.textContent)}}
>
Text in div
</div>
答案 6 :(得分:1)
由于编辑完成后,总是会失去元素的焦点,因此您只需使用onBlur钩子即可。
<input type="text" pattern="\d{2}-\d{7}" />
答案 7 :(得分:-1)
如果您正在寻找一种简单易行的方法,那就是这里。
<div
contentEditable={true}
onSelect={() => callMeOnSelect()}
onChange={() => callMeOnChange()}
onMouseUp={() => callMeOnMouseUp()}
className="myClassNameHere"/>
如果您想在触发选择时获取所选文本,您可以这样做。
let selection = window.getSelection().toString();