1

JS Bin Available here

I have a component called ComponentB for which I want to get key down events:

var ComponentB = React.createClass({

  render: function() {
    return (
       <div 
           contentEditable 
           onKeyDown={this.handleKeyDown}>{this.props.data}</div>
    );
  },
  handleKeyDown: function(e) {
     console.log("B: " + e.type +"-" + e.which);
  }
});

I can get those events if this ComponentB is directly under the main/App component.

If I try to embed this component inside another component (componentA) I don't receive those events anymore (this.props.lines is an array of 3 strings) :

var ComponentA = React.createClass({
    render: function(){
        return (
          <div
            contentEditable>
            { 
              this.props.lines.map(function(line,i) {
                return <ComponentB key={i} data={line} />  
              })
            }             
          </div>
          );
    }
});

The associated JS BIN shows this behavior : if you try to edit the lines in the component A section. no event will be emitted but they will if you edit the sinbgle instance of componentB below...

Looks to me like a bug in react.js but wanted to check here first.

Pierre
  • 58
  • 1
  • 5
  • 1
    It seems to be a problem with nested contentEditable elements. Remove `contentEditable` from ComponentA, and it works as you expect. – Ross Allen Jul 24 '14 at 05:42

1 Answers1

6

As @ssorallen said in a comment, you can't have nested contentEditable elements, with or without react.

It seems to be a problem with nested contentEditable elements. Remove contentEditable from ComponentA, and it works as you expect.

One of the reasons this doesn't work, is because React doesn't really support contentEditable. Basically it sets the attribute, but it can't sensibly render into the contentEditable because when it tries to update it... the DOM has changed without its knowledge (which throws an error).

Rather you should treat contentEditable elements like an input element, which doesn't have children. Instead you update the innerHTML (see renderComponentToString and dangerouslySetInnerHTML) and listen to onInput events to get changes. See Stack Overflow: onChange event for contentEditable for details on how this works.

A proper handling of contentEditable has been discussed briefly, but no solution was arrived at. Feel free to suggest good ways to handle it.

Community
  • 1
  • 1
Brigand
  • 84,529
  • 20
  • 165
  • 173
  • Indeed, if I remove contentEditable from componentA I get the keyboard event notifications from componentB. The unfortunate side effect, is that the arrow navigation doesn't work after this change: the arrows left and down don't automatically move between the different divs - this makes sense as ComponentA is no more editable. – Pierre Jul 24 '14 at 14:19