When pasting rich text into Chrome it adds a space on either side of the string (first screenshot). When pasting in any other browser (Safari or Firefox) it behaves fine (second screenshot). It seems that Chrome is using the HTML text and the others are using plain text.
Is there a way to get Chrome to trim the string on paste? Tried using handlePastedText but I'm not sure how to update the state on firing.
Chrome:
Safari: 
I can confirm this.
not sure why this would happen, but this code should work to trim the trailing whitespace assuming your change handler is called onChange:
handlePastedText(text) {
const {editorState} = this.state;
const blockMap = ContentState.createFromText(text.trim()).blockMap;
const newState = Modifier.replaceWithFragment(editorState.getCurrentContent(), editorState.getSelection(), blockMap);
this.onChange(EditorState.push(editorState, newState, 'insert-fragment'));
return true;
},
@aem Thanks, your code block fixed the issue. Should I leave this issue open as it's a problem with the DraftJS framework and not just my code or just close it?
@maxmatthews i'm not really a code contributor on this project so I can't say for sure, I would leave it open and wait until next week when the primary contributor returns from vacation (#355)
Thanks I'll wait until @hellendag returns.
What is the source application for the paste?
We've seen this issue as well, this time with Chrome on a Windows machine. Will try out the workaround though.
@aem, Thanks, the code works fine.
For those using version 0.9.0+ (per to https://github.com/facebook/draft-js/commit/e64c2c3f2a3654b925b18baff448100cfbbf3738#diff-3e4f7f7129a394d16c3592dd894fd31eL92):
this.state is undefined. It should be:
handlePastedText(text) {
const {editorState} = this; // this.state is the context
// ...
Or bind this via:
<Editor handlePastedText={this.handlePastedText.bind(this)} ... />
@spence it's been a couple of months since I've worked with Draft, so correct me if I'm wrong, but the location of editorState should be whatever the client wants it to be, no? you could keep the editor state in Redux state and then it would look like const {editorState} = this.props;. I'm guessing your specific use just assigns the new editor state in onChange to this.editorState = editorState instead of using setState or a Redux function
@aem I'm following the Overview page which uses this.setState({editorState}). I'm not doing anything special that I can see.
Since the referenced change (and without binding the handlePastedText call) I can't access the resulting component (TestEditor below). handlePastedText is called using the Editor as context.
Here's the full example with bind:
class TestEditor extends React.Component {
constructor(props) {
super(props);
const initialContent = ContentState.createFromText(props.inititalText);
const editorState = EditorState.createWithContent(initialContent);
this.state = {editorState: editorState};
this.onChange = (editorState) => this.setState({editorState});
}
handlePastedText(text) {
const {editorState} = this.state;
const blockMap = ContentState.createFromText(stripNewLines(text)).blockMap;
const newState = Modifier.replaceWithFragment(editorState.getCurrentContent(), editorState.getSelection(), blockMap);
this.onChange(EditorState.push(editorState, newState, 'insert-fragment'));
return true;
}
render() {
return (
<Editor editorState={this.state.editorState} onChange={this.onChange}
handlePastedText={this.handlePastedText.bind(this)} />
);
}
}
// ...
<TestEditor inititalText="Test" />
ES6 class members aren't auto-bound like they are in React.createClass, implementing members as arrow functions solves this problem:
class TestEditor extends React.Component {
constructor(props) {
super(props);
const initialContent = ContentState.createFromText(props.inititalText);
const editorState = EditorState.createWithContent(initialContent);
this.state = {editorState};
}
onChange = (editorState) => this.setState({editorState});
handlePastedText = (text) => {
const {editorState} = this.state;
const blockMap = ContentState.createFromText(stripNewLines(text)).blockMap;
const newState = Modifier.replaceWithFragment(editorState.getCurrentContent(), editorState.getSelection(), blockMap);
this.onChange(EditorState.push(editorState, newState, 'insert-fragment'));
return true;
};
render() {
return (
<Editor editorState={this.state.editorState} onChange={this.onChange}
handlePastedText={this.handlePastedText} />
);
}
}
So I'm having the same issue (windows chrome, works fine on mac chrome/ff windows ff), and unfortunately using the custom handlePastedText won't work with html like blah, it will just paste 'blah'.
I've done a little debugging and I'm noticing in the code here:
if (!isOldIE && document.implementation && document.implementation.createHTMLDocument) {
doc = document.implementation.createHTMLDocument('foo');
!doc.documentElement ? process.env.NODE_ENV !== 'production' ? invariant(false, 'Missing doc.documentElement') : invariant(false) : void 0;
doc.documentElement.innerHTML = html;
root = doc.getElementsByTagName('body')[0];
}
return root;
root.innerText is different on win/mac chrome.
For mac I see the following:

and for windows I'm seeing this:

I'm not familiar on why we are using createHTMLDocument nor am I really all that familiar with the document.implementation api. But I thought I'd pass this info along.
Thanks!
Hey folks - I can't repro this on the latest Chrome v68 on a Mac. I assume that it got fixed at some point – can anyone on this thread still repro this?
hey @niveditc I can still reproduce this on Windows chrome/ff. Mac Chrome/ff works fine for me.
Thanks @khawkinson. Still figuring out what the next steps will be for bugs like this one that only repro in certain environments, so just adding the appropriate tags for now.
How is this still a bug in 2019?
Windows + Chrome is among the most popular combinations of my users.
This is still an issue for me using Chrome on Mac and PC. I paste rich text and I have stripPastedStyles to false. All of the formatting is great, but I get an empty leading paragraph block and a trailing space. I tried the workaround with handlePastedText, but it doesn't work in my case because I very much need the rich text formatting. Any help would be greatly appreciated!
Hey @crispiestsquid, our team handled this issue by some ugly replaces within the handlePastedText Handler. Maybe it handles your Mac issue too?
Only the part that cleans up the inserted HTML:
/**
* When pasting text in some cases (e.g. from Word or another editor instance on Windows),
* an empty space and/or empty lines are inserted, because DraftJS doesn't handle newlines correctly in this instance.
* This workaround removes newlines before/after the main content to fix the behavior.
*/
const handleMicrosoftNewlines = (html: string): string => {
const microsoftCopyPasteStartFragment = /^<html[^]*<!--StartFragment-->[^]*?</;
const microsoftCopyPasteEndFragment = />[^<]*?<!--EndFragment-->[^]*$/;
const startFragment = microsoftCopyPasteStartFragment.exec(html);
const cleanedStartFragment = startFragment ? startFragment[0].replace(/>\s+</g, '><') : '';
const endFragment = microsoftCopyPasteEndFragment.exec(html);
const cleanedEndFragment = endFragment ? endFragment[0].replace(/>\s+</g, '><') : '';
return html
.replace(microsoftCopyPasteStartFragment, cleanedStartFragment)
.replace(microsoftCopyPasteEndFragment, cleanedEndFragment);
};
Hey @crispiestsquid, our team handled this issue by some ugly replaces within the handlePastedText Handler. Maybe it handles your Mac issue too?
Only the part cleans the pasted HTML:
/** * When pasting text in some cases (e.g. from Word or another editor instance on Windows), * an empty space and/or empty lines are inserted, because DraftJS doesn't handle newlines correctly in this instance. * This workaround removes newlines before/after the main content to fix the behavior. */ const handleMicrosoftNewlines = (html: string): string => { const microsoftCopyPasteStartFragment = /^<html[^]*<!--StartFragment-->[^]*?</; const microsoftCopyPasteEndFragment = />[^<]*?<!--EndFragment-->[^]*$/; const startFragment = microsoftCopyPasteStartFragment.exec(html); const cleanedStartFragment = startFragment ? startFragment[0].replace(/>\s+</g, '><') : ''; const endFragment = microsoftCopyPasteEndFragment.exec(html); const cleanedEndFragment = endFragment ? endFragment[0].replace(/>\s+</g, '><') : ''; return html .replace(microsoftCopyPasteStartFragment, cleanedStartFragment) .replace(microsoftCopyPasteEndFragment, cleanedEndFragment); };
@martinkutter Thanks for the response! So I am trying now to figure out how I should get this new clean HTML into the editor. The method that I am trying is inserting the HTML itself as the text, and not displaying the content as rich text like I need. What method should I be using to get the clean HTML into the editor?
@martinkutter Never mind. I actually discovered the solution on my own. I did use your function, so thank you very much again! Leaving my HandlePasted.js file for reference
```import { EditorState, ContentState, Modifier, convertFromHTML } from 'draft-js';
const HandlePasted = (state, onChange) => ({
handlePastedText(text, html) {
const { editorState } = state;
const blocksFromHTML = convertFromHTML(handleMicrosoftNewlines(html));
const newState = ContentState.createFromBlockArray(
blocksFromHTML.contentBlocks,
blocksFromHTML.entityMap,
);
const finalState = Modifier.replaceWithFragment(editorState.getCurrentContent(), editorState.getSelection(), newState.getBlockMap());
onChange(EditorState.push(editorState, finalState, 'insert-fragment'));
return 'handled';
}
});
const handleMicrosoftNewlines = html => {
const microsoftCopyPasteStartFragment = /^[^]?;
const microsoftCopyPasteEndFragment = />[^<]?[^]*$/;
const startFragment = microsoftCopyPasteStartFragment.exec(html);
const cleanedStartFragment = startFragment ? startFragment[0].replace(/>\s+</g, '><') : '';
const endFragment = microsoftCopyPasteEndFragment.exec(html);
const cleanedEndFragment = endFragment ? endFragment[0].replace(/>\s+</g, '><') : '';
return html
.replace(microsoftCopyPasteStartFragment, cleanedStartFragment)
.replace(microsoftCopyPasteEndFragment, cleanedEndFragment);
};
export default HandlePasted;
Most helpful comment
not sure why this would happen, but this code should work to trim the trailing whitespace assuming your change handler is called
onChange: