Clickable text button for replaying audio not working

I have multiple audios on notes. They’re from different sources. Since the sources are of varied quality, it’s very helpful to know which source the audio is playing from. Clickable text buttons for replaying audios seems to be a good solution.

After trying for hours, I managed to make it work on my Linux desktop using the code from reddit with some small modification.

But the button is not clickable on iPhone/iPad. Is there a way to work around?

html:

{{#audio_BolorToli}}
    <input type="button" value="BT" class="data-audio" data-audio="audio_BolorToli">
    <span id="audio_BolorToli">
        {{audio_BolorToli}}
    </span>
{{/audio_BolorToli}}

js:

<script>
  var elements = document.querySelectorAll(".data-audio");
  for (var i = 0; i < elements.length; i++) {
    elements[i].addEventListener("click", playSound);
  }
  function playSound(evt) {
    var el = evt.target.closest(".data-audio");
    var audio = document.getElementById(el.getAttribute("data-audio"));
    fakeClick(evt, audio.querySelector(".soundLink, .replaybutton"));
  }

  // https://developer.mozilla.org/en-US/docs/Web/API/Element/closest#Polyfill
  if (window.Element && !Element.prototype.closest) {
    Element.prototype.closest =
    function(s) {
      var matches = (this.document || this.ownerDocument).querySelectorAll(s),
          i,
          el = this;
      do {
        i = matches.length;
        while (--i >= 0 && matches.item(i) !== el) {};
      } while ((i < 0) && (el = el.parentElement));
      return el;
    };
  }
  
  // https://stackoverflow.com/a/1421968
  function fakeClick(event, anchorObj) {
    if (anchorObj.click) {
      anchorObj.click()
    } else if(document.createEvent) {
      if(event.target !== anchorObj) {
        var evt = document.createEvent("MouseEvents"); 
        evt.initMouseEvent("click", true, true, window, 
            0, 0, 0, 0, 0, false, false, false, false, 0, null); 
        var allowDefault = anchorObj.dispatchEvent(evt);
        // you can check allowDefault for false to see if
        // any handler called evt.preventDefault().
        // Firefox will *not* redirect to anchorObj.href
        // for you. However every other browser will.
      }
    }
  }
</script>

CSS

 .soundLink, .replay-button {
  display: none;
 }

Preview on PC where click replaying works:
Screenshot_20220401_160128

Preview on iPad where click replaying doesn’t work:

Here’s what I tried. I’m thinking if the problem is that click events do not work on touchscreens. So I changed click to touchstart and MouseEvents to TouchEvent. It still doesn’t work.

<script>
  var elements = document.querySelectorAll(".data-audio");
  for (var i = 0; i < elements.length; i++) {
    elements[i].addEventListener("touchstart", playSound);
  }
  function playSound(evt) {
    var el = evt.target.closest(".data-audio");
    var audio = document.getElementById(el.getAttribute("data-audio"));
    fakeClick(evt, audio.querySelector(".soundLink, .replay-button"));
  }

  // https://developer.mozilla.org/en-US/docs/Web/API/Element/closest#Polyfill
  if (window.Element && !Element.prototype.closest) {
    Element.prototype.closest =
    function(s) {
      var matches = (this.document || this.ownerDocument).querySelectorAll(s),
          i,
          el = this;
      do {
        i = matches.length;
        while (--i >= 0 && matches.item(i) !== el) {};
      } while ((i < 0) && (el = el.parentElement));
      return el;
    };
  }
  
  // https://stackoverflow.com/a/1421968
  function fakeClick(event, anchorObj) {
    if (anchorObj.touchstart) {
      anchorObj.touchstart()
    } else if(document.createEvent) {
      if(event.target !== anchorObj) {
        var evt = document.createEvent('TouchEvent');
        evt.initTouchEvent("touchstart", true, true, window);
        var allowDefault = anchorObj.dispatchEvent(evt);
        // you can check allowDefault for false to see if
        // any handler called evt.preventDefault().
        // Firefox will *not* redirect to anchorObj.href
        // for you. However every other browser will.
      }
    }
  }
</script>

If you have other elements that must receive tap events, give them the class ‘tappable’ to tell AnkiMobile 2.0.39 or later that it should pass taps through to the element.

From: More Features - AnkiMobile Manual

2 Likes

Thanks so much! It works like a charm!