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/
편집 : 내 구현의 버그를 수정하는 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;
}
});
2015 년 수정
누군가 내 솔루션으로 NPM에 프로젝트를 만들었습니다 : https://github.com/lovasoa/react-contenteditable
2016 년 6 월 편집 : 브라우저가 방금 제공 한 html을 "재 포맷"하려고 할 때 발생하는 새로운 문제를 해결하여 구성 요소가 항상 다시 렌더링됩니다. 보다
2016 년 7 월 편집 : 여기 내 프로덕션 콘텐츠 편집 가능한 구현입니다. 다음을 react-contenteditable
포함하여 원하는 추가 옵션 이 있습니다.
- 잠금
- html 조각을 포함 할 수있는 명령형 API
- 콘텐츠를 다시 포맷하는 기능
요약:
FakeRainBrigand의 솔루션은 새로운 문제가 발생할 때까지 한동안 꽤 잘 작동했습니다. ContentEditables는 고통스럽고 React를 다루기가 정말 쉽지 않습니다 ...
이 JSFiddle 은 문제를 보여줍니다.
보시다시피 일부 문자를 입력하고을 클릭 Clear
하면 내용이 지워지지 않습니다. 이는 contenteditable을 마지막으로 알려진 가상 DOM 값으로 재설정하려고하기 때문입니다.
따라서 다음과 같이 보입니다.
shouldComponentUpdate
캐럿 위치 점프를 방지 해야 합니다.shouldComponentUpdate
이런 식으로 사용하면 React의 VDOM diffing 알고리즘에 의존 할 수 없습니다 .
따라서 shouldComponentUpdate
예를 반환 할 때마다 DOM 콘텐츠가 실제로 업데이트되었는지 확인할 수 있도록 추가 줄이 필요합니다 .
따라서 여기 버전은 a를 추가하고 다음 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은 구식으로 유지되며 가장 효율적인 코드는 아니지만 적어도 작동합니다. :) 내 버그가 해결되었습니다.
세부:
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.
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 is false
, I use text
prop as is, but when it is true
, I switch to editing mode in which text
has no effect (but at least browser doesn't freak out). During this time onChange
are fired by the control. Finally, when I change editable
back to false
, it fills HTML with whatever was passed in text
:
/** @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;
This is the is simplest solution that worked for me.
<div
contentEditable='true'
onInput={e => console.log('Text inside div', e.currentTarget.textContent)}
>
Text inside div
</div>
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
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;
}
}
Here is a component that incorporates much of this by lovasoa: https://github.com/lovasoa/react-contenteditable/blob/master/index.js
He shims the event in the 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;
}
I'm using a similar approach successfully
Since when the edit is complete the focus from the element is always lost you could simply use the onBlur hook.
<div onBlur={(e)=>{console.log(e.currentTarget.textContent)}} contentEditable suppressContentEditableWarning={true}>
<p>Lorem ipsum dolor.</p>
</div>
참고URL : https://stackoverflow.com/questions/22677931/react-js-onchange-event-for-contenteditable
'developer tip' 카테고리의 다른 글
PHP 및 fgetcsv 함수를 사용하여 CSV 파일에서 배열을 만드는 방법 (0) | 2020.09.02 |
---|---|
Postgres 9.3에서 json 필드에 인덱스를 만드는 방법 (0) | 2020.09.01 |
pySerial 패키지 사용의 전체 예제 (0) | 2020.09.01 |
jQuery를 사용한 원형 차트 (0) | 2020.09.01 |
Python 용 시각적 프로파일 러가 있습니까? (0) | 2020.09.01 |