Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

editor.isActive() very slow - freezes UI for 2-3 seconds on selectAll() #1930

Closed
nokola opened this issue Sep 22, 2021 · 27 comments
Closed

editor.isActive() very slow - freezes UI for 2-3 seconds on selectAll() #1930

nokola opened this issue Sep 22, 2021 · 27 comments
Labels
Type: Bug The issue or pullrequest is related to a bug

Comments

@nokola
Copy link
Contributor

nokola commented Sep 22, 2021

Description
For ~3000 lines doc, editor.isActive() takes a long time on selectAll()
isActive('image'') took: 119.39999997615814ms
Editor.svelte:186 isActive('bold'') took: 14.299999952316284ms
Editor.svelte:186 isActive('italic'') took: 9.5ms
Editor.svelte:186 isActive('strike'') took: 9.300000011920929ms
Editor.svelte:186 isActive('paragraph'') took: 113ms
Editor.svelte:186 isActive('heading'') took: 110.19999998807907ms
Editor.svelte:186 isActive('heading'') took: 111.10000002384186ms
Editor.svelte:186 isActive('heading'') took: 112.5ms
Editor.svelte:186 isActive('bulletList'') took: 113.29999995231628ms
Editor.svelte:186 isActive('orderedList'') took: 112.19999998807907ms
Editor.svelte:186 isActive('code'') took: 7.399999976158142ms
Editor.svelte:186 isActive('codeBlock'') took: 109.80000001192093ms
Editor.svelte:186 isActive('blockquote'') took: 110.30000001192093ms
Editor.svelte:186 isActive('tableOfContents'') took: 110.19999998807907ms
Editor.svelte:186 isActive('date'') took: 111.59999996423721ms
Editor.svelte:186 isActive('link'') took: 7.899999976158142ms

Steps to reproduce the bug

  1. Setup a toolbar for the editor, similar to examples on tiptap.dev:
    let ed = {
        isActive: (name: string, attributes?: {}) => {
            if (editor == null) {
                return false;
            }

            const start = performance.now();
            let result: boolean = editor.isActive(name, attributes);
            const end = performance.now();
            console.log(`isActive('${name}'') took: ${end - start}ms`);
            return result;
        },
    };

...

        .toolbar
            button(on:click="{addImage}" class:active="{ed.isActive('image')}")
                .button-fill.icon(title="image") image
            button(on:click="{editor.chain().focus().toggleBold().run()}" class:active="{ed.isActive('bold')}"
            )
                .button-fill.icon(title="bold") bold
            button(on:click="{editor.chain().focus().toggleItalic().run()}" class:active="{ed.isActive('italic') }"
            )
                .button-fill.icon(title="italic") italic
            button(on:click="{editor.chain().focus().toggleStrike().run()}" class:active="{ed.isActive('strike') }"
            )
                .button-fill.icon(title="strikethrough") strikethrough
            //- button(on:click="{editor.chain().focus().unsetAllMarks().run()}"
            //- )
            //-     .button-fill.icon(title="clear formatting") clear-formatting
            //- button(on:click="{editor.chain().focus().clearNodes().run()}"
            //- )
            //-     .button-fill clear nodes
            .separator
            button(on:click="{editor.chain().focus().setParagraph().run()}" class:active="{ed.isActive('paragraph') }"
            )
                .button-fill(title="normal text") normal
            button(on:click="{editor.chain().focus().toggleHeading({ level: 1 }).run()}" class:active="{ed.isActive('heading', { level: 1 }) }"
            )
                .button-fill.icon(title="heading 1") h1
            button(on:click="{editor.chain().focus().toggleHeading({ level: 2 }).run()}" class:active="{ed.isActive('heading', { level: 2 }) }"
            )
                .button-fill.icon(title="heading 2") h2
            button(on:click="{editor.chain().focus().toggleHeading({ level: 3 }).run()}" class:active="{ed.isActive('heading', { level: 3 }) }"
            )
                .button-fill.icon(title="heading 3") h3
            //- button(on:click="{editor.chain().focus().toggleHeading({ level: 4 }).run()}" class:active="{ed.isActive('heading', { level: 4 }) }"
            //- )
            //-     .button-fill.icon h4
            //- button(on:click="{editor.chain().focus().toggleHeading({ level: 5 }).run()}" class:active="{ed.isActive('heading', { level: 5 }) }"
            //- )
            //-     .button-fill.icon h5
            //- button(on:click="{editor.chain().focus().toggleHeading({ level: 6 }).run()}" class:active="{ed.isActive('heading', { level: 6 }) }"
            //- )
            //-     .button-fill.icon h6
            .separator
            button(on:click="{editor.chain().focus().toggleBulletList().run()}" class:active="{ed.isActive('bulletList') }"
            )
                .button-fill.icon(title="list") list2
            button(on:click="{editor.chain().focus().toggleOrderedList().run()}" class:active="{ed.isActive('orderedList') }"
            )
                .button-fill.icon(title="ordered list") list-numbered
            button(on:click="{editor.chain().focus().toggleCode().run()}" class:active="{ed.isActive('code') }"
            )
                .button-fill.icon(title="inline code") code
            button(on:click="{editor.chain().focus().toggleCodeBlock().run()}" class:active="{ed.isActive('codeBlock') }"
            )
                .button-fill.icon(title="code block") code2
            button(on:click="{editor.chain().focus().toggleBlockquote().run()}" class:active="{ed.isActive('blockquote') }"
            )
                .button-fill.icon(title="quote") quotes-left
            .separator
            button(on:click="{editor.chain().focus().setHorizontalRule().run()}"
            )
                .button-fill(title="horizontal line") hr
            button(on:click="{editor.chain().focus().insertContent({type: 'tableOfContents'}).run()}" class:active="{ed.isActive('tableOfContents')}"
            )
                .button-fill(title="table of contents") toc
            button(on:click="{editor.chain().focus().insertContent({type: 'date'}).run()}" class:active="{ed.isActive('date')}"
            )
                .button-fill.icon(title="today's date and time") date
            //- button(on:click="{editor.chain().focus().setHardBreak().run()}"
            //- )
            //-     .button-fill.icon(title="page break") pagebreak
            .separator
            button(on:click="{setLink}" class:active="{ed.isActive('link') }"
            )
                .button-fill.icon(title="add link") link
            button(on:click="{editor.chain().focus().unsetLink().run()}" class:active="{ed.isActive('link') }"
            )
                .button-fill.icon(title="delete link") -link
            button(on:click="{editor.chain().focus().undo().run()}"
            )
                .button-fill.icon(title="undo") undo
            button(on:click="{editor.chain().focus().redo().run()}"
            )
                .button-fill.icon(title="redo") redo
  1. Add a lot of text in editor - 3000-5000 lines
  2. Press Ctrl+A to select all
  3. Editor freezes for 2+ seconds in isActive()
    Timings:
isActive('image'') took: 119.39999997615814ms
Editor.svelte:186 isActive('bold'') took: 14.299999952316284ms
Editor.svelte:186 isActive('italic'') took: 9.5ms
Editor.svelte:186 isActive('strike'') took: 9.300000011920929ms
Editor.svelte:186 isActive('paragraph'') took: 113ms
Editor.svelte:186 isActive('heading'') took: 110.19999998807907ms
Editor.svelte:186 isActive('heading'') took: 111.10000002384186ms
Editor.svelte:186 isActive('heading'') took: 112.5ms
Editor.svelte:186 isActive('bulletList'') took: 113.29999995231628ms
Editor.svelte:186 isActive('orderedList'') took: 112.19999998807907ms
Editor.svelte:186 isActive('code'') took: 7.399999976158142ms
Editor.svelte:186 isActive('codeBlock'') took: 109.80000001192093ms
Editor.svelte:186 isActive('blockquote'') took: 110.30000001192093ms
Editor.svelte:186 isActive('tableOfContents'') took: 110.19999998807907ms
Editor.svelte:186 isActive('date'') took: 111.59999996423721ms
Editor.svelte:186 isActive('link'') took: 7.899999976158142ms

image

Expected behavior
I expect Ctrl+A to be instant

Environment?

  • operating system: Win10
  • browser: Edge Chromium
  • mobile/desktop: desktop
  • tiptap version: 2

Additional context
Issue seems related to nodeBetween/textBetween

@nokola nokola added Type: Bug The issue or pullrequest is related to a bug v2 labels Sep 22, 2021
@sereneinserenade
Copy link
Contributor

@nokola I had the same issue, but with Vue 2. Since I didn't know the solution, I added a debouce from lodash(750ms) before checking the active state of every extension and now it works much better. The only catch is that the latest active state gets reflected on the menubar after ~750ms, but for us it was fine. if needed, I can provide with a codesandbox.

@philippkuehn
Copy link
Contributor

philippkuehn commented Sep 22, 2021

I’ve released some changes that makes isActive a lot faster in my local tests. But I think it’s still not enough for your needs. Can you please test again (with benchmarks) with the latest version of @tiptap/core?

@philippkuehn
Copy link
Contributor

I’ve updated our book example. It loads moby dick now (with over 200,000 words) which runs fine for me.

@nokola
Copy link
Contributor Author

nokola commented Sep 23, 2021

btw, the easy workaround for me - once selection exceeds 1000 chars, have isActive return false - works for my toolbar.

   let ed = {
        isActive: (name: string, attributes?: {}) => {
            if (editor == null) {
                return false;
            }

            const selectionRanges: SelectionRange[] =
                editor.state.selection.ranges;

            // if selection is big, workaround TipTap perf bug with isActive https://github.com/ueberdosis/tiptap/issues/1930
            if (
                selectionRanges.length > 0 &&
                selectionRanges[0].$to.pos - selectionRanges[0].$from.pos > 1000
            ) {
                return false;
            }
            // const start = performance.now();
            const result: boolean = editor.isActive(name, attributes);
            // const end = performance.now();
            // console.log(`isActive('${name}'') took: ${end - start}ms`);
            return result;
        },
    };

Then I use ed.isActive() in the toolbar.

@nokola
Copy link
Contributor Author

nokola commented Sep 23, 2021

Looks great now, thanks for the fix @philippkuehn.

I see a different bug now - editor doesn't start because of gapcursor. I'll investigate and open separate bug.

index.es.js:188 Uncaught RangeError: Duplicate use of selection JSON ID gapcursor
    at Function.jsonID (index.es.js:188)
    at index.es.js:92
    at main.ts:7

(edit: above issue fixed by removing prosemirror-example-setup and adding "prosemirror-commands": "^1.1.10", to package.json)

I commented the gapcursor code in node_modules and here are the new timings. Crazy how much the arrays were previously thrashing GC. Much better and > 300x faster now:

isActive('image'') took: 0.30000007152557373ms
Editor.svelte:198 isActive('bold'') took: 0.5ms
Editor.svelte:198 isActive('italic'') took: 0.6000000238418579ms
Editor.svelte:198 isActive('strike'') took: 0.5ms
Editor.svelte:198 isActive('paragraph'') took: 0.3999999761581421ms
Editor.svelte:198 isActive('heading'') took: 0.2999999523162842ms
Editor.svelte:198 isActive('heading'') took: 0.20000004768371582ms
Editor.svelte:198 isActive('heading'') took: 0.09999990463256836ms
Editor.svelte:198 isActive('bulletList'') took: 0.20000004768371582ms
Editor.svelte:198 isActive('orderedList'') took: 0.19999992847442627ms
Editor.svelte:198 isActive('code'') took: 0.7000000476837158ms
Editor.svelte:198 isActive('codeBlock'') took: 0.09999990463256836ms
Editor.svelte:198 isActive('blockquote'') took: 0.20000004768371582ms
Editor.svelte:198 isActive('tableOfContents'') took: 0.09999990463256836ms
Editor.svelte:198 isActive('date'') took: 0.30000007152557373ms
Editor.svelte:198 isActive('link'') took: 0.5ms

@nokola nokola closed this as completed Sep 23, 2021
@philippkuehn
Copy link
Contributor

great!

the other issue seems like a bug with your dependencies. try removing node_modules und lock file and do a fresh install.

@rezaffm
Copy link

rezaffm commented Dec 13, 2021

Not sure why this was closed, I updated all tiptap dependencies and I still have the issue. Having a text around 3.000 words with around 30-40 images, some bullet lists, around 30-40 H2, H3...laggs heavy unfortunately.

@philippkuehn
Copy link
Contributor

@rezaffm do you use react, vue 2, vue 3?

@sereneinserenade
Copy link
Contributor

@rezaffm have you tried turning spellcheck off? In my case, it was the culprit and it's been working blazing fast after turning it off

@rezaffm
Copy link

rezaffm commented Dec 13, 2021

well, without spellcheck it is a bit difficult to write 3.000 words without any errors (at least for me) :-)

by the way, can you share what you did with the debounce function?

I tested it with vue-2 and vue-3. I use Tables, Images, External Videos, Iframes et cetera - but even when I copy over the moby dick example it crashes completely actually.

I basically set up the menu exactly like here: https://tiptap.dev/examples/collaborative-editing

But even if I try it only with buttons (just as in the moby dick example), it laggs.

Only solution is to take out all the isActive() calls and then it works okay.

@sereneinserenade
Copy link
Contributor

sereneinserenade commented Dec 13, 2021

@rezaffm here you go, I think you'll get the idea from the code I've shared, but still if you think I left something out, let me know.

use of isActive with debounce and throttle

Ultimately I ended up using both debounce and throttle for different things since I wanted to save every 5 seconds even if user goes on typing for like 25s.

I have an object which has info about which button is active or not.

const editorFunctionsActiveStatuses = ref<Record<string, boolean>>({
  h1: false,
  h2: false,
  h3: false,
  h4: false,
  h5: false,
  bold: false,
  italic: false,
  underline: false,
  strike: false,
  textAlignLeft: false,
  textAlignCenter: false,
  textAlignRight: false,
  textAlignJustified: false,
  bulletList: false,
  orderedList: false,
  deleteTable: false,
  addColumnBefore: false,
  addColumnAfter: false,
  deleteColumn: false,
  addRowBefore: false,
  addRowAfter: false,
  deleteRow: false,
  mergeCells: false,
  splitCell: false,
  toggleHeaderColumn: false,
  toggleHeaderRow: false,
  toggleHeaderCell: false,
})

and i update the value with debounce

import { debounce, throttle } from 'lodash'

let lastContent = editor.getHTML()

const updateEditor = () => {
  if (!editor || lastContent === editor.getHTML()) return

  lastContent = editor.getHTML()
  // do your thing here
}

export const editorFunctionsActiveSettings = {
  h1: (editor): boolean => editor.isActive('heading', { level: 1 }),
  h2: (editor): boolean => editor.isActive('heading', { level: 2 }),
  h3: (editor): boolean => editor.isActive('heading', { level: 3 }),
  h4: (editor): boolean => editor.isActive('heading', { level: 4 }),
  h5: (editor): boolean => editor.isActive('heading', { level: 5 }),

  bold: (editor): boolean => editor.isActive('bold'),
  italic: (editor): boolean => editor.isActive('italic'),
  underline: (editor): boolean => editor.isActive('underline'),
  strike: (editor): boolean => editor.isActive('strike'),

  textAlignLeft: (editor): boolean => editor.isActive({ textAlign: 'left' }),
  textAlignCenter: (editor): boolean => editor.isActive({ textAlign: 'center' }),
  textAlignRight: (editor): boolean => editor.isActive({ textAlign: 'right' }),
  textAlignJustified: (editor): boolean => editor.isActive({ textAlign: 'justify' }),

  bulletList: (editor): boolean => editor.isActive('bulletList'),
  orderedList: (editor): boolean => editor.isActive('orderedList'),
}

export const tableFunctionsActiveSettings = {
  deleteTable: (editor): boolean => editor.can().deleteTable(),
  addColumnBefore: (editor): boolean => editor.can().addColumnBefore(),
  addColumnAfter: (editor): boolean => editor.can().addColumnAfter(),
  deleteColumn: (editor): boolean => editor.can().deleteColumn(),
  addRowBefore: (editor): boolean => editor.can().addRowBefore(),
  addRowAfter: (editor): boolean => editor.can().addRowAfter(),
  deleteRow: (editor): boolean => editor.can().deleteRow(),
  mergeCells: (editor): boolean => editor.can().mergeCells(),
  splitCell: (editor): boolean => editor.can().splitCell(),
  toggleHeaderColumn: (editor): boolean => editor.can().toggleHeaderColumn(),
  toggleHeaderRow: (editor): boolean => editor.can().toggleHeaderRow(),
  toggleHeaderCell: (editor): boolean => editor.can().toggleHeaderCell(),
}


const calcEditorButtonsActiveStatuses = () => {
  const objectToReturn: Record<string, boolean> = {}

  for (const key in editorFunctionsActiveSettings) {
    if (key) objectToReturn[key] = editorFunctionsActiveSettings[key](editor)
  }

  if (tableFunctionsActiveSettings.deleteTable(editor)) {
    objectToReturn.deleteTable = true

    for (const key in tableFunctionsActiveSettings) {
      if (key) objectToReturn[key] = tableFunctionsActiveSettings[key](editor)
    }
  } else {

    for (const key in tableFunctionsActiveSettings) {
      if (key) objectToReturn[key] = false
    }
  }

  editorFunctionsActiveStatuses.value = objectToReturn
}

const updateEditorThrottle = throttle(() => updateEditor(), 5000)

const updateEditorDebounce = debounce(() => updateEditor(), 1000)

const calcEditorButtonsThrottle = throttle(() => calcEditorButtonsActiveStatuses(), 1000, { leading: true, trailing: false })

const onEditorUpdated = () => {
  calcEditorButtonsThrottle()
  updateEditorThrottle()
  updateEditorDebounce()
}

const editor = gimmeEditor({
  content: props.content,
  autofocus: true,
})

editor.on('update', () => onEditorUpdated())

editor.on('selectionUpdate', () => onEditorUpdated())

@sereneinserenade
Copy link
Contributor

Oh and also I implemented a button for toggling spellcheck, so when users want to be in a quick mode and don't want to stop while writing and check their errors later, it's possible.

vid

Bildschirmaufnahme.2021-12-13.um.22.31.32.mp4

code:

    const isSpellCheckActive = ref(false)

    const toggleSpellCheck = () => {
      const state = JSON.parse(document.querySelector('.ProseMirror')?.getAttribute('spellcheck') || '')

      const newState = `${!state}`

      if (!['true', 'false'].includes(newState)) return

      editor.setOptions({
        editorProps: {
          attributes: {
            spellcheck: newState
          }
        }
      })

      isSpellCheckActive.value = !state
    }

@rezaffm
Copy link

rezaffm commented Dec 13, 2021

Actually, I was just about to write that disabling spell check temporarily could be also a smart solution... that's very helpful :-)

About this isActive() thing I suggested a while ago to have something like a getActiveMarkorNode() function which would just return a string, object or whatsoever... this way, you would only have to call it once and could it just compare against the string.

@hanspagel
Copy link
Contributor

Related:
https://discuss.prosemirror.net/t/performance-issue-with-chrome-v96/4208/7

Tl;dr: chrome spellcheck perf got worse.

@rezaffm
Copy link

rezaffm commented Dec 13, 2021

Yes, I actually disabled spell checking of Google as I use LanguageTool. However, it is the same story with this tool in chrome.

still the isActive issue remains, I like the denouncing idea but still wondering if there is not another solution?

I have been playing around, with vue 2 and vue 3, once I started to use more complex html elements editor became laggy and then not usable anymore.

@philippkuehn
Copy link
Contributor

@rezaffm this demo with 200.000 words runs smooth on my machines https://tiptap.dev/examples/book

@nokola
Copy link
Contributor Author

nokola commented Dec 14, 2021

Not sure why this was closed, I updated all tiptap dependencies and I still have the issue. Having a text around 3.000 words with around 30-40 images, some bullet lists, around 30-40 H2, H3...laggs heavy unfortunately.

I closed it because @philippkuehn's fix fixed the issue I was reporting - from 2-3 sec my selection perf improved to <30ms.
I suspect the spellcheck may have a different core cause.

@rezaffm
Copy link

rezaffm commented Dec 14, 2021

@philippkuehn

Okay, thatt's what I did:

Machine: Ubuntu 20.04.3 LTS
Browser: Version 95.0.4638.69 (Official Build) (64-bit)
Framework: vue-2

First Test

I copied your whole example, with the menu, with 200.000 words, disabled my extensions, just used the starer kit, spellcheck disabled. Result: laggy, doesn't work at all

Second Test

I copied the whole example, without menu, disabled my extensions, only starer kit, spellcheck disabled. Result: works perfectly

Third Test

I copied the whole example, without menu, own extensions, spellcheck disabled. Result: Laggy (I have one intensive onUpdate Function that makes it slower).

According spell check, yes, once activated, everything laggs and breaks.

I can do the same for vue 3 if it helps.

@rezaffm
Copy link

rezaffm commented Dec 14, 2021

I implemented the debouncing solution recommended by @sereneinserenade and it works now with any issues at all, there is this 1-second lag for status, but no issues at all.

It is basically similar to what I suggested initially, having an object or whatever that holds the statuses for nodes and marks.

@philippkuehn
Copy link
Contributor

@rezaffm There is a known performance issue with vue 2. That’s why I asked which framework you are using. In vue 2 the whole editor instance is reactive. In vue 3 and react only the editor state is reactive. It’s a limitation of the reactivity system of vue 2 so we can’t do here anything. Vue 3 runs super smooth. Except of the spellcheck issue in chrome (which is a browser limitation).

@rezaffm
Copy link

rezaffm commented Dec 14, 2021

@philippkuehn Hi, I ran another test, when I debounced the following code on vue-3:

this.$emit('update:modelValue', this.editor.getHTML())

Then indeed it runs smoothly (as in your demo).

It is also correct that once spellcheck is true, the whole thing hangs and doesn't work anymore, which is something we can only wait for Chrome to fix (it's quite annoying actually).

I also tested my initial text with numerous images, et cetera, and it also runs fine (even with spell checking enabled).

Just wondering, wouldn't it be a good idea to give a hint in the documentation that once used together with v-model (both in vue-2 and vue-3) it is a good idea to use a debounce function?

But coming back to your point, indeed on vue-3 it works fine.

@PeterSchoell
Copy link

I have encountered the same problem with the performance of isActive.

Another performance bottleneck are the methods this.$emit('input', this.editor.getHTML()) and the CharacterCount extension.

The performance problems only occur with very long texts (especially when jumping to the end of the text) and with Vue 2. Vue 3 may be faster, so these bottlenecks don't come into play.

But the general solution was the reference to the debounce() functions. Thx to @sereneinserenade

I debounced() all methods accordingly. Editing now runs absolutely smooth (Vue 2, Very long texts).

@rezaffm
Copy link

rezaffm commented Dec 21, 2021

Yes, this is confirmed, tiptap has clearly a weakness for vue-2 when it comes to isActive.

I can also confirm that I had to debounce the CharacterCount functionality as well, otherwise it would lagg from texts around > 1.500/2.000 words.

Debouncing CharacterCount and also the input (v-model binding) event is also necessary on vue-3, otherwise it starts lagging there as well. That's why I recommended adding a "best practice" section for this matter on the documentation side.

Finally, I went that far that I completely removed isActive from the MenuBar, however, I want to migrate to vue-3 (and vuetify) in Q1 and for the time-being we can't live without the "dancing" formats (tough we miss it a bit).

The final bottleneck when it comes to performance on my side is spellcheck. We write a lot about financial topcis, so good grammar and spelling is a must, that's why we work with LanguageTool.

However, LanguageTool checks texts in the browsers without debouncing, making the editor again very slugish from a count of 2.000 words.

Finally, the only solution to me is to in source the spelling functionality of LanguageTool with an own plugin, which also helps to get finally rid of the unreliable way Chrome handles contenteditable fields with spellchecking (it's a mess).

I really like the two guys here (and also the LanguageTool Plugin people), so definitely the extension will be open source.

@PeterSchoell
Copy link

PeterSchoell commented Dec 22, 2021

Finally, I went that far that I completely removed isActive from the MenuBar, however, I want to migrate to vue-3 (and vuetify) in Q1 and for the time-being we can't live without the "dancing" formats (tough we miss it a bit).

I have written my own method isActive
isActive(value) { if (this.activeStatuses.includes(value)) { return true; } },

In another updateButtons method i push the value in an array
if (this.editor.isActive('bold')) { this.activeStatuses.push('bold'); }

The update method is called in the editor.on("selectionUpdate"
const updateButtons = debounce(() => this.updateButtons(), 100); updateButtons();

Works perfect. I tested this with the Book Example (>200.000 words).

@rezaffm
Copy link

rezaffm commented Dec 22, 2021

Hi Peter,

Thanks for sharing. I like your solution.

With the debounce alternative, it also started lagging for me because if you call the function on 15-20 immediately, it somehow has to use "resources", right? Maybe you had the same experience.

For your updateButtons method, how many times do you call it?

They way I see it, you call it for all the marks/nodes for which you want to show an active class, right?

@PeterSchoell
Copy link

For your updateButtons method, how many times do you call it?
Each time the content changes in the editor.on("update") - once - and each time the content changes in the editor.on("selectionUpdate")

If you call the original editor.isActive method each time the content changes, it slows down. Therefore i update the states of the toolbar buttons in an update method which is debounce called - calls the update methods every 100ms, independent of the original calls.

They way I see it, you call it for all the marks/nodes for which you want to show an active class, right?

Yes

@rezaffm
Copy link

rezaffm commented Dec 22, 2021

I see, I mean I debounce the input event (500m) anyways, as otherwise two way binding is laggy.

I will have a try, or maybe just wait for the vue-3 integration. Thanks for sharing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Type: Bug The issue or pullrequest is related to a bug
Projects
None yet
Development

No branches or pull requests

6 participants