Draft-js: Is it possible to truncate the editor content ?

Created on 26 Oct 2016  路  8Comments  路  Source: facebook/draft-js

Perhaps I just missed it, but is there any way to truncate the current ContentState while keeping its rich styles ? I'd like to render a preview of the content, say 100 characters but I can't find anywhere how to do so.

Thanks a lot 馃檹

question

Most helpful comment

I wrote a simple function to truncate it.

  truncate(editorState, charCount) {
    const contentState = editorState.getCurrentContent();
    const blocks = contentState.getBlockMap();

    let count = 0;
    let isTruncated = false;
    const truncatedBlocks = [];
    blocks.forEach((block) => {
      if (!isTruncated) {
        const length = block.getLength();
        if (count + length > charCount) {
          isTruncated = true;
          const truncatedText = block.getText().slice(0, charCount - count);
          const state = ContentState.createFromText(`${truncatedText}...`);
          truncatedBlocks.push(state.getFirstBlock());
        } else {
          truncatedBlocks.push(block);
        }
        count += length + 1;
      }
    });

    if (isTruncated) {
      const state = ContentState.createFromBlockArray(truncatedBlocks);
      return EditorState.createWithContent(state);
    }

    return editorState;
  }

All 8 comments

@antoinerey my solution is covert to rawContentState then modify blocks after that covert it back to editorState.. feels that's not ideal.. but it works on my project

That's what I'm doing too at the moment, but I have the impression that I'm fighting against the system.

@antoinerey - I apologize for the late response, could you explain your use case more? Is it that you want to render the entire Editor component, but only show the first 100 characters, but then you could hit like a "See More" button and it would expand to show everything? will you have multiple instances of this on the page?

@davidchang No problem 馃憤 What I'm trying to accomplish is a preview system, pretty much like a "Read more" button.

  1. There is a list of article
  2. On the home page, instead of displaying the full articles, I want to show a list of truncated content. Pretty much like Medium.
  3. These truncated content should be 100 characters long, with the image at the top.

So, what I'm doing at the moment is :

  1. Reorder the blocks so there is always an image at the top of the editor
  2. Modify the text content of the X first blocks so the truncated preview is always, at most, 100 characters long
  3. Add ... at the end of the preview

But I'm pretty much fighting against the system here since I have to add character metadata to be sure ... is displayed, remove the inline styles of unused text...

I could probably go for a max-height container, but how would I display an ellipsis at the end ?

I wrote a simple function to truncate it.

  truncate(editorState, charCount) {
    const contentState = editorState.getCurrentContent();
    const blocks = contentState.getBlockMap();

    let count = 0;
    let isTruncated = false;
    const truncatedBlocks = [];
    blocks.forEach((block) => {
      if (!isTruncated) {
        const length = block.getLength();
        if (count + length > charCount) {
          isTruncated = true;
          const truncatedText = block.getText().slice(0, charCount - count);
          const state = ContentState.createFromText(`${truncatedText}...`);
          truncatedBlocks.push(state.getFirstBlock());
        } else {
          truncatedBlocks.push(block);
        }
        count += length + 1;
      }
    });

    if (isTruncated) {
      const state = ContentState.createFromBlockArray(truncatedBlocks);
      return EditorState.createWithContent(state);
    }

    return editorState;
  }

TY for that piece of code @JimLiu it was really useful, I iterated over it and decided to use getBlocksAsArray as the solution you posted will iterate over all of the blocks and in my case I will have multiple instances of this "preview" component, maybe this is a premature optimization, but it was cheap so. Here it is in case it is useful for someone else:

truncate = (editorState, charCount = 200) => {
  const contentState = editorState.getCurrentContent();
  const blocks = contentState.getBlocksAsArray();

  let index = 0;
  let currentLength = 0;
  let isTruncated = false;
  const truncatedBlocks = [];

  while (!isTruncated && blocks[index]) {
    const block = blocks[index];
    const length = block.getLength();
    if (currentLength + length > charCount) {
      isTruncated = true;
      const truncatedText = block
        .getText()
        .slice(0, charCount - currentLength);
      const state = ContentState.createFromText(`${truncatedText}...`);
      truncatedBlocks.push(state.getFirstBlock());
    } else {
      truncatedBlocks.push(block);
    }
    currentLength += length + 1;
    index++;
  }

  if (isTruncated) {
    const state = ContentState.createFromBlockArray(truncatedBlocks);
    return EditorState.createWithContent(state);
  }

  return editorState;
};

@JimLiu @irvingv8 if we use ContentState.createFromText(${truncatedText}...);
It won't show entity thing also. If I want to truncate content but the entity should be there or entity should not truncate.

Here is my method of truncating editor text while preserving the formatting and styles after slightly modifying the solutions proposed by @JimLiu and @irvingv8

truncate = (editorState, charCount = 200) => {
    // https://github.com/facebook/draft-js/issues/742
    // thanks to JimLiu & irvingv8

    let newEditorState = null;

    if (editorState && editorState.getCurrentContent) {

        const contentState = editorState.getCurrentContent();
        const blocks = contentState.getBlocksAsArray();

        let index = 0;
        let currentLength = 0;
        let isTruncated = false;
        const truncatedBlocks = [];

        let inlineStyleChars = 0;

        while (!isTruncated && blocks[index]) {
            const block = blocks[index];
            const length = block.getLength();

            if (currentLength + length > charCount) {
                isTruncated = true;
                inlineStyleChars = charCount - currentLength;

                const characterList = block.getCharacterList().slice(0, inlineStyleChars);
                const newBlock = block.set('characterList', characterList);
                    // .push(
                    //     CharacterMetadata.create({'entity': '.'}),
                    //     CharacterMetadata.create({'entity': '.'}), 
                    //     CharacterMetadata.create({'entity': '.'}))) 
                    // .set('text', `${block.getText()}...`)

                truncatedBlocks.push(newBlock);

            } else {
                truncatedBlocks.push(block);
            }

            currentLength += length + 1;
            index+=1;
        }

        if (isTruncated) {

            const state = ContentState.createFromBlockArray(truncatedBlocks);
            newEditorState = EditorState.createWithContent(state)
        } else {
            newEditorState = editorState;
        }

        return [newEditorState, isTruncated];
    }

    return [newEditorState, false];
};

Note that the commented out sections were my attempts to add the ... characters, but was unsuccessful.

Was this page helpful?
0 / 5 - 0 ratings