Friday 16 October 2009
Removing The Media Element 'load' Event
Yesterday I checked in a patch that removes support for the 'load' event on <video> and <audio> elements. We simply never fire it. Also, the networkState
attribute is now never NETWORK_LOADED
. When we've read to the end of the media resource, networkState
changes to NETWORK_IDLE
. We plan to ship this change for Firefox 3.6.
There are several reasons we're doing this, even though it will break some Web content. One reason is that the spec says that 'load' should only fire when/if we guarantee that we will never again hit the network for data for that media resource. We never guarantee this, because our cache may always discard data. For example if you play through a large media file that doesn't fit in the cache, we'll read from the network again if you seek back to the beginning. If you play through a resource that does fit in the cache, and then play another large resource we might evict blocks from the first resource to make room for the second resource. So we never followed the spec here, and if we do follow the spec then we can never fire the 'load' event.
Another reason is that 'load' is a dangerous and fairly useless event. Web authors expect that every media element will eventually receive a 'load' event, because documents and images do, right? But that expectation is in vain, in many situations a 'load' event will never be fired. Furthermore authors cannot predict those situations because they depend on client cache size and policy. It is quite likely that the author will always get 'load' events during testing but some clients won't get a 'load' event and the site will be broken for those clients. So since the 'load' event won't ever fire in some browsers, and in other browsers will fail to fire in unpredictable situations, authors should never use it. In practice we see authors inappropriately using 'load' when they should be using another event like 'canplaythrough' or 'suspend'.
In fact, this argument is persuasive enough that 'load' for media elements and the NETWORK_LOADED state have been removed from the spec. There was consensus from Mozilla, Google, Apple and Opera developers that this is the right thing to do. I expect that other browsers will be following suit soon; in fact, apparently Chrome has never fired a 'load' event on media elements.
If you have been using 'load' on your <video> or <audio> elements, you should use a different event. There are several to choose from depending on what you actually want:
- If you want to do something when the video metadata is available (e.g. duration or size), use 'loadedmetadata'.
- If you want to do something when the first frame of video can be displayed, use 'loadeddata'.
- If you want to do something when the browser has stopped downloading the resource, use 'suspend'. Note however that the browser might stop before reaching the end of the resource, e.g. if its cache fills up, or the element doesn't have 'autobuffer' set.
- If you want to do something when there's enough data loaded to start playing, use 'canplay'.
- If you want to do something when there's enough data loaded to start playing and playback should be able to make it to the end without stalling on the network, use 'canplaythrough'. Note however that 'canplaythrough' might not fire because the browser's cache fills up, in which case you'll get a 'suspend'.
- If you want to do something when the video has finished playing, use 'ended'.
Comments
canplaythrough relies on the datarate not decreasing (which might happen) and suspend doesn't help either, as you say.
So now what? How can I make the sound play when its done loading? I want to be absolutely sure it'll finish playing without buffering in the middle.
canplaythrough is our best guess. Just use that. But you probably should also check for 'suspend' or fall back to a timeout, since we might never fire canplaythrough if the cache is small and your resource is large.