SOLVED: Some Help: el.classList is undefined / button.classList is undefined

I am not sure what is wrong here since the code is working.

How do I fix it?

“203:12 TypeError: el.classList is undefined”

Code: https://jsfiddle.net/8gajsp3v/7/

(function iife() {
  "use strict";

  function show(el) {
    el.classList.remove("hide");
  }

  function hide(el) {
    el.classList.add("hide");
  }

  function getButtonContainer(el) {
    while (el.classList.contains("playButton") === false) {
      el = el.parentNode;
    }
    return el;
  }

  function hideAllButtons(button) {
    const buttons = button.querySelectorAll(".play, .pause, .speaker");
    for (let i = 0; i < buttons.length; i++) {
      hide(buttons[i]);
    }
  }

  function getPlay(button) {
    return button.querySelector(".play");
  }

  function getPause(button) {
    return button.querySelector(".pause");
  }

  function getSpeaker(button) {
    return button.querySelector(".speaker");
  }
  
  
  function showPlayButton(button) {
    const play = getPlay(button);
    hideAllButtons(button);
    show(play);
    
  }

  function isPlaying(button) {
    const play = getPlay(button);
    return play.classList.contains("hide");
  }

  function pauseAllButtons() {
    let buttons = document.querySelectorAll(".playButton");
    for (let i = 0; i < buttons.length; i++) {
      if (isPlaying(buttons[i])) {
        showPlayButton(buttons[i]);
      }
    }
  }

  function showPauseButton(button) {
    const pause = getPause(button);
    pauseAllButtons();
    hideAllButtons(button);
    show(pause);
    button.classList.add("activated");
  }

  function showSpeakerButton(button) {
    const speaker = getSpeaker(button);
    hideAllButtons(button);
    show(speaker);
  }

  function getAudio() {
    return document.querySelector("audio");
  }

  function playAudio(player, src) {
    player.volume = 1.0;
    if (player.getAttribute("src") !== src) {
      player.setAttribute("src", src);
    }
    player.play();
  }

  function showButton(button, opts) {
    if (opts.playing) {
      showPlayButton(button);
    } else {
      showPauseButton(button);
    }
  }

  function pauseAudio(player) {
    player.pause();
  }

  function manageAudio(player, opts) {
    if (opts.playing) {
      pauseAudio(player);
    } else {
      playAudio(player, opts.src);
    }
  }

  function playButton(button) {
    const player = getAudio();
    const playing = isPlaying(button);
    showButton(button, {
      playing
    });
    manageAudio(player, {
      src: button.getAttribute("data-audio"),
      playing
    });
  }


  function showPause(button) {
    if (isPlaying(button)) {
      showPauseButton(button);
    }
  }

  function showSpeaker(button) {
    if (isPlaying(button)) {
      showSpeakerButton(button);
    }
  }

  function playButtonClickHandler(evt) {
    const button = getButtonContainer(evt.target);
    playButton(button);
  }
  
     function playButtonMouseoverHandler(evt) {
      const button = getButtonContainer(evt.target);
      showPause(button);
   }

  function playButtonMouseoutHandler(evt) {
    const button = getButtonContainer(evt.target);
    showSpeaker(button);
  }

  function initButton(selector) {
    const wrapper = document.querySelector(selector);
    wrapper.addEventListener("click", playButtonClickHandler);
    wrapper.addEventListener("mouseover", playButtonMouseoverHandler);
    wrapper.addEventListener("mouseout", playButtonMouseoutHandler);
  }
  initButton(".wrapc");
}());

It may be browser dependent, but I get the error:

Uncaught TypeError: Cannot read property ‘contains’ of undefined

This boils down to one thing: In one of those functions, whatever you are trying to call the method contains on, doesn’t exist.

I would take a look at:

  function getButtonContainer(el) {
    while (el.classList.contains("playButton") === false) {
      el = el.parentNode;
    }
    return el;
  }

and

  function isPlaying(button) {
    const play = getPlay(button);
    return play.classList.contains("hide");
  }

For example, you could log out the nodes:

  function getButtonContainer(el) {
    while (el.classList.contains("playButton") === false) {
      el = el.parentNode;
      console.log(el);
    }
    return el;
  }

to see what is happening.

If there is a possibility that that may not exist, you may have to do a safety check like

while ((el.classList && el.classList.contains("playButton")) === false) {

or with optional chaining:

while (el.classList?.contains("playButton") === false) {

How do I fix it?

What’s the solution?

You need to figure out if it is acceptable for it to be undefined. If it is, then build in protection, as explained. If not, then diagnose the problem and figure out why you are getting bad data.

The problem is the blue borders. When you hover over them, your code looks upwards in the DOM tree if there’s an element with class .playButton, but since you’re only hovering over the .wrapc container, you’re actually looking for the child, not the parent. Fix:

  function getButtonContainer(el) {
    let node = el.closest('.playButton') || el.querySelector('.playButton');
    return node
  }

You still have the same problem when you hover over the blue lines in between the buttons, but I guess from here you can come up with solution.

I changed this:

Code: https://jsfiddle.net/7xpngjua/

TypeError: el.classList is undefined"

  function getButtonContainer(el) {
    while (el.classList.contains("playButton") === false) {
      el = el.parentNode;
    }
    return el;
  }

to this:

A different error message now.

Code: https://jsfiddle.net/tg056zv4/

TypeError: button.classList is undefined"

 function getButtonContainer(el) {
    function isPlayButton(el) {
      return el.classList.contains("playButton") === true;
    }
    while (el !== document && !isPlayButton(el)) {
      el = el.parentNode;
    }
    return el;
  }

Fixed

Code: https://jsfiddle.net/n30h7sw4/

  function getButtonContainer(el) {
    function isPlayButton(el) {
      return el.classList.contains("playButton") === true;
    }
    while (el !== document && !isPlayButton(el)) {
      el = el.parentNode;
    }
    return el;
  }
 function initButton(wrapperSelector) {
    const wrapper = document.querySelector(wrapperSelector);
    const buttons = wrapper.querySelectorAll(".playButton");
    buttons.forEach(function (button) {
      button.addEventListener("click", playButtonClickHandler);
      button.addEventListener("mouseover", playButtonMouseoverHandler);
      button.addEventListener("mouseout", playButtonMouseoutHandler);
     });
  }
  initButton(".wrapc");
}());