Skip to content

Instantly share code, notes, and snippets.

@N1kto
Created October 25, 2016 20:27
Show Gist options
  • Select an option

  • Save N1kto/6702e1c2d89a33a15a032c234fc4c34e to your computer and use it in GitHub Desktop.

Select an option

Save N1kto/6702e1c2d89a33a15a032c234fc4c34e to your computer and use it in GitHub Desktop.
DraftJS: Example of converting <img /> elements from HTML string to custom ContentBlocks
import { ContentState, genKey, Entity, CharacterMetadata, ContentBlock, convertFromHTML } from 'draft-js'
import { List, OrderedSet, Repeat, fromJS } from 'Immutable'
import { compose, toArray, extend } from 'underscore'
/*
* Helpers
*/
// Prepares img meta data object based on img attributes
const getBlockSpecForElement = (imgElement) => ({
contentType: 'image',
imgSrc: imgElement.getAttribute('src')
})
// Wraps meta data in HTML element which is 'understandable' by Draft, I used <blockquote />.
const wrapBlockSpec = (blockSpec) => {
if (blockSpec == null) {
return null
}
const tempEl = document.createElement('blockquote')
// stringify meta data and insert it as text content of temp HTML element. We will later extract
// and parse it.
tempEl.innerText = JSON.stringify(blockSpec)
return tempEl
}
// Replaces <img> element with our temp element
const replaceElement = (oldEl, newEl) => {
if (!(newEl instanceof HTMLElement)) {
return
}
const parentNode = oldEl.parentNode
return parentNode.replaceChild(newEl, oldEl)
}
const elementToBlockSpecElement = compose(wrapBlockSpec, getBlockSpecForElement)
const imgReplacer = (imgElement) =>
replaceElement(imgElement, elementToBlockSpecElement(imgElement))
// creates ContentBlock based on provided spec
const createContentBlock = (blockData = {}) => {
const { key, type, text, data, inlineStyles, entityData } = blockData
let blockSpec = {
type: type != null ? type : 'unstyled',
text: text != null ? text : '',
key: key != null ? key : genKey()
}
if (data) {
blockSpec.data = fromJS(data)
}
if (inlineStyles || entityData) {
let entityKey
if (entityData) {
const { type, mutability, data } = entityData
entityKey = Entity.create(type, mutability, data)
} else {
entityKey = null
}
const style = OrderedSet(inlineStyles || [])
const charData = CharacterMetadata.create({ style, entityKey })
blockSpec.characterList = List(Repeat(charData, text.length))
}
return new ContentBlock(blockSpec)
}
/*
* Main function
*/
// takes HTML string and returns DraftJS ContentState
export default function customHTML2Content (HTML) {
let tempDoc = new DOMParser().parseFromString(HTML, 'text/html')
// replace all <img /> with <blockquote /> elements
toArray(tempDoc.querySelectorAll('img')).forEach(imgReplacer)
// use DraftJS converter to do initial conversion. I don't provide DOMBuilder and
// blockRenderMap arguments here since it should fall back to its default ones, which are fine
let contentBlocks = convertFromHTML(tempDoc.body.innerHTML)
// now replace <blockquote /> ContentBlocks with 'atomic' ones
contentBlocks = contentBlocks.reduce(function (contentBlocks, block) {
if (block.getType() !== 'blockquote') {
return contentBlocks.concat(block)
}
const { imgSrc } = JSON.parse(block.getText())
const entityData = {
type: 'IMAGE',
mutability: 'IMMUTABLE',
data: { imgSrc }
}
const blockSpec = extend({ type: 'atomic', text: ' ' }, { entityData })
const atomicBlock = createContentBlock(blockSpec)
const spacerBlock = createContentBlock()
return contentBlocks.concat([ atomicBlock, spacerBlock ])
}, [])
tempDoc = null
return ContentState.createFromBlockArray(contentBlocks)
}
@ochukai
Copy link

ochukai commented Dec 6, 2016

I follow your code, but when i run as this

const htmlStr = '<p>Lorem ipsum ' +
      'dolor sit amet, consectetur adipiscing elit. Mauris tortor felis, volutpat sit amet ' +
      'maximus nec, tempus auctor diam. Nunc odio elit,  ' +
      'commodo quis dolor in, sagittis scelerisque nibh. ' +
      'Suspendisse consequat, sapien sit amet pulvinar  ' +
      'tristique, augue ante dapibus nulla, eget gravida ' +
      'turpis est sit amet nulla. Vestibulum lacinia mollis  ' +
      'accumsan. Vivamus porta cursus libero vitae mattis. ' +
      'In gravida bibendum orci, id faucibus felis molestie ac.  ' +
      'Etiam vel elit cursus, scelerisque dui quis, auctor risus.</p>'
       +
      '<img src="https://ss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/img/logo/logo_white_fe6da1ec.png" />';

const contentState = customHTML2Content(htmlStr);
const rawContentState = convertToRaw(contentState);

console.log('rawContentState:', rawContentState);

Uncaught Invariant Violation: Unknown DraftEntity key throw.

could you help me to reslove this problem, Thank you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment