React.js: onChange événement pour contentEditable

Comment puis-je écouter l'événement de modification pour le contrôle basé sur le 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/

Edit: Voir la réponse de Sebastien Lorber qui corrige un bug dans ma mise en œuvre.


Utilisez l'événement onInput, et éventuellement onBlur comme un repli. Vous voudrez peut-être enregistrer les contenus précédents pour éviter d'envoyer des événements supplémentaires.

J'aurais personnellement cette fonction de rendu.

 var handleChange = function(event){ this.setState({html: event.target.value}); }.bind(this); return (<ContentEditable html={this.state.html} onChange={handleChange} />); 

Jsbin

Qui utilise ce wrapper simple sur le contenuEditable.

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

Modifier 2015

Quelqu'un a réalisé un projet sur NPM avec ma solution: https://github.com/lovasoa/react-conteneditable

Edit 06/2016: Je viens de créer un nouveau problème qui se produit lorsque le navigateur essaie de "reformater" le html que vous venez de lui donner, ce qui a amené le composant à redémarrer toujours. Voir

Edit 07/2016: voici mon contenu de productionEdition rendue possible. Il existe des options supplémentaires sur les react-contenteditable que vous pourriez vouloir, y compris:

  • verrouillage
  • API impérative permettant d'intégrer des fragments html
  • Capacité à reformater le contenu

Résumé:

La solution de FakeRainBrigand m'a bien fonctionné pendant un certain temps jusqu'à ce que j'aie eu de nouveaux problèmes. ContentEditables est une douleur et ne sont pas vraiment faciles à traiter avec React …

Ce jSFiddle démontre le problème.

Comme vous pouvez le voir, lorsque vous tapez certains caractères et cliquez sur Clear , le contenu n'est pas effacé. C'est parce que nous essayons de réinitialiser le contentement à la dernière valeur de dom virtuel connue.

Il semble donc que:

  • Vous devez shouldComponentUpdate pour éviter les sauts de position du caret
  • Vous ne pouvez pas compter sur l'algorithme différent VDOM de React si vous utilisez shouldComponentUpdate cette façon.

Donc, vous avez besoin d'une ligne supplémentaire pour que chaque fois que shouldComponentUpdate renvoie oui, vous êtes sûr que le contenu DOM est actuellement mis à jour.

La version ici ajoute un componentDidUpdate et devient:

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

Le domaine virtuel reste obsolète, et ce n'est peut-être pas le code le plus efficace, mais au moins ça fonctionne 🙂 Mon bug est résolu


Détails:

1) Si vous mettez shouldComponentUpdate pour éviter les sauts de caret, alors le contenteditable ne rende jamais (au moins sur les frappes de touche)

2) Si le composant ne rende jamais sur le coup de touche, React conserve un dom virtuel obsolète pour ce contourné.

3) Si React conserve une version désuète de contenteditable dans son arborescence dom virtuelle, alors si vous essayez de réinitialiser le contenteditable à la valeur dépassée dans le dom virtuel, alors, pendant la diff de dom virtuel, React calculera qu'il n'y a pas de modifications à Postuler au DOM!

Cela se produit surtout lorsque:

  • Vous avez initialement un contente content (shouldComponentUpdate = true, prop = "", vdom précédent = N / A),
  • L'utilisateur tape un certain texte et vous prévoyez des rendu (shouldComponentUpdate = false, prop = text, vdom précédent "" ")
  • Une fois que l'utilisateur clique sur un bouton de validation, vous souhaitez vider ce champ (shouldComponentUpdate = false, prop = "", précédent vdom = "")
  • Car le Vdom nouvellement produit et le vieux sont "", React ne touche pas le dom.

Ce n'est probablement pas exactement la réponse que vous recherchez, mais après avoir lutté avec moi-même et avoir des problèmes avec des réponses suggérées, j'ai décidé de le déconcerter à la place.

Lorsque l' editable est false , j'utilise le support de text tel quel, mais quand il est true , je passe au mode d'édition dans lequel le text n'a aucun effet (mais au moins le navigateur n'est pas effrayant). Pendant ce temps, les onChange sont déclenchés par le contrôle. Enfin, lorsque je modifie les editable en false , il remplit HTML avec ce qui a été transmis dans le 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; 

Voici un composant qui intègre beaucoup de ceci par lovasoa: https://github.com/lovasoa/react-contenteditable/blob/master/index.js

Il réduit l'événement dans l'émissionChange

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

J'utilise une approche similaire avec succès