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;
    }
});




reactjs