Search code examples
javascriptfunctionevent-bubblingpropagationremoveeventlistener

Javascript - Remove event listener at resize


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", onLoadFunction);

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);
    }
  })

}

Solution

  • Ok, so here it is the answer :

    I had to split my function and do the condition in each.

    window.addEventListener("DOMContentLoaded", onLoadFunction);
    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);
        }
      });
    }