Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ContentEditable element and caret position jumps #2047

Closed
slorber opened this issue Aug 15, 2014 · 12 comments
Closed

ContentEditable element and caret position jumps #2047

slorber opened this issue Aug 15, 2014 · 12 comments

Comments

@slorber
Copy link
Contributor

@slorber slorber commented Aug 15, 2014

Hi.

I don't think if this is really a bug nor if this can be fixed easily but this is an annoying behavior.

See http://jsfiddle.net/kb3gN/4603/
The cursor jumps unexpectedly as the contenteditable element is rerendered.

Does this mean that we should always use contentEditable elements as uncontrolled components?

@cody

This comment has been minimized.

Copy link
Contributor

@cody cody commented Aug 15, 2014

It works if the event handler reads the value like this:

emitChange: function(){
  var html = this.target.value;
  this.setState({html: html})
},
@slorber

This comment has been minimized.

Copy link
Contributor Author

@slorber slorber commented Aug 15, 2014

hmmm yes it seems so !!!

can you explain what's the difference?

@slorber

This comment has been minimized.

Copy link
Contributor Author

@slorber slorber commented Aug 15, 2014

Actually it works because of the error in my console Uncaught TypeError: Cannot read property 'value' of undefined. The rerendering is never triggered so it's normal the caret does not jump, but it does not work actually because I need to update the state

@syranide

This comment has been minimized.

Copy link
Contributor

@syranide syranide commented Aug 15, 2014

The caret doesn't jump when you update value (to the new value) because it has the HAS_SIDE_EFFECTS flag in the React, so React polls the DOM before updating the value. This prevents the caret from jumping.

Polling innerHTML would not make much practical sense at all I think, it could be prohibitively expensive and for most cases you wouldn't want it. I think there's some experimentation going on with controlled content-editables, but uncontrolled is the only sane way for now AFAIK.

@cody

This comment has been minimized.

Copy link
Contributor

@cody cody commented Aug 15, 2014

Ah, yes, my answer was wrong.
The only idea I have now is to change shouldComponentUpdate like this:

shouldComponentUpdate: function(nextProps, nextState){
  return nextState.html !== this.getDOMNode().innerHTML;
}

Then the cursor does not jump when the user types, But it still jumps when the change comes from the script.
It would be interesting to know if this could be done in a more controlled way.

@syranide

This comment has been minimized.

Copy link
Contributor

@syranide syranide commented Aug 15, 2014

@cody Updating from script inherently resets the cursor, there's no way for the browser to know where the cursor is in the new content. You need to get the cursor position before setting and restore it after.

PS. Also beware of tons of undo bugs when you set from script.

@syranide

This comment has been minimized.

Copy link
Contributor

@syranide syranide commented Sep 29, 2014

Realistically I don't think there's anything React can/should do here, closing as wontfix, reopen if you disagree.

@syranide syranide closed this Sep 29, 2014
@slorber

This comment has been minimized.

Copy link
Contributor Author

@slorber slorber commented Dec 2, 2014

@syranide I think it would be nice if React team did document good practices about dealing with contenteditables as it is quite hard to manage them well.

I think I've found a way to deal with them:
http://stackoverflow.com/questions/22677931/react-js-onchange-event-for-contenteditable/27255103#27255103

@cody if you want to manipulate the contenteditable from a script without jumping cursors, the best you can do is to update the DOM directly (I do this to "sanitize" rich pasted content). The cursor does not jump if you do not touch the nodes of the selection (I think, works for me)

@abritinthebay

This comment has been minimized.

Copy link

@abritinthebay abritinthebay commented Sep 29, 2015

I know this is an old closed issue but I wanted to chime in on a simple solution for people that run into it.

This only happens when you mutate the value so the DOM representation is different to a state value that's being set.

Simple solution - add a ref, mutate the ref's value to the manipulated one, then update the state. It's not quite as clean but it solves all the problems.

@slorber

This comment has been minimized.

Copy link
Contributor Author

@slorber slorber commented Oct 7, 2015

@abritinthebay yes this looks like what is proposed in the linked StackOverflow question and this project: https://github.com/lovasoa/react-contenteditable

@clintharris

This comment has been minimized.

Copy link

@clintharris clintharris commented May 3, 2017

For anyone else who, like me, ends up looking at this ticket because they think they need to roll their own contentEditable for a fancier text input: make sure to check out Facebook's DraftJS (and the 3rd-party DraftJS Plugins project)--might save you a lot of time.

@arifhussain353

This comment has been minimized.

Copy link

@arifhussain353 arifhussain353 commented Jan 7, 2019

@clintharris Can we implement this in Vue?. If not then is there any way to add a single component of React inside Vue app?. Sound insane but I am also facing the same issue for 2 weeks. I am slowly giving up.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
6 participants
You can’t perform that action at this time.