1

Trying to do this as simply as possible. I want to play an audio file when a hyperlink is clicked and display audio controls.

Also, is there a way to make the audio persist so that it will continue playing while the visitor navigates to other pages within the site?

HTML Hyperlink:

<a href="audiofile.mp3" onclick="myFunction(this); return false;">[Audio]</a>

JS Code:

function myFunction(audioFile){
    var myAudio = new Audio(audioFile);
    myAudio.controls = true;
    myAudio.play();
}

This code plays the audio file, but does not display the audio controls.

I have also tried:

var x = document.createElement("AUDIO");

if (x.canPlayType("audio/mpeg")) {
  x.setAttribute("src","audioFile.mp3");
} else {
  x.setAttribute("src","audioFile.ogg");
}

x.setAttribute("controls", "controls");
document.body.appendChild(x);

which fails miserably.

Have also tried:

var audio = new Audio(audioFile);
audio.setAttribute("controls", "controls");
audio.play();

Also fails to play the audio or display controls

Heretic Monkey
  • 11,687
  • 7
  • 53
  • 122
  • *Also, is there a way to make the audio persist so that it will continue playing while the visitor navigates to other pages within the site?* <-- Not natively. You'd need to start the audio again on the new page. – Scott Marcus Aug 30 '23 at 17:43
  • You can make the audio continue playing if this is a SPA or you use a framework like Next.js where you can implement a persistent layout. – Danziger Aug 30 '23 at 23:03
  • You could create an iframe for the "pages within the site" to run in and have your main page just play the audio. Of course, that means your content runs in a frame with all of the issues that come with that... – Heretic Monkey Aug 30 '23 at 23:04

3 Answers3

0

What might be happening is that when the link is clicked, the audio element is created and added to the page, but the page is reloaded, so you don't get to see it, as you can see in the example below:

const audioLink = document.getElementById('audioLink');

audioLink.addEventListener('click', () => {  
  const audioElement = document.createElement('AUDIO');

  audioElement.setAttribute('src', `audioFile.${ audioElement.canPlayType('audio/mpeg') ? 'mp3' : 'ogg' }`);

  audioElement.setAttribute('controls', 'controls');
  
  document.body.appendChild(audioElement);
  
  return false;
});
audio {
  display: block;
  margin-top: 16px;
}
<a id="audioLink" href="audiofile.mp3">[Audio]</a>

Using return false as a way to prevent the event's default behavior as well as its propagation (e.preventDefault() + e.stopPropagation()) is jQuery-specific. Take a look at these:

Replacing return false with e.preventDefault() will fix your issue, as you can see below:

const audioLink = document.getElementById('audioLink');

audioLink.addEventListener('click', (e) => {
  e.preventDefault();
  
  const audioElement = document.createElement('AUDIO');

  audioElement.setAttribute('src', `audioFile.${ audioElement.canPlayType('audio/mpeg') ? 'mp3' : 'ogg' }`);

  audioElement.setAttribute('controls', 'controls');
  
  document.body.appendChild(audioElement);
});
audio {
  display: block;
  margin-top: 16px;
}
<a id="audioLink" href="audiofile.mp3">[Audio]</a>

This is how it looks for me in this second example:

enter image description here

Edit:

Also, as others have pointed out, using a link might not be semantically correct here:

  • A <button> is the most semantic and accessible option (which you can still style to look like a link), and probably won't require the use of e.preventDefault().

  • Using <a> might be acceptable in some very specific cases: For example, these links might actually open an embed player when clicked normally or open a standalone player with the selected song (or a download page) on a new tab when clicked while holding down Ctrl, and/or you might want to give your users the ability to copy or share the direct links.

  • Other elements such as <div>, <span>... won't need the e.preventDefault() either but won't be accessible at all, so you'll need to manually add some aria roles and keyboard events to get all the functionality users would usually expect from a link or a button, and even if you do that, you won't be able to reproduce all the native functionality from a link.

    See https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/button_role.

Here's an example using a <button> styled like a <a> that doesn't use e.preventDefault():

const audioButton = document.getElementById('audioButton');

audioButton.addEventListener('click', (e) => {  
  const audioElement = document.createElement('AUDIO');

  audioElement.setAttribute('src', `audioFile.${ audioElement.canPlayType('audio/mpeg') ? 'mp3' : 'ogg' }`);

  audioElement.setAttribute('controls', 'controls');
  
  document.body.appendChild(audioElement);
});
button {
  display: inline;
  background: none;
  border: none;
  padding: 0;
  font: inherit;
  color: blue;
  color: -webkit-link;
  text-decoration: underline;
  cursor: pointer;
}

audio {
  display: block;
  margin-top: 16px;
}
<button id="audioButton" data-src="audiofile.mp3">[Audio]</button>
Danziger
  • 19,628
  • 4
  • 53
  • 83
  • Hyperlinks shouldn't be used as "hooks" for click events in the first place. Hyperlinks are for navigation and to use them for other purposes can cause problems for those who rely on assistive technologies to interact with the page. Just about every element that you can see on a page supports a `click` event and so an element like a `span` should really be used here. That, not only would negate the need for `e.preventDefault()`, but it would also make the page more accessible. – Scott Marcus Aug 30 '23 at 17:46
  • @ScottMarcus I'm just pointing out the problem / misconception in the OP's question here, plus there are valid cases to use a link (e.g. this is actually a link that opens an embed player when clicked normally but opens a standalone player with the selected song when clicked while pressing Ctrl or when shared). Also, using a `span` might actually be worse as now screen readers won't even know that element can be clicked. See https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/button_role. – Danziger Aug 30 '23 at 22:33
-1

Hyperlinks shouldn't be used as "hooks" for click events in the first place. Hyperlinks are for navigation and to use them for other purposes can cause problems for those who rely on assistive technologies to interact with the page.

In your case, when you click the link, you have two things happening, first the native navigation to the href and second the click event handler, which is often a "conflict of interests" of sorts.

Just about every element that you can see on a page supports a click event and so an element like a span should really be used here. A span or a div are semantically neutral - - they don't convey any particular meaning for the data they contain, so they are great for custom non-semantical data like you need here.

You've also got an issue with the way you've created the audio element - - you never actually included it into the DOM, so it's never visible.

Really, you just need to use the HTML5 native audio element that starts out as part of the DOM, just hidden. Then, with a click to a span you simply unhide the element (which has already been configured to show its controls) and instruct it to play the audio.

Also, you should not be setting events up using HTML event attributes. That is a 25+ year old technique that we used before we had standards. Instead, do your event wiring in JavaScript with .addEventListener() as seen below.

const player = document.querySelector("audio");

document.querySelector("#audio").addEventListener("click", function(){
  player.classList.remove("hidden");
  player.play();
});
.hidden { display:none; }

/* Make the span look and feel like a clickable element  */
#audio { cursor:pointer; color:blue; }
#audio:hover { color:red; }
<span id="audio">Play Audio</span>
<audio controls src="https://www.learningcontainer.com/wp-content/uploads/2020/02/Kalimba.mp3" class="hidden"></audio>

Also, is there a way to make the audio persist so that it will continue playing while the visitor navigates to other pages within the site?

No. Most browsers will stop playing media when the browser tab loses focus and, in the case of navigating to another page in the site, the first page would be unloaded and so the audio element would no longer exist. If you really want this capability, you should look into having a SPA (single page application) and you could load new content via AJAX calls or using an iframe. This would allow you to never leave the page and therefore the audio would keep playing.

Scott Marcus
  • 64,069
  • 6
  • 49
  • 71
  • Actually, in terms of accessibility, the best option here is to use a ` – Danziger Aug 30 '23 at 22:36
-2

This is because you created a new audio which is not a part of the body so it will not be displayed.

You can try this:

function myFunction(audioFile){

var myAudio = new Audio(audioFile);
document.body.innerHTML += myAudio
myAudio.controls = true;
myAudio.play();

}
  • Have you tested this? Because I'm guessing that would just append the text "[object Audio]" to the body, since `myAudio` is not HTML... – Heretic Monkey Aug 30 '23 at 23:06