javascript - React.js: onChange event for contentEditable
dom reactjs (4)
كيف أستمع لتغيير الحدث من أجل التحكم في 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/
https://code.i-harness.com
أقترح استخدام 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;
}
}
ربما هذا ليس بالضبط الإجابة التي تبحث عنها ، ولكن بعد أن جاهدت مع نفسي وأواجه مشاكل مع الإجابات المقترحة ، قررت أن تجعلها غير خاضعة للرقابة بدلاً من ذلك.
عندما يكون البرنامج editable
false
، أستخدم text
كما هو ، ولكن عندما يكون true
، أقوم بالتبديل إلى وضع التحرير الذي لا يكون text
تأثير فيه (ولكن على الأقل لا يفاجئ المتصفح). خلال هذا الوقت يتم تشغيل onChange
بواسطة عنصر التحكم. أخيرًا ، عندما أقوم بتغيير التغيير مرة أخرى إلى false
، فإنه يملأ HTML بكل ما تم تمريره في 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;
تحرير 2015
شخص ما قام بعمل مشروع على الآلية الوقائية الوطنية مع الحل الخاص بي: https://github.com/lovasoa/react-contenteditable
تعديل 06/2016: لقد قمت فقط بتثبيت مشكلة جديدة تحدث عندما يحاول المتصفح "إعادة تنسيق" html الذي قدمته له للتو ، مما يؤدي إلى إعادة تقديم العنصر دائمًا. See
تحرير 07/2016: هنا محتوى الإنتاج الخاص بيتنفيذ مناسب . لديها بعض الخيارات الإضافية على react-contenteditable
التي قد ترغب في react-contenteditable
، بما في ذلك:
- قفل
- واجهة برمجة التطبيقات الضرورية التي تسمح بتضمين أجزاء html
- القدرة على إعادة صياغة المحتوى
ملخص:
حل FakeRainBrigand عمل جيد جدا بالنسبة لي لبعض الوقت حتى حصلت على مشاكل جديدة. ContentEditables هي ألم ، وليس من السهل التعامل مع React ...
يوضح هذا JSFiddle المشكلة.
كما ترى ، عند كتابة بعض الأحرف والنقر على Clear
، لا يتم مسح المحتوى. هذا لأننا نحاول إعادة تعيين المحتوى القابل للاحتفال إلى آخر قيمة دوم افتراضية معروفة.
لذلك يبدو أن:
- أنت في حاجة
shouldComponentUpdate
لمنع قفزات موقف الإقحام - لا يمكنك الاعتماد على خوارزمية
shouldComponentUpdate
حالة استخدامshouldComponentUpdate
بهذه الطريقة.
لذلك تحتاج إلى خط إضافي بحيث كلما shouldComponentUpdate
إرجاع "نعم" ، فأنت متأكد من تحديث محتوى DOM فعليًا.
لذلك يضيف الإصدار هنا componentDidUpdate
DD تحديثًا ويصبح:
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;
}
});
يبقى دوم الافتراضي عفا عليه الزمن ، وربما لا يكون رمز الأكثر كفاءة ، ولكن على الأقل أنه لا يعمل :) يتم حل بلدي علة
تفاصيل:
1) إذا قمت بوضع shouldComponentUpdate لتجنب إقحام علامة الإقحام ، فإن المحتالين لا يقبلون المحتوى (على الأقل على ضغطات المفاتيح)
2) إذا لم يعثر المكون مطلقًا على ضربة المفتاح ، فعندئذٍ يحتفظ React بقاعدة افتراضية قديمة لهذا المحتوى القابل للرضا.
3) إذا كان React يحتفظ بنسخة قديمة من المحتوى القابل للامتثال في شجرة DOM الافتراضية الخاصة به ، فعندئذ إذا حاولت إعادة تعيين المحتوى القابل للامتثال إلى القيمة التي عفا عليها الزمن في DOM الافتراضية ، ثم خلال فرق dom الظاهري ، فإن React سيحسب أنه لا توجد تغييرات على تنطبق على DOM!
يحدث هذا في الغالب عندما:
- لديك في البداية محتوى مضمون فارغ (shouldComponentUpdate = true ، prop = "" ، vdom السابق = N / A) ،
- يكتب المستخدم بعض النصوص ويمنع الاداءات (shouldComponentUpdate = false ، prop = text ، vdom = "" السابق)
- بعد قيام المستخدم بالنقر فوق زر التحقق من الصحة ، فأنت تريد إفراغ هذا الحقل (shouldComponentUpdate = false ، prop = "" ، vdom = "" السابق)
- كما أن كلا من vdom المنتجة حديثا والقديمة هي "" ، رد فعل لا تلمس dom.
تعديل: راجع إجابة سيباستيان لوربر التي تعمل على إصلاح خطأ في التنفيذ.
استخدم الحدث onInput ، واختياريا على سبيل المثال كخلفية احتياطية. قد ترغب في حفظ المحتويات السابقة لمنع إرسال أحداث إضافية.
كنت شخصيا لدي هذا بمثابة وظيفة التقديم.
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;
}
});