I'm building an AAC App for my non-verbal daughter. The idea is that she would click on a category (image) nav link and then she would get a page of other images based on that category. each image is linked to a sound. The sound works great in the index and the category filters the images correctly, However when i click on the nav link the page loads the results and redirects too quickly before the nav links play. I tried JS to slow it down without success. Can anyone point me in the right direction please.
posts/_header.html.erb
<% if [email protected]? %>
<% @categoriez.each do |category| %>
<ul >
<li >
<%= audio_tag(polymorphic_path(category.sound), class: "audio-play", id: "#{category.category}") %>
<%= link_to image_tag(category.image, class:"category-pic"), posts_path(:cate => category.id), :onclick =>"play(\"#{category.category}\")", class:"p-1 audioButton" %>
</li>
</ul>
<% end %>
<% end %>
<script>
function play(element){
var audio = document.getElementById(element);
var isPlaying = audio.currentTime > 0 && !audio.paused && !audio.ended && audio.readyState > audio.HAVE_CURRENT_DATA;
setTimeout(function(){
if (!isPlaying) {
audio.play();
}
}, 5000);
}
</script>
CodePudding user response:
When sound is played after the click event, it doesn't stop the actual event from propagating. You could catch the event and stop it, then when audio is done playing navigate to the linked URL.
<%= link_to "category 1", "/category/1",
onclick: "play(event)",
data: { audio: audio_path("sound.mp3") } %>
<script type="text/javascript">
function play(event){
event.preventDefault(); // stop the click
const link = event.target; // get the clicked link tag
const audio =
new Audio(link.dataset.audio); // create a new audio element
audio.onended = function() {
window.location = link.href; // navigate to clicked link when audio stops playing
};
audio.play();
}
</script>
Or if you need to keep the <audio> tag on the page:
<div>
<%= link_to "category 1", "/category/1",
onclick: "play(event)",
data: { audio: "category_1_sound" } %>
<%= audio_tag("sound.mp3", id: "category_1_sound") %>
</div>
<script type="text/javascript">
function play(event){
event.preventDefault();
const link = event.target;
const audio = document.getElementById(link.dataset.audio)
// you could add an event listener, instead, for "ended" event.
audio.onended = function() { window.location = link.href };
audio.play();
}
</script>
Update
In case you want to use Turbo:
<%= link_to "category 1", "/category/1",
onclick: "play(event)",
data: { audio: audio_path("sound.mp3") } %>
<script type="text/javascript">
function play(event) {
// Create audio element when clicking a link
const audio = new Audio(event.target.dataset.audio);
// Start playing the audio
audio.play();
// Let the click event propagate.
// Turbo will navigate without refreshing the page,
// which means audio won't be interrupted.
}
</script>
Package the above into Stimulus:
# app/helpers/application_helper.rb
module ApplicationHelper
# I mean, even I got tired of typing these data options while
# testing for this answer.
def audio_link_to name = nil, options = nil, html_options = nil, &block
html_options ||= options
html_options["data"] ||= {}
html_options["data"].merge!({
controller: "audio-link",
action: "click->audio-link#playSound",
audio_link_audio_url_value: audio_path(html_options.delete(:play))
})
link_to name, options, html_options, &block
end
end
# view
<%= audio_link_to "Category 1", "category/1", play: "audio.mp3" %>
// app/javascript/controller/audio_link_controller.js
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
static values = {
audioUrl: String
}
playSound() {
const audio = new Audio(this.audioUrlValue);
audio.play();
}
}