UNPKG

@ckeditor/ckeditor5-utils

Version:

Miscellaneous utilities used by CKEditor 5.

136 lines (135 loc) 5.11 kB
/** * @license Copyright (c) 2003-2025, CKSource Holding sp. z o.o. All rights reserved. * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options */ /** * @module utils/dom/resizeobserver */ import global from './global.js'; /** * A helper class which instances allow performing custom actions when native DOM elements are resized. * * ```ts * const editableElement = editor.editing.view.getDomRoot(); * * const observer = new ResizeObserver( editableElement, entry => { * console.log( 'The editable element has been resized in DOM.' ); * console.log( entry.target ); // -> editableElement * console.log( entry.contentRect.width ); // -> e.g. '423px' * } ); * ``` * * It uses the [native DOM resize observer](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver) * under the hood. */ class ResizeObserver { /** * The element observed by this observer. */ _element; /** * The callback executed each time {@link #_element} is resized. */ _callback; /** * The single native observer instance shared across all {@link module:utils/dom/resizeobserver~ResizeObserver} instances. */ static _observerInstance = null; /** * A mapping of native DOM elements and their callbacks shared across all * {@link module:utils/dom/resizeobserver~ResizeObserver} instances. */ static _elementCallbacks = null; /** * Creates an instance of the `ResizeObserver` class. * * @param element A DOM element that is to be observed for resizing. Note that * the element must be visible (i.e. not detached from DOM) for the observer to work. * @param callback A function called when the observed element was resized. It passes * the [`ResizeObserverEntry`](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserverEntry) * object with information about the resize event. */ constructor(element, callback) { // **Note**: For the maximum performance, this class ensures only a single instance of the native // observer is used no matter how many instances of this class were created. if (!ResizeObserver._observerInstance) { ResizeObserver._createObserver(); } this._element = element; this._callback = callback; ResizeObserver._addElementCallback(element, callback); ResizeObserver._observerInstance.observe(element); } /** * The element observed by this observer. */ get element() { return this._element; } /** * Destroys the observer which disables the `callback` passed to the {@link #constructor}. */ destroy() { ResizeObserver._deleteElementCallback(this._element, this._callback); } /** * Registers a new resize callback for the DOM element. */ static _addElementCallback(element, callback) { if (!ResizeObserver._elementCallbacks) { ResizeObserver._elementCallbacks = new Map(); } let callbacks = ResizeObserver._elementCallbacks.get(element); if (!callbacks) { callbacks = new Set(); ResizeObserver._elementCallbacks.set(element, callbacks); } callbacks.add(callback); } /** * Removes a resize callback from the DOM element. If no callbacks are left * for the element, it removes the element from the native observer. */ static _deleteElementCallback(element, callback) { const callbacks = ResizeObserver._getElementCallbacks(element); // Remove the element callback. Check if exist first in case someone // called destroy() twice. if (callbacks) { callbacks.delete(callback); // If no callbacks left for the element, also remove the element. if (!callbacks.size) { ResizeObserver._elementCallbacks.delete(element); ResizeObserver._observerInstance.unobserve(element); } } if (ResizeObserver._elementCallbacks && !ResizeObserver._elementCallbacks.size) { ResizeObserver._observerInstance = null; ResizeObserver._elementCallbacks = null; } } /** * Returns are registered resize callbacks for the DOM element. */ static _getElementCallbacks(element) { if (!ResizeObserver._elementCallbacks) { return null; } return ResizeObserver._elementCallbacks.get(element); } /** * Creates the single native observer shared across all `ResizeObserver` instances. */ static _createObserver() { ResizeObserver._observerInstance = new global.window.ResizeObserver(entries => { for (const entry of entries) { const callbacks = ResizeObserver._getElementCallbacks(entry.target); if (callbacks) { for (const callback of callbacks) { callback(entry); } } } }); } } export default ResizeObserver;