ueberdosis / tiptap

The headless rich text editor framework for web artisans.
https://tiptap.dev
MIT License
26.6k stars 2.23k forks source link

The insertContent command adds unnecessary blank paragraphs with HTML as value #2720

Open rfgamaral opened 2 years ago

rfgamaral commented 2 years ago

What’s the bug you are facing?

The insertCommand seems to be adding unnecessary blank paragraphs to the editor when newlines are found between HTML tags.

Which browser was this experienced in? Are any special extensions installed?

n/a

How can we reproduce the bug on our side?

Take the following HTML as an example:

const htmlInput= `<h1><a href="https://tiptap.dev/">Tiptap</a></h1>
<p><strong>Hello World</strong></p>`

And then call editor.commands.insertCommand(htmlInput). This is what gets rendered by Tiptap:

<div
    translate="no"
    role="textbox"
    aria-readonly="false"
    aria-multiline="true"
    class="ProseMirror"
    tabindex="0"
    contenteditable="true"
>
    <p><br class="ProseMirror-trailingBreak" /></p>
    <h1>
        <a target="_blank" rel="noopener noreferrer nofollow" href="https://tiptap.dev">Tiptap</a>
    </h1>
    <p></p>
    <p><strong>Hello World</strong></p>
</div>

As you can see, there's one paragraph before and after the header that shouldn't be there.

Can you provide a CodeSandbox?

No response

What did you expect to happen?

Based on the example above, Tiptpa should've rendered this:

<div
    translate="no"
    role="textbox"
    aria-readonly="false"
    aria-multiline="true"
    class="ProseMirror"
    tabindex="0"
    contenteditable="true"
>
    <h1>
        <a target="_blank" rel="noopener noreferrer nofollow" href="https://tiptap.dev">Tiptap</a>
    </h1>
    <p><strong>Hello World</strong></p>
</div>

Anything to add? (optional)

No response

Did you update your dependencies?

Are you sponsoring us?

stale[bot] commented 2 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

rfgamaral commented 2 years ago

@bdbch This issue still exists, do you think this could be reopened, and possibly some attention?

github-actions[bot] commented 1 year ago

This issue is stale because it has been open 45 days with no activity. Remove stale label or comment or this will be closed in 7 days

bdbch commented 1 year ago

bump

evanshe commented 1 year ago

The same with u, instead , i call insertcontent, value is the json

inserContent({
  type: 'xxx',
content: []
})
determinationlove commented 1 year ago

Errr... Can't believe this bug has been reported for more than a year without being fixed? https://github.com/ueberdosis/tiptap/issues/2560

My situation is that I use the Enter key to break a line in the editor. The next time the editor reads the HTML, a <br> will be automatically added to the line break block.

like this:

<p>
    <br> //<--- this is redundant
    <br class="ProseMirror-trailingBreak">
</p>

I offer a temporary solution here This basically finds all ProseMirror-trailingBreak and checks if its sibling is <br> and if so, kills its sibling.

const TiptapBugPatch = (html: string) => {

        const savedHtmlString = html;

        // 創建一個暫時的 div 元素來解析 HTML 字串
        const tempDiv = document.createElement("div");
        tempDiv.innerHTML = savedHtmlString;

        // 找到所有具有 class 為 "ProseMirror-trailingBreak" 的元素
        const trailingBreakElements = tempDiv.querySelectorAll(
            ".ProseMirror-trailingBreak"
        );

        // 遍歷這些元素
        trailingBreakElements.forEach((element) => {
            // 檢查前一個元素是否為 <br>
            const previousElement = element.previousElementSibling;
            if (previousElement && previousElement.tagName === "BR") {
                // 如果前一個元素是 <br>,刪除它
                previousElement.remove();
            }
            // 刪除具有 class 為 "ProseMirror-trailingBreak" 的元素
            element.remove();
        });

        // 取得最終處理後的 HTML 字串
        const processedHtmlString = tempDiv.innerHTML;
        return processedHtmlString;
    };

Use the return value obtained as the HTML that will eventually be read by the Tiptap editor. After editing in the editor, to output and save the HTML, you only need to obtain the HTML rendered in the editor, so that there will be no errors.

Again, this is only a temporary solution, please deprecate it after the original author releases a fix.

ondrejsevcik commented 1 year ago

@determinationlove I don't think that comment like this is appropriate.

Errr... Can't believe this bug has been reported for more than a year without being fixed?

This is open source project that you can use for free. Unless you're paying for it, you can only appreciate the work of the maintainers that they put into it. And also report bugs and issues, but always in a polite way! They are not working for you.

sakshi-klaarhq commented 11 months ago

Hello, is there a way to solve this problem ? I have encountered it as well in the taskItems extension

bdbch commented 11 months ago

@sakshi-klaarhq did you experience it by using insertContentAt manually to insert a taskList or was your issue caused by the taskList extension itself?

rajatkulkarni95 commented 11 months ago

Was running into the same issue, but adding parseOptions to not preserveWhitespace made it work

  editor.chain()
            .focus()
            .deleteRange(range)
            .insertContent(content, { // content in this case is markdown with lists/task items
              parseOptions: {
                preserveWhitespace: false,
              },
            })
            .run();
        },

Here's a before after video comparison

https://github.com/ueberdosis/tiptap/assets/57321156/27dd8463-2204-4530-85aa-9f7183bbd3d7

rdewolff commented 3 months ago

This bug is still here.

Inserting html code with a list, tiptap adds paragraph tags

on every line.

For example:

When using editor.chain().focus().insertContentAt({ x, y }, text).run()

With:

const text = `<h1>Liste des Courses</h1>
    <ul>
        <li>Un gâteau à la crème</li>
        <li>Un gâteau au chocolat</li>
        <li>Des carottes</li>
        <li>Des tomates</li>
    </ul>`

The HTML in TipTap editor is the following:

<p></p><h1>Liste des Courses!</h1><p> </p><ul><li><p> </p></li><li><p>Un gâteau à la crème</p></li><li><p> </p></li><li><p>Un gâteau au chocolat</p></li><li><p> </p></li><li><p>Des carottes</p></li><li><p> </p></li><li><p>Des tomates</p></li><li><p> </p></li></ul>

Anyone found a workaround for this bug?

rdewolff commented 3 months ago

This is a codesandbox repo where you can see and reproduce the bug in action:

https://codesandbox.io/p/devbox/editor-forked-p376l7?file=%2Fsrc%2FApp.js

  1. load it
  2. hit the "ADD HTML LIST" button on the top menubar

See the resulted list broken 😭

rdewolff commented 3 months ago

I have been able to fix it:

Just remove any space between your HTML tags and the layout will be fine.

vucuong1820 commented 3 months ago

I have been able to fix it:

Just remove any space between your HTML tags and the layout will be fine.

Could you show me your solution in code? @rdewolff

rdewolff commented 3 months ago

Sure, I just use regexp to cleanup the HTML code, as follow:

export const cleanHTML = (htmlString) => {
  // This will remove all the spaces between HTML tags and words. It is too aggressive.
  // return htmlString.replace(/\s*(<[^>]+>)\s*/g, '$1')
  // This pattern specifically targets spaces that occur between a closing tag (>) and an opening tag (<), effectively removing only those spaces.
  return htmlString.replace(/>\s+</g, '><')
}

Let me know if that works for you 😉

gkubisa commented 1 month ago

Could we revert https://github.com/ueberdosis/tiptap/pull/4767 because it breaks the expected functionality within <pre>? For example:

<pre>
<span>a</span>
<span>b</span>
</pre>

image

<pre>
<span>a</span>
 <span>b</span>
</pre>

image

<pre>
<span>a</span>
  <span>b</span>
</pre>

image

There must be a better way to deal with the unwanted whitespace...

Beslinda commented 16 hours ago

For ListItems you can do the following and it will not add the additional paragraph.

import ListItem from '@tiptap/extension-list-item';

const CustomListItem = ListItem.extend({
  content: 'text*'
});

and add the CustomListItem on the extensions extensions: [......, CustomListItem]