Difference between 2 Javascript codes

What are the differences between these 2 javascript codes?

I would say, Code 1 is meant for a single player, whereas Code 2 is meant for when you have more than one audio player.

Why there are more characters in Code 1, than there are in Code 2, I have no idea.

Why would there be less code putting together 3 audio players inside one code, than there would putting together 1 audio player in a single code?

Also, in Code 2, should this code be below the function?

  function getButton(el) {
      while (el.nodeName !== "HTML" && el.nodeName !== "DIV") {
        el = el.parentNode;
      }
      return el;
    }

Code 1

(function iife() {
    "use strict";

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

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

    function togglePlayButton(button) {
      var player = button.querySelector("audio");
      var play = button.querySelector(".play");
      var pause = button.querySelector(".pause");
      player.volume = 1.0;
      if (player.paused) {
        hide(play);
        show(pause);
        player.play();
        button.classList.add("playing");
      } else {
        button.classList.remove("playing");
        show(play);
        hide(pause);
        player.pause();
      }

    }
    var playButton = document.querySelector(".playButton");
    playButton.addEventListener("click", playButtonClickHandler);

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

    function playButtonClickHandler(evt) {
      var button = getPlayButton(evt.target);
      togglePlayButton(button);
    }

    function upTo(el, className) {
      while (el.classList.contains(className) === false) {
        el = el.parentNode;
      }
      return el;
    }
    var button = upTo(evt.target, "playButton");
  }());

Code 2

(function iife() {
    "use strict";

    function getButton(el) {
      while (el.nodeName !== "HTML" && el.nodeName !== "DIV") {
        el = el.parentNode;
      }
      return el;
    }


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

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


    function playButton(event) {
      var button = getButton(event.target);
      var player = button.querySelector("audio");
      var play = button.querySelector(".play");
      var pause = button.querySelector(".pause");
      button.classList.add("clicked");
      player.volume = 1.0;
      if (player.paused) {
        hide(play);
        show(pause);
        player.play();
      } else {
        show(play);
        hide(pause);
        player.pause();
      }
    }


    var playButtons = document.querySelectorAll(".playButton");
    playButtons.forEach(function addPlayButtonHandler(el) {
      el.addEventListener("click", playButton);
    });
  }());

Code 1 is more organised, so that it’s easier to find things and easier to modify as need be.

Your toys can take up less space by cramming them all into the same box, but it’s harder to find what you want when you need it.

Are you saying that, if I wanted to, I could get Code 2 to work inside of Code 1?

Only the left play button turns on.
The green one.

Why isn’t it working?

That would be because the event listener is only being added to the first playButton that it finds.

Code 2 shows you the technique for adding multiple event listeners to all of the play buttons.

Like this

What’s the difference between these 2?

What does each one mean?

  function getButton(el) {
     while (el.nodeName !== "HTML" && el.nodeName !== "DIV") {
       el = el.parentNode;
     }
     return el;
   }

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

The first one uses a superfluous check to see if we’ve gone too far up the DOM, and checks for any DIV element.
The second one also looks up the DOM, for any element that contains a classname of “playButton”.

If you have the following for the DOM:

html
- head
- body
  - div
  - div.playButton
    - svg
    - svg

and you run getPlayButton from the second svg element, it will check that second svg element, which won’t contain the playButton element. parentNode takes us up to div.playButton, which does contain playButton, so the while loop exits, and returns that element.

Is one better to use over the other?

How would I get this to work on here?

I just did it:


 (function iife() {
   "use strict";

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

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

   function playButton(event) {
     var button = getButton(event.target);
     var player = button.querySelector("audio");
     var play = button.querySelector(".play");
     var pause = button.querySelector(".pause");
     button.classList.add("clicked");
     player.volume = 1.0;
     if (player.paused) {
       hide(play);
       show(pause);
       player.play();
       button.classList.add("playing");
     } else {
       button.classList.remove("playing");
       show(play);
       hide(pause);
       player.pause();
     }

   }


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

   var playButtons = document.querySelectorAll(".playButton");
   playButtons.forEach(function addPlayButtonHandler(el) {
     el.addEventListener("click", playButton);

   });
 }());

I used to think that checking for HTML was better because it checked if what it was looking for couldn’t be found, but disguising such failures is not such a good idea.

The second one is better, but it’s restricted to finding only the one class name, and uses contains which checks everything inside of it, when matches is the more appropriate one to use.

A third one that I’ve done tends to be called upTo, which also takes the className of what you are looking for.

function upTo(el, selector) {
    while (el.classList.matches(selector) === false) {
        el = el.parentNode;
    }
    return el;
}

It’s common to use the exclamation mark in conditionals, to invert true to false, or false to true. Why did I not use it in the above function? Because el.classList.matches(selector) is visually quite busy, and easily hides the exclamation mark.

Compare these types of conditional matches for example:

    while (!el.classList.matches(selector)) {
        ...
    }
    while (el.classList.matches(selector) === false) {
        ...
    }
    while (noMatch(el, selector)) {
        ...
    }
    while (!hasMatch(el, selector)) {
        ...
    }

Those are listed in order of preference.

  • The first one results in easily making errors due to failing to notice things.
  • The second one is better than the first, and helps to make it crystal clear what we are looking for.
  • The third one is easier to understand when it’s as a function name, but using a negative term in a function name only makes it more confusing when you try to figure out the conditions under which the condition is true.
  • The last one is easier to notice the exclamation mark, but also requires having an additional hasMatch function. It is my most preferred technique to use though, because a well-named function name helps to remove a lot of confusion.
1 Like

I used to think that checking for HTML was better because it checked if what it was looking for couldn’t be found, but disguising such failures is not such a good idea.

I guess last. Not a good one.

Where does this one rank on your list?
while (el.nodeName !== "HTML" && el.nodeName !== "DIV") {

Code 1

It works without this, do I need it?


    function upTo(el, className) {
      while (el.classList.contains(className) === false) {
        el = el.parentNode;
      }
      return el;
    }
    var button = upTo(evt.target, "playButton");

Why are 2 bottom functions needed?

In the Singleplayer, compared to the Multiplayer.

Singleplayer

    var playButton = document.querySelector(".playButton");
    playButton.addEventListener("click", playButtonClickHandler);

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

    function playButtonClickHandler(evt) {
      var button = getPlayButton(evt.target);
      togglePlayButton(button);
    }

Multiplayer


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

   var playButtons = document.querySelectorAll(".playButton");
   playButtons.forEach(function addPlayButtonHandler(el) {
     el.addEventListener("click", playButton);

They were ordered from worst to best, with the last one being the best.

It has a partial benefit in that the code doesn’t fail when none of the parents are that node, but it has more of a distraction due to the HTML part helping to disguise the intent of the code. So, it comes in at worse than the first one, in place zero.

Combining the best of what we’ve come across, we end up with an ultimately best code of:

function hasMatch(el, selector) {
    return el.classList.matches(selector);
}
function upTo(el, selector) {
    while (!hasMatch(el, selector)) {
        el = el.parentNode;
    }
    return el;
}
// examples
var div = upTo(el, "div");
var playButton = upTo(el, ".playButton");

What do you mean? Can you provide further explanatory detail to your question?

In the Singleplayer, compared to the Multiplayer.

Single needs this:

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

    function playButtonClickHandler(evt) {
      var button = getPlayButton(evt.target);
      togglePlayButton(button);
    }

Multi only needs this.

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

The second one attaches the playButton function directly as the event handler, which doesn’t allow as much flexibility as when using ta handler function. That second set of code with just the one function, will eventually need to be changed.

Why would it need to be changed? It can’t stay like that?

So that I can remove this?

    function playButtonClickHandler(evt) {
      var button = getPlayButton(evt.target);
      togglePlayButton(button);
    }

So it’s just this?

    var playButton = document.querySelector(".playButton");
    playButton.addEventListener("click", playButtonClickHandler);

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

Is this supposed to be the very last in the code?

 var playButton = document.querySelector(".playButton");
    playButton.addEventListener("click", playButtonClickHandler);