6

I have seen similar questions - but not that fix my problem!

I have audio on my page and when one ends, I want the next to start, but I can't even get the ended to trigger...

I cut the code down to this:

function DaisyChainAudio() {
    $().on('ended', 'audio','' ,function () {
        alert('done');
    });
}

This is called from my page/code (and is executed, setting a break point shows that).

As far as I understand this should set the handler at the document level, so any 'ended' events from any 'audio' tag (even if added dynamically) should be trapped and show me that alert...

But it never fires.

edit

With some borrowing from Çağatay Gürtürk's suggestion so far have this...

function DaisyChainAudio() {
        $(function () {
        $('audio').on('ended', function (e) {
            $(e.target).load();
            var next = $(e.target).nextAll('audio');
            if (!next.length) next = $(e.target).parent().nextAll().find('audio');
            if (!next.length) next = $(e.target).parent().parent().nextAll().find('audio');
            if (next.length) $(next[0]).trigger('play');
        });
    });
}

I'd still like to set this at the document level so I don't need to worry about adding it when dynamic elements are added...

pperrin
  • 1,487
  • 15
  • 33
  • Are the audio tags already available when you call `.on()`? jquery.com says: "Event handlers are bound only to the currently selected elements; they must exist at the time your code makes the call to .on()." – dsuckau Apr 19 '15 at 11:02
  • As it happens they are. But as I understand it $().on will attach the events to the document root - so any ended (even from dynamically added tags) will bubble up and be caught. – pperrin Apr 19 '15 at 13:20

2 Answers2

3

Try this:

$('audio').on('ended', function (e) {
        alert('done');
        var endedTag=e.target; //this gives the ended audio, so you can find the next one and play it.
});

Note that when you create a new audio dynamically, you should assign the events. A quick and dirty solution would be:

function bindEvents(){
$('audio').off('ended').on('ended', function (e) {
            alert('done');
            var endedTag=e.target; //this gives the ended audio, so you can find the next one and play it.
    });
}

and run bindEvents whenever you create/delete an audio element.

Cagatay Gurturk
  • 7,186
  • 3
  • 34
  • 44
  • Hi do you know why it can't be set at the document level like other events? So far I have managed to keep the 'display' and 'behaviour' separate. All the 'behaviour' being set at the document level before the dynamic stuff is added. – pperrin Apr 19 '15 at 17:17
  • try $(document).on('ended', 'audio', function (e) { alert('done'); }); but document-level events are not so good as far as i know. I always use the method that i wrote in my answer. – Cagatay Gurturk Apr 19 '15 at 22:29
  • Thanks - $().on has worked for everything else - maybe audio has issues... but I used your suggestion so have a tick :) – pperrin Apr 19 '15 at 23:22
3

The reason it does not fire is, media events( those specifically belonging to audio or video like play, pause, timeupdate, etc) do not get bubbled. you can find the explanation for that in the answer to this question.

So using their solution, I captured the ended event, and this would allow setting triggers for dynamically added audio elements.

$.createEventCapturing(['ended']);  // add all the triggers for which you like to catch.
$('body').on('ended', 'audio', onEnded); // now this would work.

JSFiddle demo

the code for event capturing( taken from the other SO answer):

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

                    return $.event.dispatch.call(this, e);
                };
                special[name] = special[name] || {};
                if (special[name].setup || special[name].teardown) {
                    return;
                }
                $.extend(special[name], {
                    setup: function () {
                        this.addEventListener(name, handler, true);
                    },
                    teardown: function () {
                        this.removeEventListener(name, handler, true);
                    }
                });
            });
        };
    })();
Community
  • 1
  • 1
mido
  • 24,198
  • 15
  • 92
  • 117