Skip to content

Why don’t audio and video events bubble?

I was wondering why some Javascript of mine would not work until I figured that the audio events did not bubble up the DOM tree, e.g. the timeupdate-event.

Is there a rationale for not letting the events of the audio- and video-tag bubble?


The reason why event bubbling exists is solve the ambiguous question of which element is the intended target of the event. So, if you click on a div, did you mean to click the div, or its parent? If the child doesn’t have a click handler attached, then it checks the parent, and so on. I’m sure you know how that works.

The reason why audio events don’t bubble is because they don’t make sense on any other element. There’s no ambiguity when you trigger a timeupdate on an audio element whether it’s meant for the audio element itself or its parent div, so there’s no need to bubble it.

You can read a fuller history of event bubbling here

Event delegation

Event delegation is still possible by utilizing the capturing phase of the event. Simply add true as the third argument for addEventListener which looks like this:

document.addEventListener('play', function(e){
    // audio/video element
}, true);

Note, that this event doesn’t bubble, but goes down the DOM-tree and can’t be stopped with stopPropagation.

In case you want to use this with the jQuery’s .on/.off methods (for example to have namespacing and other jQuery event extensions). The following function, taken form the webshim library, should become usefull:

$.createEventCapturing = (function () {
    var special = $.event.special;
    return function (names) {
        if (!document.addEventListener) {
        if (typeof names == 'string') {
            names = [names];
        $.each(names, function (i, name) {
            var handler = function (e) {
                e = $.event.fix(e);

                return $, e);
            special[name] = special[name] || {};
            if (special[name].setup || special[name].teardown) {
            $.extend(special[name], {
                setup: function () {
                    this.addEventListener(name, handler, true);
                teardown: function () {
                    this.removeEventListener(name, handler, true);


$.createEventCapturing(['play', 'pause']);

$(document).on('play', function(e){
    $('audio, video').not({