How do I listen to change event for contentEditable
-based control?
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);
Edit 2015
Someone has made a project on NPM with my solution: https://github.com/lovasoa/react-contenteditable
Edit 06/2016: I've just encoutered a new problem that occurs when the browser tries to "reformat" the html you just gave him, leading to component always re-rendering. See
Edit 07/2016: here's my production contentEditable implementation. It has some additional options over
react-contenteditable
that you might want, including:Summary:
FakeRainBrigand's solution has worked quite fine for me for some time until I got new problems. ContentEditables are a pain, and are not really easy to deal with React...
This JSFiddle demonstrates the problem.
As you can see, when you type some characters and click on
Clear
, the content is not cleared. This is because we try to reset the contenteditable to the last known virtual dom value.So it seems that:
shouldComponentUpdate
to prevent caret position jumpsshouldComponentUpdate
this way.So you need an extra line so that whenever
shouldComponentUpdate
returns yes, you are sure the DOM content is actually updated.So the version here adds a
componentDidUpdate
and becomes:The Virtual dom stays outdated, and it may not be the most efficient code, but at least it does work :) My bug is resolved
Details:
1) If you put shouldComponentUpdate to avoid caret jumps, then the contenteditable never rerenders (at least on keystrokes)
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:
Edit: See Sebastien Lorber's answer which fixes a bug in my implementation.
Use the onInput event, and optionally onBlur as a fallback. You might want to save the previous contents to prevent sending extra events.
I'd personally have this as my render function.
jsbin
Which uses this simple wrapper around contentEditable.
Since when the edit is complete the focus from the element is always lost you could simply use the onBlur hook.
This probably isn't exactly the answer you're looking for, but having struggled with this myself and having issues with suggested answers, I decided to make it uncontrolled instead.
When
editable
prop isfalse
, I usetext
prop as is, but when it istrue
, I switch to editing mode in whichtext
has no effect (but at least browser doesn't freak out). During this timeonChange
are fired by the control. Finally, when I changeeditable
back tofalse
, it fills HTML with whatever was passed intext
:This is the is simplest solution that worked for me.
I suggest using a mutationObserver to do this. It gives you a lot more control over what is going on. It also gives you more details on how the browse interprets all the keystrokes
Here in TypeScript