Skip to content

Scope the event handlers to the specific DOM element #22

Description

@nd0ut

Hey there!

We're developing some third-party widgets and decided to integrate KeyUX with them. However, we encountered an issue where KeyUX affects the host page, which is unintended behavior. To scope KeyUX to our widget's DOM elements, we wrap the window object and apply filtering within it, like this:

/**
 * @typedef {Parameters<import('keyux').KeyUXModule>[0]} MinimalWindow
 */

/**
 * @implements {MinimalWindow}
 */
class ScopedMinimalWindow {
  /**
   * @private
   * @type {Map<Function, (event: Event) => void>}
   */
  _listeners = new Map();

  /**
   * @private
   * @type {Node[]}
   */
  _scope = [];

  /**
   * @param {'keydown' | 'keyup'} type
   * @param {(event: Event) => void} listener
   */
  addEventListener(type, listener) {
    /** @param {Event} e */
    const wrappedListener = (e) => {
      const target = e.target;
      if (!target) return;
      if (
        this._scope.some(
          (el) => el === e.target || el.contains(/** @type {Node} */ (target))
        )
      ) {
        listener(e);
      }
    };
    this._listeners.set(listener, wrappedListener);
    window.addEventListener(type, wrappedListener);
  }

  /**
   * @param {'keydown' | 'keyup'} type
   * @param {(event: {}) => void} listener
   */
  removeEventListener(type, listener) {
    const wrappedListener = this._listeners.get(listener);
    if (wrappedListener) {
      window.removeEventListener(type, wrappedListener);
    }
    this._listeners.delete(listener);
  }

  get document() {
    return window.document;
  }

  get navigator() {
    return window.navigator;
  }

  /** @param {Node} scope */
  registerScope(scope) {
    this._scope.push(scope);
  }

  destroy() {
    this._scope = [];
    this._listeners.forEach((listener, originalListener) => {
      window.removeEventListener("keydown", listener);
      window.removeEventListener("keyup", listener);
      this._listeners.delete(originalListener);
    });
  }
}

This approach works perfectly. However, I would like to ask your opinion on implementing scoping directly within KeyUX.

What do you think about this?

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions