diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 8274f50f..d2e16763 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -9,6 +9,7 @@ - `Fix` — When the focusing Block is out of the viewport, the page will be scrolled. - `Fix` — `blocks.render()` won't lead the `onChange` call in Safari - `Fix` — Editor wrapper element growing on the Inline Toolbar close +- `Fix` — Fix errors thrown by clicks on a document when the editor is being initialized ### 2.28.2 diff --git a/package.json b/package.json index 4d87891f..0cfa67ea 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@editorjs/editorjs", - "version": "2.29.0-rc.5", + "version": "2.29.0-rc.6", "description": "Editor.js — Native JS, based on API and Open Source", "main": "dist/editorjs.umd.js", "module": "dist/editorjs.mjs", diff --git a/src/components/modules/blockManager.ts b/src/components/modules/blockManager.ts index 8fe8ff45..7075d30f 100644 --- a/src/components/modules/blockManager.ts +++ b/src/components/modules/blockManager.ts @@ -744,7 +744,11 @@ export default class BlockManager extends Module { * @param {Node} childNode - node to get Block by * @returns {Block} */ - public getBlockByChildNode(childNode: Node): Block { + public getBlockByChildNode(childNode: Node): Block | undefined { + if (!childNode || childNode instanceof Node === false) { + return undefined; + } + /** * If node is Text TextNode */ diff --git a/src/components/modules/crossBlockSelection.ts b/src/components/modules/crossBlockSelection.ts index fc0c40c9..33da99c8 100644 --- a/src/components/modules/crossBlockSelection.ts +++ b/src/components/modules/crossBlockSelection.ts @@ -187,6 +187,13 @@ export default class CrossBlockSelection extends Module { private onMouseOver = (event: MouseEvent): void => { const { BlockManager, BlockSelection } = this.Editor; + /** + * Probably, editor is not initialized yet + */ + if (event.relatedTarget === null && event.target === null) { + return; + } + const relatedBlock = BlockManager.getBlockByChildNode(event.relatedTarget as Node) || this.lastSelectedBlock; const targetBlock = BlockManager.getBlockByChildNode(event.target as Node); diff --git a/src/components/modules/toolbar/blockSettings.ts b/src/components/modules/toolbar/blockSettings.ts index e7c53114..4cc1db52 100644 --- a/src/components/modules/toolbar/blockSettings.ts +++ b/src/components/modules/toolbar/blockSettings.ts @@ -13,7 +13,10 @@ import Popover, { PopoverEvent } from '../../utils/popover'; * HTML Elements that used for BlockSettings */ interface BlockSettingsNodes { - wrapper: HTMLElement; + /** + * Block Settings wrapper. Undefined when before "make" method called + */ + wrapper: HTMLElement | undefined; } /** diff --git a/src/components/modules/toolbar/index.ts b/src/components/modules/toolbar/index.ts index 575a8786..4d4f656c 100644 --- a/src/components/modules/toolbar/index.ts +++ b/src/components/modules/toolbar/index.ts @@ -32,12 +32,12 @@ import { BlockHovered } from '../../events/BlockHovered'; * HTML Elements used for Toolbar UI */ interface ToolbarNodes { - wrapper: HTMLElement; - content: HTMLElement; - actions: HTMLElement; + wrapper: HTMLElement | undefined; + content: HTMLElement | undefined; + actions: HTMLElement | undefined; - plusButton: HTMLElement; - settingsToggler: HTMLElement; + plusButton: HTMLElement | undefined; + settingsToggler: HTMLElement | undefined; } /** * @@ -316,7 +316,7 @@ export default class Toolbar extends Module { return; } - this.nodes.wrapper.classList.remove(this.CSS.toolbarOpened); + this.nodes.wrapper?.classList.remove(this.CSS.toolbarOpened); /** Close components */ this.blockActions.hide(); diff --git a/src/components/modules/toolbar/inline.ts b/src/components/modules/toolbar/inline.ts index 03294ed8..81b4b25d 100644 --- a/src/components/modules/toolbar/inline.ts +++ b/src/components/modules/toolbar/inline.ts @@ -17,16 +17,16 @@ import { IconChevronDown } from '@codexteam/icons'; * Inline Toolbar elements */ interface InlineToolbarNodes { - wrapper: HTMLElement; - togglerAndButtonsWrapper: HTMLElement; - buttons: HTMLElement; - conversionToggler: HTMLElement; - conversionTogglerContent: HTMLElement; + wrapper: HTMLElement | undefined; + togglerAndButtonsWrapper: HTMLElement | undefined; + buttons: HTMLElement | undefined; + conversionToggler: HTMLElement | undefined; + conversionTogglerContent: HTMLElement | undefined; /** * Zone below the buttons where Tools can create additional actions by 'renderActions()' method * For example, input for the 'link' tool or textarea for the 'comment' tool */ - actions: HTMLElement; + actions: HTMLElement | undefined; } /** @@ -238,6 +238,10 @@ export default class InlineToolbar extends Module { * @param {Node} node — node to check */ public containsNode(node: Node): boolean { + if (this.nodes.wrapper === undefined) { + return false; + } + return this.nodes.wrapper.contains(node); } diff --git a/src/components/modules/ui.ts b/src/components/modules/ui.ts index 25b76b0e..296a93e7 100644 --- a/src/components/modules/ui.ts +++ b/src/components/modules/ui.ts @@ -150,12 +150,20 @@ export default class UI extends Module { */ if (!readOnlyEnabled) { /** - * Unbind all events + * Postpone events binding to the next tick to make sure all ui elements are ready */ - this.enableModuleBindings(); + window.requestIdleCallback(() => { + /** + * Bind events for the UI elements + */ + this.enableModuleBindings(); + }, { + timeout: 2000, + }); } else { /** - * Bind events for the UI elements + * Unbind all events + * */ this.disableModuleBindings(); } @@ -633,8 +641,8 @@ export default class UI extends Module { * But allow clicking inside Block Settings. * Also, do not process clicks on the Block Settings Toggler, because it has own click listener */ - const isClickedInsideBlockSettings = this.Editor.BlockSettings.nodes.wrapper.contains(target); - const isClickedInsideBlockSettingsToggler = this.Editor.Toolbar.nodes.settingsToggler.contains(target); + const isClickedInsideBlockSettings = this.Editor.BlockSettings.nodes.wrapper?.contains(target); + const isClickedInsideBlockSettingsToggler = this.Editor.Toolbar.nodes.settingsToggler?.contains(target); const doNotProcess = isClickedInsideBlockSettings || isClickedInsideBlockSettingsToggler; if (this.Editor.BlockSettings.opened && !doNotProcess) {