Two versions of a playButton Binding Audio With a grid link structure

The main intention here is to remove inflexible dependencies of the code, so that the one playButton function can be used by multiple play buttons.

Let’s go through this line by line, explaining the problems and potential solutions to those problems.

###Line 1

function playButtonClickHandler() {
    ...
}

Handler functions aren’t supposed to contain much code. Instead, they handle the incoming event and pass on information to where it’s needed.

function togglePlayButton(button) {
    // the remaining code in the handler is moved to here
}
function playButtonClickHandler(evt) {
    var button = document.querySelector(".playButton");
    togglePlayButton(button);
}

The togglePlayButton function can now use that button that’s passed in to it, to gain references to everything that it cares about.

###Line 3

Yes, we’re doing line 3 before line 2, because that’s directly related to the above, and line 2 will require what we do to fix line 3.

      var button = document.querySelector(".playButton");

This line in the handler function won’t work when there are multiple links.

What will work is to use the evt object to gain a reference to what was clicked on.
Because the event listener is defined on the playButton element, it could be anything inside of that playButton that evt.target will refer to. What we can do about that is to check if the target is the playButton element, and if it isn’t to go to its parent element.

function getPlayButton(el) {
    while (el.classList.contains("playButton") === false) {
        el = el.parentNode;
    }
    return el;
}
...
function playButtonClickHandler(evt) {
    var button = getPlayButton(evt.target);
    togglePlayButton(button);
}

Now the handler is doing its proper job. It’s using information from the event, to pass custom information to the togglePlayButton function.

###Line 2

      document.querySelector(".myLink").classList.remove("hide");

Hiding the element with a class name of “myLink” only works on the first one that’s on the page. It won’t work when there are multiple sets of links on the page.

What we can do about that is to start from the button that we have, and go up the DOM to a common element, which in this case is the element with a classname of “wrap”

The getPlayButton function can be made more generic for this task, by being called upTo and giving it a className.

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

That way, we can use that same upTo function to find the “wrap” element.

      upTo(button, "wrap").querySelector(".myLink").classList.remove("hide");

That’s becoming something of a train wreck though, with lots of fullstop couplings. The problem with that is that the code becomes more difficult to understand. We can create a myLink variable to help clean things up.

      var wrap = upTo(button, "wrap");
      wrap.querySelector(".myLink").classList.remove("hide");

###Line 4

      var player = button.querySelector("audio");

The button variable is being used now, so this will always find the <audio> element inside of the play button, which is good.

###Line 5

      var play = button.querySelector(".play");

This is good too.

###Line 6

      var pause = button.querySelector(".pause");

And this one is good too.

###Line 7

      playButton.querySelector(".initial").classList.add("hide");

playButton needs to be button, and when there are multiple links on the page it cannot currently find any of the others.

      button.querySelector(".initial").classList.add("hide");

###Line 8

      player.volume = 1.0;

There’s nothing wrong with this line.

###Line 9

      if (player.paused) {

This one is good too.

###Line 10

        button.classList.add("playing");

This line should not be in the if statement. It’s supposed to happen along with the mylink and initial parts, so let’s put them all together.

      button.classList.add("playing");
      button.querySelector(".initial").classList.add("hide");
      wrap.querySelector(".myLink").classList.remove("hide");
      player.volume = 1.0;
      ...

###Line 11

        playButton.querySelector(".play").classList.add("hide");

We already have play stored as a variable, so we should use that instead of searching the DOM for it all over again.

        play.classList.add("hide");

###Line 12

        playButton.querySelector(".pause").classList.remove("hide");

Likewise for the pause button. We should use the information that we already have.

        pause.classList.remove("hide");

###Line 13

        player.play();

This works well.

###Line 14

      } else {

And this is good too.

###Line 15

        playButton.querySelector(".play").classList.remove("hide");

We can use the play variable here.

        play.classList.remove("hide");

###Line 16

        button.querySelector(".pause").classList.add("hide");

And the pause variable can be used here too.

        pause.classList.add("hide");

###Line 17

        player.pause();

This line is good.

###Line 18

      }

Nothing much can go wrong with this line.

###Line 19

    }

And that last closing brace ends the function.

Conclusion

The code after those changes will be more more capable of handling multiple play buttons on the same page.

2 Likes