1

I have a toggle player here that works without an issue.

https://jsfiddle.net/h4q86s25/

I can't get this one to work using the same code.

In the snippet, when you click on it, the audio plays, the button changes, but only once and doesn't flip back.

How do I get the button to flip back and for the audio to go off?

https://jsfiddle.net/md4a0xyv/

enter image description here

(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");
     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 showPlayButton(button) {
       const play = getPlay(button);
       hideAllButtons(button);
       show(play);
       button.classList.remove("active");
    }
   function isPlaying(button) {
      const play = getPlay(button);
      return play.classList.contains("active");

   }

   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("active");
   }

   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 togglePlayButton(button) {
      const player = getAudio();
      const playing = isPlaying(button);
      showButton(button, {
        playing
      });
      manageAudio(player, {
         src: button.getAttribute("data-audio"),
         playing
      });
   }

   function playButtonClickHandler(evt) {
      const button = getButtonContainer(evt.target);
      togglePlayButton(button);
   }

   const playButton = document.querySelector(".wrapa");
   playButton.addEventListener("click", playButtonClickHandler);
}());
.wrapa {
  position: absolute;
  top: 0;
  left: 0;
  bottom: 0;
  right: 0;
  margin: auto;
  background-color: black;
  width: 150px;
  height: 195px;
  box-shadow: 0 0 10px 2px rgba(0, 0, 0, 0.2), 0 0 1px 2px black, inset 0 2px 2px -2px white, inset 0 0 2px 15px #47434c, inset 0 0 2px 22px black;
  border-radius: 5px;
  padding: 20px;
  perspective: 700px;
}

.wrapa.active .play.pause.button {
  transform: translateZ(20px) rotateX(25deg);
  box-shadow: 0 -10px 20px #ff1818;
}

.wrapa.active .play.pause.button .light {
  animation: flicker 0.2s infinite 0.3s;
}

.wrapa.active .play.pause.button .shine {
  opacity: 1;
}

.wrapa.active .play.pause.button .shadow {
  opacity: 0;
}

.wrapa .button {
  transition: all 0.3s cubic-bezier(1, 0, 1, 1);
  transform-origin: center center -20px;
  transform: translateZ(20px) rotateX(-25deg);
  transform-style: preserve-3d;
  background-color: #9b0621;
  width: 100%;
  height: 100%;
  position: relative;
  cursor: pointer;
  background: linear-gradient( #980000 0%, #6f0000 30%, #6f0000 70%, #980000 100%);
  background-repeat: no-repeat;
}

.wrapa .button::before {
  content: "";
  background: linear-gradient( rgba(255, 255, 255, 0.8) 10%, rgba(255, 255, 255, 0.3) 30%, #650000 75%, #320000) 50% 50%/97% 97%, #b10000;
  background-repeat: no-repeat;
  width: 100%;
  height: 50px;
  transform-origin: top;
  transform: rotateX(-90deg);
  position: absolute;
  top: 0;
}

.wrapa .button::after {
  content: "";
  background-image: linear-gradient(#650000, #320000);
  width: 100%;
  height: 50px;
  transform-origin: top;
  transform: translateY(50px) rotateX(-90deg);
  position: absolute;
  bottom: 0;
  box-shadow: 0 50px 8px 0px black, 0 80px 20px 0px rgba(0, 0, 0, 0.5);
}

.wrapa .light {
  opacity: 0;
  animation: light-off 1s;
  position: absolute;
  width: 100%;
  height: 100%;
  background-image: radial-gradient(#ffc97e, #ff1818 40%, transparent 70%);
}

.wrapa .dots {
  position: absolute;
  width: 100%;
  height: 100%;
  background-image: radial-gradient(transparent 30%, rgba(101, 0, 0, 0.7) 70%);
  background-size: 10px 10px;
}

.wrapa .characters {
  position: absolute;
  width: 100%;
  height: 100%;
  background: linear-gradient(white, white) 50% 20%/5% 20%, radial-gradient( circle, transparent 50%, white 52%, white 70%, transparent 72%) 50% 80%/33% 25%;
  background-repeat: no-repeat;
}

.wrapa .shine {
  transition: all 0.3s cubic-bezier(1, 0, 1, 1);
  opacity: 0.3;
  position: absolute;
  width: 100%;
  height: 100%;
  background: linear-gradient(white, transparent 3%) 50% 50%/97% 97%, linear-gradient( rgba(255, 255, 255, 0.5), transparent 50%, transparent 80%, rgba(255, 255, 255, 0.5)) 50% 50%/97% 97%;
  background-repeat: no-repeat;
}

.wrapa .shadow {
  transition: all 0.3s cubic-bezier(1, 0, 1, 1);
  opacity: 1;
  position: absolute;
  width: 100%;
  height: 100%;
  background: linear-gradient(transparent 70%, rgba(0, 0, 0, 0.8));
  background-repeat: no-repeat;
}

@keyframes flicker {
  0% {
    opacity: 1;
  }
  80% {
    opacity: 0.8;
  }
  100% {
    opacity: 1;
  }
}

@keyframes light-off {
  0% {
    opacity: 1;
  }
  80% {
    opacity: 0;
  }
}
<audio></audio>
<div class="playButton wrapa" data-audio="https://getradio.me/svoefm">
   <div class="button play pause">
      <div class="light"></div>
      <div class="dots"></div>
      <div class="characters"></div>
      <div class="shine"></div>
      <div class="shadow"></div>
   </div>
</div>
  • 1
    The selector `.wrapa.active .play .pause .button .shine` is unlikely to match anything, since it appears as though the element with the class `play` always(?) has the class `pause` and the class `button` and that selector finds all elements with the class `shine` which has an ancestor with the class `button`, which itself has an ancestor with the class `pause`, which itself has an ancestor with the class `play`, which itself has an ancestor with the classes `wrapa` and `active`. Space is the descendant selector. Combine multiple classes on the same element with no spaces. – Heretic Monkey Jul 26 '21 at 23:34
  • How would I be able to get it working? –  Jul 26 '21 at 23:35
  • 1
    BTW, the function `getButtonContainer` could be replaced with `return el.closest('.playButton');` in modern browsers. – Heretic Monkey Jul 26 '21 at 23:37
  • Will you be able to provide an answer with the code working? –  Jul 26 '21 at 23:38
  • 1
    "Combine multiple classes on the same element with no spaces." So `.wrapa.active .play.pause.button .shine` for that specific selector. Use logic to figure out the rest. – Heretic Monkey Jul 26 '21 at 23:38
  • I made those changes here: https://jsfiddle.net/L763kxst/ –  Jul 26 '21 at 23:40
  • Probably not. This is a commonly asked question, and you can easily fix it yourself. – Heretic Monkey Jul 26 '21 at 23:40
  • Does this answer your question? [What does a space mean in a CSS selector? i.e. What is the difference between .classA.classB and .classA .classB?](https://stackoverflow.com/questions/1126338/what-does-a-space-mean-in-a-css-selector-i-e-what-is-the-difference-between-c) – Heretic Monkey Jul 26 '21 at 23:41
  • I can't fix it myself though. Javascript is new to me. –  Jul 26 '21 at 23:42
  • What do I do next? https://jsfiddle.net/L763kxst/ –  Jul 26 '21 at 23:43
  • 1
    There's a lot of code that's been commented out. Seems to me that the `showButton` function should handle the state of the button. Did you try to add back in the functions that are commented out? – John Jul 26 '21 at 23:52
  • Yes, Here I got it to turn on, but not off. https://jsfiddle.net/md4a0xyv/ And I updated the snippet. –  Jul 26 '21 at 23:53
  • I can't get the audio player button to turn off. It flips on, but I can't get it to go off. –  Jul 27 '21 at 00:01

1 Answers1

1

The error is in your getPause and getPlay functions. You try to find the button with the .play class, but your container contains the active state, not the .play button. If you edit these two functions:

   function getPlay(button) {
      return button;
   }

   function getPause(button) {
      return button;
   }

I think it should work as expected.

EDIT: Second solution (exercise). You could also try to add the active state to the .play button instead of the container. Then you have to modify showPauseButton and showPlayButton.

Example:

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

(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");
     for (let i = 0; i < buttons.length; i++) {
       hide(buttons[i]);
     }
   }

   function getPlay(button) {
      return button;
   }

   function getPause(button) {
      return button;
   }

   function showPlayButton(button) {
       const play = getPlay(button);
       hideAllButtons(button);
       show(play);
       button.classList.remove("active");
    }
   function isPlaying(button) {
      const play = getPlay(button);
      return play.classList.contains("active");

   }

   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("active");
   }

   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 togglePlayButton(button) {
      const player = getAudio();
      const playing = isPlaying(button);
      showButton(button, {
        playing
      });
      manageAudio(player, {
         src: button.getAttribute("data-audio"),
         playing
      });
   }

   function playButtonClickHandler(evt) {
      const button = getButtonContainer(evt.target);
      togglePlayButton(button);
   }

   const playButton = document.querySelector(".wrapa");
   playButton.addEventListener("click", playButtonClickHandler);
}());
.wrapa {
  position: absolute;
  top: 0;
  left: 0;
  bottom: 0;
  right: 0;
  margin: auto;
  background-color: black;
  width: 150px;
  height: 195px;
  box-shadow: 0 0 10px 2px rgba(0, 0, 0, 0.2), 0 0 1px 2px black, inset 0 2px 2px -2px white, inset 0 0 2px 15px #47434c, inset 0 0 2px 22px black;
  border-radius: 5px;
  padding: 20px;
  perspective: 700px;
}

.wrapa.active .play.pause.button {
  transform: translateZ(20px) rotateX(25deg);
  box-shadow: 0 -10px 20px #ff1818;
}

.wrapa.active .play.pause.button .light {
  animation: flicker 0.2s infinite 0.3s;
}

.wrapa.active .play.pause.button .shine {
  opacity: 1;
}

.wrapa.active .play.pause.button .shadow {
  opacity: 0;
}

.wrapa .button {
  transition: all 0.3s cubic-bezier(1, 0, 1, 1);
  transform-origin: center center -20px;
  transform: translateZ(20px) rotateX(-25deg);
  transform-style: preserve-3d;
  background-color: #9b0621;
  width: 100%;
  height: 100%;
  position: relative;
  cursor: pointer;
  background: linear-gradient( #980000 0%, #6f0000 30%, #6f0000 70%, #980000 100%);
  background-repeat: no-repeat;
}

.wrapa .button::before {
  content: "";
  background: linear-gradient( rgba(255, 255, 255, 0.8) 10%, rgba(255, 255, 255, 0.3) 30%, #650000 75%, #320000) 50% 50%/97% 97%, #b10000;
  background-repeat: no-repeat;
  width: 100%;
  height: 50px;
  transform-origin: top;
  transform: rotateX(-90deg);
  position: absolute;
  top: 0;
}

.wrapa .button::after {
  content: "";
  background-image: linear-gradient(#650000, #320000);
  width: 100%;
  height: 50px;
  transform-origin: top;
  transform: translateY(50px) rotateX(-90deg);
  position: absolute;
  bottom: 0;
  box-shadow: 0 50px 8px 0px black, 0 80px 20px 0px rgba(0, 0, 0, 0.5);
}

.wrapa .light {
  opacity: 0;
  animation: light-off 1s;
  position: absolute;
  width: 100%;
  height: 100%;
  background-image: radial-gradient(#ffc97e, #ff1818 40%, transparent 70%);
}

.wrapa .dots {
  position: absolute;
  width: 100%;
  height: 100%;
  background-image: radial-gradient(transparent 30%, rgba(101, 0, 0, 0.7) 70%);
  background-size: 10px 10px;
}

.wrapa .characters {
  position: absolute;
  width: 100%;
  height: 100%;
  background: linear-gradient(white, white) 50% 20%/5% 20%, radial-gradient( circle, transparent 50%, white 52%, white 70%, transparent 72%) 50% 80%/33% 25%;
  background-repeat: no-repeat;
}

.wrapa .shine {
  transition: all 0.3s cubic-bezier(1, 0, 1, 1);
  opacity: 0.3;
  position: absolute;
  width: 100%;
  height: 100%;
  background: linear-gradient(white, transparent 3%) 50% 50%/97% 97%, linear-gradient( rgba(255, 255, 255, 0.5), transparent 50%, transparent 80%, rgba(255, 255, 255, 0.5)) 50% 50%/97% 97%;
  background-repeat: no-repeat;
}

.wrapa .shadow {
  transition: all 0.3s cubic-bezier(1, 0, 1, 1);
  opacity: 1;
  position: absolute;
  width: 100%;
  height: 100%;
  background: linear-gradient(transparent 70%, rgba(0, 0, 0, 0.8));
  background-repeat: no-repeat;
}

@keyframes flicker {
  0% {
    opacity: 1;
  }
  80% {
    opacity: 0.8;
  }
  100% {
    opacity: 1;
  }
}

@keyframes light-off {
  0% {
    opacity: 1;
  }
  80% {
    opacity: 0;
  }
}
<audio></audio>
<div class="playButton wrapa" data-audio="https://getradio.me/svoefm">
   <div class="button play pause">
      <div class="light"></div>
      <div class="dots"></div>
      <div class="characters"></div>
      <div class="shine"></div>
      <div class="shadow"></div>
   </div>
</div>
John
  • 10,165
  • 5
  • 55
  • 71
  • Is play pause only needed in the html, not the css?
    css removed. https://jsfiddle.net/mvrhjo9k/
    –  Jul 27 '21 at 00:26
  • You need it in the css as well if you want the button to look and behave the same visually. Just try to remove it from the css and see what happens. – John Jul 27 '21 at 00:31
  • I did. And it works as expected. https://jsfiddle.net/mvrhjo9k/ Then they are not needed in the css? Did you see any difference? I also cleaned up the javascript in there. I removed as much without the code breaking. –  Jul 27 '21 at 00:33
  • 1
    I noticed you kept `.button` in the jsfiddle. Yes, then `.play.pause` is not needed in the css since you only have one `.button`-element in the hierarchy. – John Jul 27 '21 at 00:35
  • Did I do a good job of cleaning up the javascript? https://jsfiddle.net/mvrhjo9k/ –  Jul 27 '21 at 00:36
  • I just did this ///// function showPlayButton(button) { const play = getPlay(button); play.classList.remove("active"); } ///// function showPauseButton(button) { const pause = getPause(button); pause.classList.add("active"); } https://jsfiddle.net/kq9wrn7t/ What you had said to do. –  Jul 27 '21 at 00:41
  • 1
    Maybe I was too quick suggesting to do that now that I had a closer look. It's still working, but you still had to edit the `getPlay()` and `getPause()` functions. I first thought that we could get away with only changing where we added and removed the `.active` class, but I was wrong. The original example seemingly had more than one button. Anyway, keep at it :) – John Jul 27 '21 at 02:00
  • 1
    I added button back then: button.classList.remove("active"); //// button.classList.add("active"); https://jsfiddle.net/ac9q4nL5/ And I just noticed, play/pause are not needed anymore in the html. –  Jul 27 '21 at 02:10
  • I'm receiving this jslint error, how is it fixed? https://i.imgur.com/InukVl1.png Code https://jsfiddle.net/6y09z7fu/ –  Jul 27 '21 at 23:15
  • Fixed: manageAudio(player, { playing, src: button.getAttribute("data-audio") }); } https://jsfiddle.net/by02cesm/1/ –  Jul 28 '21 at 00:15
  • I updated the code here and added multiple toggle buttons. https://jsfiddle.net/cwobhv94/ When one is on, you click on another, the one before it shuts off. –  Jul 28 '21 at 00:59