@limetech/lime-elements
Version:
92 lines (91 loc) • 3.45 kB
JavaScript
const eventHandlers = new WeakMap();
class EnterClickable {
constructor(element) {
this.element = element;
this.isActive = false;
this.hasJustReleasedEnter = true;
this.handleKeyDown = (event) => {
if (event.key === 'Enter' && !this.isActive && !event.repeat) {
this.isActive = true;
}
};
this.handleKeyUp = (event) => {
if (event.key === 'Enter' && this.isActive) {
this.isActive = false;
this.hasJustReleasedEnter = true;
}
};
this.handleBlur = () => {
this.isActive = false;
this.hasJustReleasedEnter = true;
};
this.handleClick = (event) => {
if (!this.isActive) {
return;
}
if (this.hasJustReleasedEnter) {
this.hasJustReleasedEnter = false;
return;
}
event.stopImmediatePropagation();
};
this.callbacks = {
keydownHandler: this.handleKeyDown.bind(this),
keyupHandler: this.handleKeyUp.bind(this),
blurHandler: this.handleBlur.bind(this),
clickHandler: this.handleClick.bind(this),
};
}
enable() {
this.element.addEventListener('keydown', this.callbacks.keydownHandler);
this.element.addEventListener('keyup', this.callbacks.keyupHandler);
this.element.addEventListener('blur', this.callbacks.blurHandler);
this.element.addEventListener('click', this.callbacks.clickHandler, true);
}
disable() {
this.element.removeEventListener('keydown', this.callbacks.keydownHandler);
this.element.removeEventListener('keyup', this.callbacks.keyupHandler);
this.element.removeEventListener('blur', this.callbacks.blurHandler);
this.element.removeEventListener('click', this.callbacks.clickHandler, true);
}
}
/**
* Overrides the default browser behavior for clickable elements.
* When focused and pressing down Enter, avoids calling onClick repeatedly.
*
* **Accessibility Context:**
* Per WCAG 2.1 guidelines, keyboard users must be able to activate buttons and
* clickable elements using both Enter and Space keys. Browsers natively support
* both keys on button elements, allowing keyboard-only users full access to
* interactive controls.
*
* **Why only Enter, not Space?**
* - Enter key: Fires click events repeatedly while held down (browser bug/quirk)
* - Space key: Fires click only once on keyup (correct behavior)
*
* This utility only fixes the problematic Enter key behavior. Space key
* works correctly by default and doesn't need intervention.
*
* @param element - The clickable element
*/
export function makeEnterClickable(element) {
if (!eventHandlers.has(element)) {
const enterClickable = new EnterClickable(element);
enterClickable.enable();
eventHandlers.set(element, enterClickable);
}
}
/**
* Removes the keyboard-enter click override behavior from an element.
* Call this during teardown (e.g. `disconnectedCallback`) to avoid leaking
* event listeners.
*
* @param element - The clickable element
*/
export function removeEnterClickable(element) {
const enterClickable = eventHandlers.get(element);
if (enterClickable) {
enterClickable.disable();
eventHandlers.delete(element);
}
}