I'm doing some work to make navigation fully accessible by mouse over, keyboard and click, depending on the resolution.
I am looking for that in mobile, only the click works. And hover, click, keyboard for higher resolutions.
Important edit :
It only works perfectly when I load the page at the correct resolution (low or high). BUT, if I resize live, the removeEventListener
doesn't work.
Plus : I don't know if my stopImmediatePropagation
is the better solution to avoid multiple functions.
Here is the idea of the code.
JS
window.addEventListener("DOMContentLoaded", onl oadFunction);
function onLoadFunction(e) {
menu_burger();
onResizeFunction(); //trigger resize function immediately
window.addEventListener("resize", onResizeFunction);
}
function onResizeFunction(e) {
if (window.innerWidth > 770) {
window.removeEventListener('resize', menu_onClick);
menu_onClick();
menu_mouseOver();
menu_onFocus();
menu_onFocusDetection();
} else {
window.removeEventListener('resize', menu_onClick);
window.removeEventListener('resize', menu_mouseOver);
window.removeEventListener('resize', menu_onFocusDetection);
window.removeEventListener('resize', menu_onFocus);
menu_onClick();
}
}
// FONCTIONS MENU
const focusDetectionEntry = document.querySelectorAll('.nav-list--focus-detection > li[aria-expanded] > button');
const focusEntry = document.querySelectorAll('.nav-list--focus > li[aria-expanded] > button');
const overEntry = document.querySelectorAll('.nav-list--over > li[aria-expanded] > button');
const clickEntry = document.querySelectorAll('.nav-list--click > li[aria-expanded] > button');
function menu_burger() {
document.querySelector('.menu-burger').addEventListener('click', function () {
this.classList.toggle('menu--open');
document.querySelector('.main-nav-list').classList.toggle('main-nav-list--open');
});
}
function menu_mouseOver() {
overEntry.forEach(sub => {
var subParent = sub.parentElement;
subParent.addEventListener('mouseover', function(e) {
subParent.setAttribute('aria-expanded', 'true');
console.log('OVER IN');
e.stopImmediatePropagation();
});
subParent.addEventListener('mouseout', function(e) {
subParent.setAttribute('aria-expanded', 'false');
console.log('OVER OUT');
e.stopImmediatePropagation();
});
})
}
function menu_onClick() {
clickEntry.forEach(sub => {
var subParent = sub.parentElement;
sub.addEventListener('click', function(e) {
let attrState = subParent.getAttribute('aria-expanded');
if (attrState === 'false') {
subParent.setAttribute('aria-expanded', 'true')
} else if (attrState === 'true') {
subParent.setAttribute('aria-expanded', 'false');
}
console.log('CLICK');
e.stopImmediatePropagation();
});
})
}
function menu_onFocus() {
focusEntry.forEach(sub => {
var subParent = sub.parentElement;
sub.addEventListener('keyup', (e) => {
console.log('TOUCHE');
if (e.keyCode === 27) {
console.log('ECHAP');
subParent.setAttribute('aria-expanded', 'false');
}
e.stopImmediatePropagation();
});
})
}
function menu_onFocusDetection() {
focusDetectionEntry.forEach(sub => {
var subParent = sub.parentElement;
sub.addEventListener('focus', function (e) {
subParent.setAttribute('aria-expanded', 'true');
console.log('FOCUS');
e.stopImmediatePropagation();
});
subParent.addEventListener('focusout', function (e) {
console.log('FOCUS OUT');
//console.log(e.relatedTarget); //target actuel
if (!subParent.contains(e.relatedTarget)) {
subParent.setAttribute('aria-expanded', 'false');
}
e.stopImmediatePropagation();
});
})
}
I tried something like that, but not successfull :
function menu_onFocusDetection() {
focusDetectionEntry.forEach(sub => {
var subParent = sub.parentElement;
// si focus sur le bouton, on modifie sont parent
function test(ev) {
subParent.setAttribute('aria-expanded', 'true');
console.log('FOCUS');
ev.stopImmediatePropagation();
}
// si le focus sort du li
function test2(ev2) {
// console.log(ev2.relatedTarget); //target actuel
console.log('FOCUS OUT');
if (!subParent.contains(ev2.relatedTarget)) {
subParent.setAttribute('aria-expanded', 'false');
}
ev2.stopImmediatePropagation();
}
if (window.innerWidth > 770) {
sub.addEventListener('focus', test);
sub.addEventListener('focusout', test2);
} else {
sub.removeEventListener('focus', test);
sub.removeEventListener('focusout', test2);
}
})
}
CodePudding user response:
Ok, so here it is the answer :
I had to split my function and do the condition in each.
window.addEventListener("DOMContentLoaded", onl oadFunction);
function onLoadFunction(e) {
onResizeFunction(); //trigger resize function immediately
window.addEventListener("resize", onResizeFunction);
}
function onResizeFunction(e) {
menu_onFocusDetection();
}
function initEntries() {
focusDetectionEntry = document.querySelectorAll('.nav-list--focus-detection > li[aria-expanded] > button');
}
function focus(e) {
this.parentElement.setAttribute('aria-expanded', 'true');
e.stopImmediatePropagation();
}
function menu_onFocusDetection() {
focusDetectionEntry.forEach(sub => {
var subParent = sub.parentElement;
if (window.innerWidth > 770) {
sub.addEventListener('focus', focus);
subParent.addEventListener('focusout', focusOut);
} else {
sub.removeEventListener('focus', focus);
subParent.removeEventListener('focusout', focusOut);
}
});
}
CodePudding user response:
Event handlers are executed every time an event occurs, not just the first time. They need to be registered only once.
Move the click and mouseover handlers to standalone function declarations, and register those handlers in the outer block. So basically:
window.addEventListener("load", onl oadFunction);
function onLoadFunction(e) {
onResizeFunction();// trigger resize function immediately
window.addEventListener("resize", onResizeFunction);
}
function onResizeFunction(e) {
if (window.innerWidth > 770) {
//over click
over();
click();
} else {
//only click
click();
}
}
function click() {
console.log('CLICK');
}
function over() {
console.log('OVER');
}
document.querySelector('.test').addEventListener('click', click);
document.querySelector('.test').addEventListener('mouseover', over);