UNPKG

h5p-lib-controls

Version:

Library for adding user input to web applications

211 lines (197 loc) 5.16 kB
/** * @class * @classdesc Keyboard navigation for accessibility support */ export default class Keyboard { constructor() { /** * @property {boolean} selectability */ this.selectability = true; } /** * Inits this class * * @param {Controls} controls */ init(controls) { /** * Need to have a common binding of handleKeyDown, so that it can be a * common instance to be used for addEventListener and removeEventListener * @type {function} */ this.boundHandleKeyDown = this.handleKeyDown.bind(this); /** * @type {Controls} */ this.controls = controls; this.controls.on('addElement', this.listenForKeyDown, this); this.controls.on('removeElement', this.removeKeyDownListener, this); }; /** * Listens for a keyboard press when element is focused * * @param {HTMLElement} element * @private */ listenForKeyDown({element}) { element.addEventListener('keydown', this.boundHandleKeyDown); }; /** * Remove a keyboard press listener * * @param {HTMLElement} element * @private */ removeKeyDownListener({element}) { element.removeEventListener('keydown', this.boundHandleKeyDown); }; /** * Handles key down * * @param {KeyboardEvent} event Keyboard event * @private */ handleKeyDown(event) { switch (event.which) { case 27: // Escape this.close(event.target); event.preventDefault(); event.stopPropagation(); break; case 35: // End this.lastElement(event.target); event.preventDefault(); event.stopPropagation(); break; case 36: // Home this.firstElement(event.target); event.preventDefault(); event.stopPropagation(); break; case 13: // Enter case 32: // Space this.select(event.target); event.preventDefault(); event.stopPropagation(); break; case 37: // Left Arrow case 38: // Up Arrow // ignore with modifiers, so not to interfere with Chromevox if(!this.hasChromevoxModifiers(event)) { this.previousElement(event.target); event.preventDefault(); event.stopPropagation(); } break; case 39: // Right Arrow case 40: // Down Arrow // ignore with modifiers, so not to interfere with Chromevox if(!this.hasChromevoxModifiers(event)) { this.nextElement(event.target); event.preventDefault(); event.stopPropagation(); } break; } }; /** * Checks if the Chromevox modifiers are pressed * * @param {KeyboardEvent} event Keyboard event * @private */ hasChromevoxModifiers(event) { return event.shiftKey || event.ctrlKey; } /** * Fires the previous element event * * @param {HTMLElement|EventTarget} el * @fires Controls#previousElement */ previousElement(el) { if(this.controls.firesEvent('beforePreviousElement', el) !== false) { this.controls.firesEvent('previousElement', el); this.controls.firesEvent('afterPreviousElement', el); } }; /** * Fire the next element event * * @param {HTMLElement|EventTarget} el * @fires Controls#nextElement */ nextElement(el) { if(this.controls.firesEvent('beforeNextElement', el) !== false) { this.controls.firesEvent('nextElement', el); this.controls.firesEvent('afterNextElement', el); } }; /** * Fires the select event * * @param {EventTarget|HTMLElement} el * @fires Controls#select */ select(el){ if(this.selectability) { if(this.controls.firesEvent('before-select', el) !== false) { this.controls.firesEvent('select', el); this.controls.firesEvent('after-select', el) } } }; /** * Fire the next element event * * @param {HTMLElement|EventTarget} el * @fires Controls#nextElement */ firstElement(el) { if(this.controls.firesEvent('beforeFirstElement', el) !== false) { this.controls.firesEvent('firstElement', el); this.controls.firesEvent('afterFirstElement', el); } }; /** * Fire the next element event * * @param {HTMLElement|EventTarget} el * @fires Controls#nextElement */ lastElement(el) { if(this.controls.firesEvent('beforeLastElement', el) !== false) { this.controls.firesEvent('lastElement', el); this.controls.firesEvent('afterLastElement', el); } }; /** * Disable possibility to select a word trough click and space or enter * * @public */ disableSelectability() { this.selectability = false; }; /** * Enable possibility to select a word trough click and space or enter * * @public */ enableSelectability() { this.selectability = true; } /** * Fires the close event * * @param {HTMLElement|EventTarget} el * @fires Controls#close */ close(el) { if(this.controls.firesEvent('before-close', el) !== false) { this.controls.firesEvent('close', el); this.controls.firesEvent('after-close', el) } } }