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

Scope the event handlers to the specific DOM element #22

Open
nd0ut opened this issue Jun 24, 2024 · 1 comment
Open

Scope the event handlers to the specific DOM element #22

nd0ut opened this issue Jun 24, 2024 · 1 comment

Comments

@nd0ut
Copy link
Contributor

nd0ut commented Jun 24, 2024

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?

@ai
Copy link
Owner

ai commented Jun 25, 2024

I like the idea. Can you create PR which increase JS bundle size (if you plan to change default exports) only by 10-20% (or by using extra export)?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants