0

I was wondering how to create buttons like the ones on this website where when clicked on would play a short audio file.

https://www.theitalianexperiment.com/learn-italian/to-be

I know how to add audio files using the element, but would love to add it to the left of text in a button format (for instance to the below code) just like with the website and this is what im stuck on.

HTML

  <tr>
  <td>Here <br> There</td>
  <td>Apa <br> Kuaka</td>
  </tr>

CSS

background-color:#f7f7f7 !important;
padding:20px !important;
border-radius:10px;
border:0px !important;
}

Thanks!

Tola
  • 53
  • 4
  • 1
    Welcome to Stack Overflow! Visit the [help], take the [tour] to see what and [ask]. Please first ***>>>[Search for related topics on SO](https://www.google.com/search?q=javascript+play+audio+click+site%3Astackoverflow.com)<<<*** and if you get stuck, post a [mcve] of your attempt, noting input and expected output using the [`[<>]`](https://meta.stackoverflow.com/questions/358992/ive-been-told-to-create-a-runnable-example-with-stack-snippets-how-do-i-do) snippet editor. – mplungjan Apr 13 '21 at 13:09
  • @Tola please check my solution, and if it works for you, please consider accepting my solution. Let me know if there's anything you'd like for me to update about my solution. – Brandon McConnell Apr 13 '21 at 16:40
  • Hey Brandon, I tweaked your code a bit and now it works. Thanks so much and I updated your solution as well. – Tola Apr 13 '21 at 18:55
  • @Tola glad to hear that worked for you! I'd love to see your tweaks as well – Brandon McConnell Apr 13 '21 at 18:58

1 Answers1

1

JavaScript has audio play() and pause() methods we can use to reproduce this behavior. I built the below example using those two methods and built a system of functional operations around them which will allow you to get buttons by media node, get the media file associated with a button node, and play/pause/stop media files effortlessly.

Check it out:

const isPlaying = media => !media.paused;
const play = media => (media.play(), getMediaGroupsByMedia(media).forEach(mgroup => mgroup.dataset.mediaState = "playing"));
const pause = media => (media.pause(), getMediaGroupsByMedia(media).forEach(mgroup => mgroup.dataset.mediaState = "paused"));
const stop = media => (media.pause(), media.currentTime = 0, getMediaGroupsByMedia(media).forEach(mgroup => mgroup.dataset.mediaState = "stopped"));
const allMedia = Array.from(document.querySelectorAll('audio, video'));
const allControls = Array.from(document.querySelectorAll('.media-control'));
const allMediaGroups = Array.from(document.querySelectorAll('.media-group'));
const getMediaByControl = control => allMedia.find(media => media.matches(control.dataset?.target));
const getControlsByMedia = media => allControls.filter(control => document.querySelector(control.dataset?.target) === media);
const getMediaGroupByControl = control => control.closest('.media-group');
const getMediaGroupsByMedia = media => getControlsByMedia(media).map(control => getMediaGroupByControl(control));
const stopAllMedia = () => allMedia.forEach(media => stop(media));

document.addEventListener('click', e => {
  if (e.target && allControls.includes(e.target)) {
    const control = e.target;
    const media = document.querySelector(control.dataset.target);
    if (isPlaying(media)) { stop(media); }
    else { stopAllMedia(); play(media); }
  }
});
document.addEventListener('ended', e => {
  if (e.target && allMedia.includes(e.target)) {
    const media = e.target;
    stop(media);
  }
});
@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@400;700&display=swap');
body {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  font-family: 'Noto Sans JP', sans-serif;
}
h1 {
  margin: 0 0 10px;
}
.media-group + .media-group {
  margin-top: 10px;
}
.media-group .media-control {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  background-color: #546e7a;
  border: none;
  border-radius: 7px;
  color: rgba(255, 255, 255, 0.875);
  overflow: hidden;
  cursor: pointer;
}
.media-group .media-control::after {
  content: '▶';
  transform: rotate(0deg);
  transition: transform 0.15s ease-out;
  pointer-events: none;
}
.media-group[data-media-state="playing"] .media-control {
  background-color: #4caf50;
}
.media-group[data-media-state="playing"] .media-control::after {
  transform: rotate(90deg);
}
<h1>Audio Sample Previews</h1>
<div class="media-group" data-media-state="stopped">
  <span>Audio #1</span>
  <button class="media-control" data-target="#audio1"></button>
</div>
<div class="media-group" data-media-state="stopped">
  <span>Audio #2</span>
  <button class="media-control" data-target="#audio2"></button>
</div>


<audio id="audio1">
  <source src="https://assets.codepen.io/1580009/sample1.ogg" type="audio/ogg">
  <source src="https://assets.codepen.io/1580009/sample1.mp3" type="audio/mpeg">
  Your browser does not support the audio element.
</audio>
<audio id="audio2">
  <source src="https://assets.codepen.io/1580009/sample2.ogg" type="audio/ogg">
  <source src="https://assets.codepen.io/1580009/sample2.mp3" type="audio/mpeg">
  Your browser does not support the audio element.
</audio>
Brandon McConnell
  • 5,776
  • 1
  • 20
  • 36