Home > database >  Make sure nav link plays sound before redirecting to page (rails 7)
Make sure nav link plays sound before redirecting to page (rails 7)

Time:08-01

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();
  }
}
  • Related