4

I'm trying to populate a Draft.js 0.10.0 editor with some HTML content when it initializes. The problem is that any HTML block elements that don't have text in them don't get converted to ContentBlocks. So all of the extra spacing from line breaks is removed.

I'd expect the code below (or in jsfiddle) to have some empty ContentBlocks but there are none. Only the elements with text in them get a ContentBlock. It acts the same in Draft 0.9.1 as seen in this jsfiddle so I must be doing something wrong. The HTML can vary but I do have access to it if it needs to be manipulated.

Anyone know how to get empty lines / content blocks to appear with convertFromHTML?

const theHTML =
  '<div>first line</div>' +
  '<div></div>' +
  '<p></p>' +
  '<br />' +
  '<div>&nbsp;</div>' +
  '<p>sixth line</p>' +
  '<div>seventh line</div>';

const {
  Editor,
  EditorState,
  ContentState,
  convertFromHTML
} = Draft;

class Container extends React.Component {
  constructor(props) {
    super(props);
    const blocksFromHTML = convertFromHTML(theHTML);
    const contentState = ContentState.createFromBlockArray(
      blocksFromHTML.contentBlocks,
      blocksFromHTML.entityMap
    );
    this.state = {
      editorState: EditorState.createWithContent(contentState)
    };
  }
  render() {
    return (
      <div className = "container-root" >
      <Editor
        editorState = { this.state.editorState }
        onChange = { this._handleChange } />
      </div>
    );
  }
  _handleChange = (editorState) => {
    this.setState({
      editorState
    });
  }
}

ReactDOM.render( <Container /> , document.getElementById('react-root'))
body {
  font-family: Helvetica, sans-serif;
}

.container-root {
  border: 1px solid black;
  padding: 5px;
  margin: 5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/immutable/3.8.1/immutable.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/draft-js/0.10.0/Draft.js"></script>

<div id="react-root"></div>
Uwe Keim
  • 39,551
  • 56
  • 175
  • 291
Paul
  • 1,907
  • 2
  • 21
  • 29

4 Answers4

3

For me draft-js-import-html solved the problem. The stateFromHTML function converts the html to contentState and keeps empty tags.

Instad of this:

const blocksFromHTML = convertFromHTML(theHTML);
const contentState = ContentState.createFromBlockArray(
  blocksFromHTML.contentBlocks,
  blocksFromHTML.entityMap
);

Simply use this:

 const contentState = stateFromHTML(theHTML);
Dharman
  • 30,962
  • 25
  • 85
  • 135
szilagyi.sandor
  • 204
  • 1
  • 6
0

No way.

convertFromHTMLToContentBlocks.js:

  // Kill whitespace after blocks
  if (
    lastInA === '\r'
  ) {
    if (B.text === SPACE || B.text === '\n') {
      return A;
    }

To keep empty blocks you will need to use your own implementation of convertFromHTML() or fork draft-js and modify this file.

quotesBro
  • 6,030
  • 2
  • 31
  • 41
  • this just kill WS in TEXT, but why `

    ` not generate a empty block
    – Jiang YD Mar 20 '17 at 02:31
  • `

    ` generates an empty chunk, and then it gets removed.
    – quotesBro Mar 20 '17 at 08:30
  • Thanks for the tip @Mikhail. I've been trying to modify that `joinChunks()` method you linked to. I can comment out parts of it but then then I end up with a bunch of extra blocks that aren't in the HTML. If you have any guidance on how to modify that file we'd all appreciate it. – Paul Mar 23 '17 at 21:39
0

I was able to resolve this by appending a newline character before saving the content to the back-end. Then when it is parsed back to the editor, the empty block will contain the line-breaks.

const handleSave = (data: string) => {
    const rawContent: RawDraftContentState = JSON.parse(data);
    // If we have an empty paragraph block, append a newline character so it parses properly
    const newContent = {
      ...rawContent,
      blocks: rawContent.blocks.map(block => {
        if (block.text === "") {
          return {
            ...block,
            text: "\n"
          };
        }
        return block;
      })
    };

    const contentState = convertFromRaw(newContent);
    onSave(contentState)
  };
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Nov 29 '21 at 21:28
0

To retain empty <p><br></p> tags there are two things you can do

  1. import {stateFromHTML} from 'draft-js-import-html'; let contentState = stateFromHTML(html); which will surely to solve your problem

  2. for some reason if you are not able to import(peerDependency conflict) you simply convert all <p><br></p> to <div>&shy;</div> ex: const updatedContent = content.replace(/<p><br><\/p>/g, '<div>&shy;</div>');

then you can use updatedContent in convertFromHTML function

the con with 2nd approach is that it will not keep your editor state empty so any placeholders in the editor wont work.