trigger react javascript dom contenteditable reactjs

javascript - trigger - Evento React.js: onChange para contentEditable



react contenteditable (5)

Aquí hay un componente que incorpora mucho de esto por lovasoa: https://github.com/lovasoa/react-contenteditable/blob/master/index.js

Él calza el evento en el 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; }

Estoy usando un enfoque similar con éxito

¿Cómo escucho para cambiar el evento por el control basado en el contenido?

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/


Probablemente esta no sea exactamente la respuesta que estás buscando, pero habiendo luchado con esto yo mismo y teniendo problemas con las respuestas sugeridas, decidí hacerlo sin control.

Cuando editable prop es false , uso text Prop como está, pero cuando es true , cambio al modo de edición en el que el text no tiene ningún efecto (pero al menos el navegador no se vuelve loco). Durante este tiempo, onChange se dispara por el control. Finalmente, cuando cambio editable nuevo a false , rellena HTML con lo que pasó en el 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;


Sugiero usar un mutationObserver para hacer esto. Te da mucho más control sobre lo que está sucediendo. También le brinda más detalles sobre cómo el navegador interpreta todas las pulsaciones de teclas

Aquí en 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; } }


Editar 2015

Alguien ha realizado un proyecto sobre NPM con mi solución: https://github.com/lovasoa/react-contenteditable

Editar 06/2016: Acabo de encontrar un nuevo problema que ocurre cuando el navegador intenta "formatear" el html que acaba de darle, lo que hace que el componente siempre vuelva a generar. See

Edición 07/2016: aquí está mi contenido de producción. Implementación editable. Tiene algunas opciones adicionales sobre react-contenteditable que pueda desear, incluyendo:

  • cierre
  • API imperativa que permite incrustar fragmentos html
  • capacidad de reformatear el contenido

Resumen:

La solución de FakeRainBrigand me ha funcionado bastante bien hasta que tuve nuevos problemas. ContentEditables son un dolor, y no son realmente fáciles de tratar con Reaccionar ...

Este JSFiddle demuestra el problema.

Como puede ver, cuando escribe algunos caracteres y hace clic en Clear , el contenido no se borra. Esto se debe a que tratamos de restablecer el valor dom virtual satisfecho con el último valor virtual conocido.

Entonces parece que:

  • Necesitas shouldComponentUpdate para evitar saltos en la posición de shouldComponentUpdate
  • No puede confiar en el algoritmo de diferenciación VDOM de React si usa shouldComponentUpdate esta manera.

Por lo tanto, necesita una línea adicional para que siempre que shouldComponentUpdate devuelva yes, esté seguro de que el contenido de DOM se haya actualizado realmente.

Entonces, la versión aquí agrega un componentDidUpdate y se convierte en:

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

El Dom virtual permanece obsoleto, y puede que no sea el código más eficiente, pero al menos funciona :) Mi error está resuelto

Detalles:

1) Si pones shouldComponentUpdate para evitar saltos de caret, entonces el contenteditable nunca vuelve a soltar (al menos en las teclas)

2) Si el componente nunca se detiene en la pulsación de tecla, entonces React mantiene una dom virtual virtual obsoleta para este contenido.

3) Si React mantiene una versión desactualizada de contenteditable en su árbol dom virtual, entonces si intenta restablecer el contenteditable al valor desactualizado en el dom virtual, entonces durante el dom diff virtual, React calculará que no hay cambios en aplicar al DOM!

Esto sucede principalmente cuando:

  • tienes un contenteditable vacío inicialmente (shouldComponentUpdate = true, prop = "", vdom anterior = N / A),
  • el usuario escribe texto y previene las representaciones (shouldComponentUpdate = false, prop = text, vdom = "" anterior)
  • después de que el usuario haga clic en un botón de validación, quiere vaciar ese campo (shouldComponentUpdate = false, prop = "", vdom previo = "")
  • ya que tanto el vodium recién producido como el viejo son "", React no toca el dom.

Editar: Vea la respuesta de Sebastien Lorber que soluciona un error en mi implementación.

Use el evento onInput y, opcionalmente, onBlur como alternativa. Es posible que desee guardar los contenidos anteriores para evitar el envío de eventos adicionales.

Personalmente tendré esto como mi función de renderizado.

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

jsbin

Que usa esta envoltura simple alrededor de contenido Editable.

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