1/** 2 * Ajoute deux éléments cachés qui vont permettre de capturer le focus avant et après l'élément. 3 * @param {HTMLElement} modalElement élément sur lequel le focus doit être enfermé. 4 */ 5export function jailFocus(modalElement) { 6 if (Object.hasOwn(modalElement, 'modalJailers')) { 7 return; 8 } 9 const focusables = Array.from(modalElement.querySelectorAll(':scope *')).filter(isFocusable); 10 const beforeJailer = createJailer(); 11 const afterJailer = createJailer(); 12 modalElement.insertBefore(beforeJailer, modalElement.firstChild); 13 modalElement.appendChild(afterJailer); 14 modalElement.modalJailers = {beforeJailer, afterJailer} 15 if (focusables?.length > 0) { 16 focusables[0].focus(); 17 afterJailer.addEventListener('focus', () => { 18 focusables[0].focus(); 19 }); 20 beforeJailer.addEventListener('focus', () => { 21 focusables[focusables.length - 1].focus(); 22 }); 23 } 24} 25 26/** 27 * Suppression de la prison du focus. 28 * @param {HTMLElement} modalElement élément sur lequel le focus doit être enfermé. 29 */ 30export function unJailFocus(modalElement) { 31 modalElement.modalJailers?.beforeJailer.remove(); 32 modalElement.modalJailers?.afterJailer.remove(); 33 delete modalElement.modalJailers; 34} 35 36function isFocusable(element) { 37 if (element.getAttribute('tabindex') === '0') { 38 return true; 39 } 40 if (element.classList.contains('js-focus-jailer')) { 41 return false; 42 } 43 if (element.disabled) { 44 return false; 45 } 46 switch (element.nodeName.toLowerCase()) { 47 case 'a': 48 return !!element.href && element.rel !== 'ignore'; 49 case 'input': 50 return element.type !== 'hidden' && element.type !== 'file'; 51 case 'button': 52 case 'select': 53 case 'textarea': 54 return true; 55 default: 56 return false; 57 } 58} 59 60/** 61 * Créer un élément HTML permettant d'enfermer le focus. 62 * @return {HTMLDivElement} élément qui va récupérer le focus. 63 */ 64function createJailer() { 65 const jailer = document.createElement('div'); 66 jailer.classList.add('visually-hidden', 'js-focus-jailer'); 67 jailer.setAttribute('tabindex', '0'); 68 return jailer; 69}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.