প্রতিক্রিয়া .js: বিষয়বস্তু জন্য ইভেন্ট পরিবর্তন করুন


120

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/


11
আমি নিজেই এটির সাথে লড়াই করেছি এবং প্রস্তাবিত উত্তরের সাথে সমস্যা নিয়েছি, পরিবর্তে আমি এটিকে অনিয়ন্ত্রিত করার সিদ্ধান্ত নিয়েছি। এটি হ'ল, আমি এটি initialValueinto ুকিয়ে দিয়েছি stateএবং এতে ব্যবহার করছি render, তবে আমি প্রতিক্রিয়াটিকে এটিকে আরও আপডেট করতে দিচ্ছি না।
ড্যান আব্রামভ

আপনার জেএসফিডাল কাজ করে না
সবুজ

আমি contentEditableআমার দৃষ্টিভঙ্গি পরিবর্তনের মাধ্যমে লড়াই এড়াতে পেরেছি - একটি spanবা এর পরিবর্তে paragraph, আমি inputএর readonlyবৈশিষ্ট্য সহ একটি ব্যবহার করেছি ।
ovidiu-miu

উত্তর:


79

সম্পাদনা: দেখুন সেবাস্টিয় Lorber এর উত্তর আমার বাস্তবায়নের একটি ত্রুটি সমাধান।


অন-ইনপুট ইভেন্টটি এবং বিকল্পভাবে অনব্লুরকে ফ্যালব্যাক হিসাবে ব্যবহার করুন। অতিরিক্ত ইভেন্ট পাঠানো রোধ করতে আপনি পূর্ববর্তী বিষয়বস্তুগুলি সংরক্ষণ করতে চাইতে পারেন।

আমার ব্যক্তিগতভাবে এটি আমার রেন্ডার ফাংশন হিসাবে থাকত।

var handleChange = function(event){
    this.setState({html: event.target.value});
}.bind(this);

return (<ContentEditable html={this.state.html} onChange={handleChange} />);

jsbin

যা কন্টেন্টএডেটেবলের চারপাশে এই সাধারণ মোড়কে ব্যবহার করে।

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
@ এনভিআই, এটি উচিত কম্পোনেন্ট আপডেট পদ্ধতি। এটি কেবলমাত্র লাফিয়ে উঠবে যদি এইচটিএমএল প্রপ উপাদানটির আসল এইচটিএমএলের সাথে সিঙ্কের বাইরে থাকে। যেমন আপনি যদি করেনthis.setState({html: "something not in the editable div"}})
ব্রিগেন্ড

1
চমৎকার কিন্তু আমি থেকে কল অনুমান this.getDOMNode().innerHTMLমধ্যে shouldComponentUpdateখুব ডান অপ্টিমাইজ করা নেই
সেবাস্টিয় Lorber

@ সেবাস্তিয়ানলবার খুব পছন্দসই নয় , তবে আমি নিশ্চিত যে এইচটিএমএল সেট করা না থেকে এটি পড়া ভাল। আমি কেবলমাত্র অন্য বিকল্পটি ভাবতে পারি হ'ল এইচটিএমএল পরিবর্তন করতে পারে এমন সমস্ত ইভেন্ট শোনার, এবং যখন সেগুলি ঘটে আপনি এইচটিএমএলকে ক্যাশে করেন। এটি সম্ভবত বেশিরভাগ সময়ে দ্রুততর হবে তবে অনেক জটিলতা যুক্ত করুন add এটি খুব নিশ্চিত এবং সহজ সমাধান।
ব্রিগেড

3
আপনি যখন state.htmlসর্বশেষ "জ্ঞাত" মান সেট করতে চান তখন এটি আসলে কিছুটা ত্রুটিযুক্ত হয়, প্রতিক্রিয়াটি ডমকে আপডেট করবে না কারণ নতুন এইচটিএমএল যেমন প্রতিক্রিয়া দেখায় ঠিক তেমনই হয় (যদিও সত্যিকারের ডিওএম ভিন্ন) is Jsfiddle দেখুন । আমি এর জন্য ভাল সমাধান খুঁজে পাইনি, সুতরাং যে কোনও ধারণা স্বাগত।
univerio

1
@ ডিস্টেস্ট shouldComponentUpdateখাঁটি হওয়া উচিত (পার্শ্ব প্রতিক্রিয়া নেই)।
ব্রিগেন্ড

66

2015 সম্পাদনা করুন

কেউ আমার সমাধান দিয়ে এনপিএম-তে একটি প্রকল্প তৈরি করেছেন: https://github.com/lovasoa/react-contentedable

06/2016 সম্পাদনা করুন: আমি ব্রাউজারটি কেবলমাত্র তাকে দেওয়া এইচটিএমএলকে "পুনরায় ফর্ম্যাট" করার চেষ্টা করে তখনই একটি নতুন সমস্যা হ'ল, যা উপাদানটিকে সর্বদা পুনরায় রেন্ডারিংয়ের দিকে নিয়ে যায়। দেখা

07/2016 সম্পাদনা করুন: এখানে আমার উত্পাদনের বিষয়বস্তু সম্পাদনযোগ্য বাস্তবায়ন। এটিতে কিছু অতিরিক্ত বিকল্প রয়েছে react-contenteditableযা আপনি চাইতে পারেন, সহ:

  • লকিং
  • আবশ্যকীয় API টি এইচটিএমএল টুকরো এম্বেড করার অনুমতি দেয়
  • সামগ্রী পুনরায় ফর্ম্যাট করার ক্ষমতা

সারসংক্ষেপ:

আমি নতুন সমস্যা না পাওয়া পর্যন্ত ফেকরাইনব্রিজ্যান্ডের সমাধান কিছু সময়ের জন্য আমার পক্ষে বেশ ভাল কাজ করেছে। কন্টেন্টএডেটিবগুলি একটি ব্যথা এবং প্রতিক্রিয়াটির সাথে মোকাবিলা করা সত্যিই সহজ নয় ...

এই জেএসফিডাল সমস্যাটি দেখায়।

আপনি দেখতে পাচ্ছেন, যখন আপনি কয়েকটি অক্ষর টাইপ করেন এবং ক্লিক করুন Clear , সামগ্রীটি সাফ হয় না। এটি কারণ আমরা সর্বশেষ জ্ঞাত ভার্চুয়াল ডোম মানকে সন্তুষ্ট করতে পুনরায় সেট করার চেষ্টা করি।

সুতরাং এটি মনে হয়:

  • আপনার shouldComponentUpdateক্যারেট অবস্থানের জাম্পগুলি প্রতিরোধ করা দরকার
  • আপনি যদি shouldComponentUpdateএইভাবে ব্যবহার করেন তবে আপনি প্রতিক্রিয়াটির ভিডিএএম ডিফিং অ্যালগরিদমের উপর নির্ভর করতে পারবেন না ।

সুতরাং আপনার একটি অতিরিক্ত লাইন প্রয়োজন যাতে যখনই shouldComponentUpdateহ্যাঁ ফিরে আসে, আপনি নিশ্চিত যে ডম সামগ্রীটি আসলে আপডেট হয়েছে।

সুতরাং এখানে সংস্করণটি যুক্ত করে 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;
    }
});

ভার্চুয়াল ডোমটি পুরানো থাকে এবং এটি সবচেয়ে কার্যকর কোড নাও হতে পারে তবে কমপক্ষে এটি কার্যকর হয় :) আমার বাগটি সমাধান হয়েছে


বিবরণ:

1) ক্যারেট জাম্প এড়ানোর জন্য আপনি যদি কম্পোনেন্টটি আপডেট করেন তবে কন্টেন্টটেটেবল কখনই রিরেন্ডার করে না (কমপক্ষে কীস্ট্রোকগুলিতে)

2) যদি উপাদানটি কখনই কী স্ট্রোকটিতে পুনরায় সরবরাহ করে না, তবে প্রতিক্রিয়াটি এই তর্কযোগ্যতার জন্য একটি পুরানো ভার্চুয়াল ডোম রাখে।

3) যদি প্রতিক্রিয়াটি ভার্চুয়াল ডোম গাছটিতে সন্তুষ্টির একটি পুরানো সংস্করণ রাখে, তবে আপনি যদি ভার্চুয়াল ডোমের পুরানো মানের সাথে কনসেন্টটেটেবলটিকে পুনরায় সেট করার চেষ্টা করেন, তবে ভার্চুয়াল ডোম ভিন্ন হওয়ার সময়, প্রতিক্রিয়াটি গণনা করবে যে এতে কোনও পরিবর্তন নেই ডিওএম প্রয়োগ!

এটি বেশিরভাগ ক্ষেত্রে ঘটে যখন:

  • আপনার প্রাথমিকভাবে একটি খালি কনটেস্টেবলযোগ্য রয়েছে (shouldCompenderUpdate = true, prop = "", পূর্ববর্তী vdom = N / A),
  • ব্যবহারকারী কিছু পাঠ্য টাইপ করেন এবং আপনি রেন্ডারিংগুলি প্রতিরোধ করেন (shouldCompenderUpdate = মিথ্যা, প্রোপ = পাঠ্য, পূর্ববর্তী vdom = "")
  • ব্যবহারকারী কোনও বৈধতা বোতাম ক্লিক করার পরে, আপনি সেই ক্ষেত্রটি খালি করতে চান (shouldCompenderUpdate = মিথ্যা, প্রোপ = "", পূর্ববর্তী vdom = "")
  • যেহেতু সদ্য উত্পাদিত এবং পুরাতন দু'টি ভোম "" রয়েছে, প্রতিক্রিয়া ডোমটিকে স্পর্শ করে না।

1
আমি কী কী চাপুন এমন সংস্করণটি প্রয়োগ করেছেন যা এন্টার কী টিপলে টেক্সটটি সতর্ক করে। jsfiddle.net/kb3gN/11378
লুকা

@ লুকাকা কলোনেল্লো আপনি আরও ভাল ব্যবহার করতে চাইবেন {...this.props}যাতে ক্লায়েন্ট বাইরে থেকে এই আচরণটি অনুকূলিত করতে পারেন
সেবাস্তিয়ান লোরবার

হ্যাঁ, এই ভাল! সত্যিই আমি এই সমাধানটি চেষ্টা করেছি কেবল কি-প্রেস ইভেন্টটি ডিভি-তে কাজ করছে কিনা তা যাচাই করার জন্য! স্পষ্টতার জন্য ধন্যবাদ
লুকা কর্নেলেলো

1
আপনি কী ব্যাখ্যা করতে পারেন যে shouldComponentUpdateকোড কীভাবে ক্যারেট জাম্পগুলি বাধা দেয়?
kmoe

1
@kmoe কারণ বিষয়বস্তুতে ইতিমধ্যে উপযুক্ত পাঠ্য থাকলে (যেমন কীস্ট্রোকের উপর) উপাদানটি কখনই আপডেট হয় না। প্রতিক্রিয়া সহ সামগ্রীটি আপডেট করা ক্যারেটকে লাফিয়ে তোলে। বিষয়বস্তু ছাড়া চেষ্টা করুন সম্পাদনযোগ্য এবং নিজেকে দেখুন;)
সেবাস্তিয়ান লরবার

28

এটি আমার পক্ষে কাজ করা সহজ সমাধান।

<div
  contentEditable='true'
  onInput={e => console.log('Text inside div', e.currentTarget.textContent)}
>
Text inside div
</div>

3
এটিকে কমানোর দরকার নেই, এটি কাজ করে! onInputউদাহরণ হিসাবে বর্ণিত হিসাবে ব্যবহার করতে মনে রাখবেন ।
সেবাস্তিয়ান থমাস

সুন্দর এবং পরিষ্কার, আমি আশা করি এটি অনেকগুলি ডিভাইস এবং ব্রাউজারগুলিতে কাজ করে!
জুলিয়েনরুইক্স

7
আমি প্রতিক্রিয়ার স্থিতি দিয়ে পাঠ্য আপডেট করার সময় এটি নিয়মিত পাঠ্যের শুরুতে সরিয়ে নিয়ে যায়।
জানতা

18

এটি সম্ভবত আপনি যে উত্তরটি সন্ধান করছেন ঠিক তা নয়, তবে আমি নিজেই এটির সাথে লড়াই করে এবং প্রস্তাবিত উত্তরগুলির সাথে সমস্যা পেয়েছি, পরিবর্তে আমি এটিকে অনিয়ন্ত্রিত করার সিদ্ধান্ত নিয়েছি।

editableপ্রপ যখন হয় তখন falseআমি প্রপটি ঠিক তেমনই ব্যবহার করি textতবে এটি যখন হয় তখন trueআমি সম্পাদনা মোডে স্যুইচ করি যার textকোনও প্রভাব নেই (তবে কমপক্ষে ব্রাউজারটি ফ্রিক আউট করে না)। এই সময়ে onChangeনিয়ন্ত্রণ দ্বারা বরখাস্ত করা হয়। অবশেষে, আমি যখন editableআবার ফিরে 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;

অন্যান্য উত্তরগুলির সাথে আপনার যে সমস্যাগুলি ছিল তা কি আপনি প্রসারিত করতে পারেন?
এনভিআই

1
@ এনভিআই: আমার ইনজেকশন থেকে সুরক্ষা প্রয়োজন, তাই এইচটিএমএলকে যেমন রাখা তেমন বিকল্প নয়। যদি আমি এইচটিএমএল না রাখি এবং টেক্সট কনটেন্টটি না ব্যবহার করি তবে আমি সমস্ত ধরণের ব্রাউজারের অসঙ্গতি পেয়েছি এবং shouldComponentUpdateএত সহজে প্রয়োগ করতে পারি না এমনকি এটি আমাকে আর ক্রেট জাম্প থেকে বাঁচায় না। অবশেষে, আমার কাছে সিএসএস সিউডো-এলিমেন্টের :empty:beforeস্থানধারক রয়েছে তবে shouldComponentUpdateব্যবহারকারীর দ্বারা সাফ হয়ে গেলে এই বাস্তবায়ন এফএফ এবং সাফারিটিকে ক্ষেত্রটি পরিষ্কার করতে বাধা দেয়। অনিয়ন্ত্রিত সিই সহ আমি এই সমস্ত সমস্যাগুলিকে পাশ কাটাতে পারি বুঝতে বুঝতে আমাকে 5 ঘন্টা সময় নিয়েছে।
ড্যান আব্রামভ 18

এটি কীভাবে কাজ করে তা আমি বেশ বুঝতে পারি না। আপনি কখনই পরিবর্তন করেন editableনা UncontrolledContentEditable। আপনি একটি চলমান উদাহরণ প্রদান করতে পারেন?
এনভিআই

@ এনভিআই: যেহেতু আমি এখানে একটি প্রতিক্রিয়া অভ্যন্তরীণ মডিউল ব্যবহার করছি এটি বেশ শক্ত ... মূলত আমি editableবাইরে থেকে সেট করেছি । এমন একটি ক্ষেত্রটি ভাবুন যা ব্যবহারকারী যখন "সম্পাদনা" টিপেন তখন ইনলাইন সম্পাদনা করা যায় এবং যখন ব্যবহারকারী "সংরক্ষণ করুন" বা "বাতিল করুন" টিপেন তখন কেবল পঠনযোগ্যভাবে হওয়া উচিত। সুতরাং যখন এটি কেবল পঠনযোগ্য হয়, আমি প্রপস ব্যবহার করি তবে আমি যখনই "সম্পাদনা মোড" এ প্রবেশ করি তখনই আমি তাদের দিকে তাকাতে থামি এবং আমি যখন প্রস্থান করি তখনই কেবল প্রপসের দিকে তাকাব।
ড্যান আব্রামভ 18

3
যার জন্য আপনি এই কোডটি ব্যবহার করতে যাচ্ছেন, প্রতিক্রিয়াটির নতুন নামকরণ করা escapeTextForBrowserহয়েছে escapeTextContentForBrowser
মুছে ফেলুন

8

যেহেতু সম্পাদনাটি সম্পূর্ণ হয়ে যায় উপাদান থেকে ফোকাসটি সর্বদা হারিয়ে যায় আপনি অনুল্লুক হুকটি কেবল ব্যবহার করতে পারবেন।

<div onBlur={(e)=>{console.log(e.currentTarget.textContent)}} contentEditable suppressContentEditableWarning={true}>
     <p>Lorem ipsum dolor.</p>
</div>

5

আমি এটি করার জন্য কোনও মিউটেশনঅবার্সার ব্যবহার করার পরামর্শ দিচ্ছি। এটি আপনাকে যা চলছে তার উপর অনেক বেশি নিয়ন্ত্রণ দেয়। এটি ব্রাউজ সমস্ত কীস্ট্রোককে কীভাবে ব্যাখ্যা করে সে সম্পর্কে আপনাকে আরও বিশদ দেয়

এখানে টাইপস্ক্রিপ্ট

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

2

এখানে এমন একটি উপাদান রয়েছে যা লোভাসোয়া দ্বারা এর বেশিরভাগ সংযুক্ত করে: https://github.com/lovasoa/react-contentedables/blob/master/index.js

তিনি ইমিট চেঞ্জে ইভেন্টটি শিহরিত করেন

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

আমি সফলভাবে একটি অনুরূপ পদ্ধতির ব্যবহার করছি


1
লেখক আমার এসও উত্তরটি প্যাকেজ.জসনে জমা করেছেন। এটি প্রায় একই কোড যা আমি পোস্ট করেছি এবং আমি নিশ্চিত করি যে এই কোডটি আমার পক্ষে কাজ করে। github.com/lovasoa/react-contenteditable/blob/master/…
সেবাস্তিয়ান লরবার
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.