I have a page with a hidden <audio> object which is being started and stopped using a custom button via javascript. (The reason being I want to customise the button, and that drawing an audio player seems to destroy rendering performance on iPad anyway). A simplified example (in coffeescript):
// Works fine on all browsers
constructor: (@_button, @_audio) ->
@_button.on 'click', @_play // Bind button's click event with jQuery
_play: (e) =>
@_audio[0].play() // Call play() on audio element
The audio plays fine when triggered from a function bound to a click event, but I actually want an animation to complete before the file plays so I put .play() inside a setTimeout. However I just can’t get this to work:
// Will not play on iPad
constructor: (@_button, @_audio) ->
@_button.on 'click', @_play // Bind button's click event with jQuery
_play: (e) =>
setTimeout (=> // Declare a 300ms timeout
@_audio[0].play() // Call play() on audio element
), 300
I’ve checked that @_audio (this._audio) is in scope and that its play() method exists. Why doesn’t this work on iPad?
Edit: As it happens, the simplified test case above actually does work. See @apsillers’ answer below and my comments on it.
See Apple’s iOS considerations guide:
It appears that your
setTimeout()callback does not qualify as user-initiated action, despite the fact that thesetTimeout()itself was in a user-initiated function.Suggestion: I don’t have an iOS device to test on, but maybe doing an initial play/pause when the user presses the button will relieve this restriction. That is, you call
play()and then pause it at once, then make your call to animate and thesetTimeout()function with theplay()call. This makes the user-initiated function let iOS know that it is okay to load and play this video in the future.