Skip to content
This repository has been archived by the owner on Feb 6, 2023. It is now read-only.

convertFromHTML merged <p> tags into one <p> tag #523

Open
js8310 opened this issue Jul 7, 2016 · 22 comments
Open

convertFromHTML merged <p> tags into one <p> tag #523

js8310 opened this issue Jul 7, 2016 · 22 comments

Comments

@js8310
Copy link

js8310 commented Jul 7, 2016

I'm having some problems with the convertFromHTML, it doesn't render the right data as I expected.

For example,
html: <p>Paragraph 1</p>, <p>Paragraph 2</p>, <p>Paragraph 3</p>

editorState:
EditorState.createWithContent(ContentState.createFromBlockArray(convertFromHTML(_html_)), Decorator)

The html at this stage render correctly as expected.

Paragraph 1
Paragraph 2
Paragraph 3

However, when I save data and refresh the page, it displays as below (all in one line)

Paragraph 1 Paragraph 2 Paragraph 3

Then, I clicked on the editor and suddenly the html turned into
<p>Paragraph 1 Paragraph 2 Paragraph 3</p>

Does anyone has similar problem like this? Thanks.

@sophiebits
Copy link
Contributor

What do you mean, "when I save data"?

@js8310
Copy link
Author

js8310 commented Jul 8, 2016

@spicyj, sorry for my uncleared explanation.

here is an example,
html: <p>A</p><p>B</p><p>C</p>

Basically the result that I've got from convertFromHTML(html) is
Object {key: "1lic8", type: "unstyled", text: "A B C", characterList: Array[5], depth: 0}

I expected to get 3 ContentBlock objects as there are 3 paragraphs.
Object {key: "d1m9h", type: "unstyled", text: "A", characterList: Array[1], depth: 0}
Object {key: "4qnh7", type: "unstyled", text: "B", characterList: Array[1], depth: 0}
Object {key: "457nc", type: "unstyled", text: "C", characterList: Array[1], depth: 0}

@sophiebits
Copy link
Contributor

Thanks, I can repro this.

@CLowbrow
Copy link
Contributor

@spicyj I can take a look at this this week if no one else is.

@sophiebits
Copy link
Contributor

@CLowbrow Go for it.

@abhoopathy
Copy link

Is there currently a workaround for this?

@sophiebits
Copy link
Contributor

Workaround: convertFromHTML takes as its optional third argument a "block render map" -- I believe if you specify a map that includes the <p> tag as output then paragraphs will be mapped to that block type.

https://github.com/facebook/draft-js/blob/b5d87c2765f7a6e72cd0ae6618682e8d0c5556a3/src/model/encoding/convertFromHTMLToContentBlocks.js#L511

@abhoopathy
Copy link

Thanks @spicyj

@Grsmto
Copy link

Grsmto commented Aug 11, 2016

I tried applying the workaround but without success. Did you figure out a solution @abhoopathy ?
I passed a render map like that:

const blockRenderMap = Immutable.Map({
    'paragraph': {
        element: 'p'
    },
    'unstyled': {
        element: 'p'
    }
 });

Thanks!

Edit: I ended up storing my input content as draftjs raw datas (using convertFromRaw, convertToRaw instead of trying to output/input some HTML). I would advice anyone wanting to save datas from DraftJS to a DB to do so until some improvements are done with the HTML importer/exporter. Also to display content as HTML, I used this HTML exporter: https://github.com/sstur/draft-js-export-html

@jbrozena22
Copy link

jbrozena22 commented Aug 23, 2016

Same here, couldn't get that render map to work correctly.

tags just kept getting combined.

My workaround was to replace 'p' tags with 'div' tags in my HTML. Obviously this workaround only works if you have access to the input HTML.

@N1kto
Copy link

N1kto commented Sep 28, 2016

OK. Seems <p> tags wont work in DraftJS. Maybe I was doing smth wrong, but it looks DraftJS wraps any text contents into div -> span and then wraps this into whatever element you provide in your blockRenderMap. Since <p> element supports only Phrasing content children it throws violation error since <div> cannot be a child of <p>.
p_div

@jbrozena22 I tried same approach, but it only worked in case all blocks were <p />. If there was at least some known block right after <p />, say <h2 /> it combined all <p />'s into one <div /> with text being concatenated. My use case was copy/pasting content from other editor (Medium).

My 'workaround' was to replace all <p> tags with <section>ones (since I have access to input HTML string). And then add section to blockRenderMap

@NdYAG
Copy link

NdYAG commented Oct 25, 2016

@N1kto this workaround works but has a severe drawback: deleting section element requires two backspace .

there's a function called getBlockMapSupportedTags in convertFromHTMLToContentBlocks.js which join the chunks of two unstyled element, should we remove the filter in this function?

@N1kto
Copy link

N1kto commented Oct 25, 2016

@NdYAG well, haven't even noticed that drawback to be honest. Comparing to initial problem that's a minor issue, at least for me.
Not sure that exactly getBlockMapSupportedTags causes blocks combining, since it only returns a list of supported tags, joinChunks seems to be responsible for that. But removing the filter within getBlockMapSupportedTags should make it possible to map <p /> elements (or any other) to divs within blockRenderMap, e.g.

Draft.DefaultDraftBlockRenderMap.merge(Immutable.Map({
  p: {
    'element': 'div'
  }
}))

However don't know what would be other consequences of doing that - it has been added there for some reason. Also it might be overwritten by default unstyled record - since it also maps to div element.

@tomconroy
Copy link

tomconroy commented Nov 3, 2016

This worked for me:

import { 
  DefaultDraftBlockRenderMap, 
  ContentState, 
  convertFromHTML,
  getSafeBodyFromHTML
} from 'draft-js';
const blockRenderMap = DefaultDraftBlockRenderMap.set('p', { element: 'p' });
const blocksFromHTML = convertFromHTML(html, getSafeBodyFromHTML, blockRenderMap)
  .map(block => (block.get('type') === 'p' ? block.set('type', 'unstyled') : block));
const contentState = ContentState.createFromBlockArray(blocksFromHTML);

edited to show imports

@N1kto
Copy link

N1kto commented Nov 3, 2016

@davidchang you've closed both this task and #757 (where you said you were closing that one in favor of this one) and there is no consolidated task created (at least not referenced). Is it intentional?

@sophiebits
Copy link
Contributor

This issue is still open.

@N1kto
Copy link

N1kto commented Nov 3, 2016

@spicyj @davidchang I am sorry, must have overlooked.

@ZeroCho
Copy link

ZeroCho commented Nov 23, 2016

@tomconroy Can you tell me please what getSafeBodyFromHTML looks like? Without that, browser shows an error DOMBuilder is not a function

@landrade
Copy link

@ZeroCho Maybe you can use a jsdom to do this behavior on the server. Like this:

function getSafeBodyFromHTML(html) {
    var doc = jsdom(html);
    var window = doc.defaultView;
    global.HTMLElement = window.HTMLElement;
    global.HTMLAnchorElement = window.HTMLAnchorElement;
    return window.document.body;
}

@ZeroCho
Copy link

ZeroCho commented Nov 23, 2016

@tomconroy @landrade
Oh oh, never mind. I found out that I can import { getSafeBodyFromHTML } from 'draft-js'.
Thanks for helping me!

@thibaudcolas
Copy link
Contributor

thibaudcolas commented Mar 7, 2018

I just realised this is particularly problematic when copy-pasting Draft.js images from one editor to another. If the unstyled blocks in-between the images are empty, then they will be removed in the target editor... and it becomes impossible to select the images to remove them or add content before/after.

Edit: this also happens for other empty tags, not just p, so the problem here might be closer to #1082.

Here's an example from my demo site:

draft-js-drop-p-images

In that example, the content changes from unstyled, atomic, unstyled, atomic, unstyled to atomic, atomic which makes it completely impossible to use the editor.

@thibaudcolas
Copy link
Contributor

Here is another illustration from Facebook Notes:

notes-line-breaks-copypaste

If there was a viable workaround for this, out of all place I imagine it would have one.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests