If you dynamically add a <video> to your page that is not in the HTML message from the server, you may encounter a race condition that may result in the event ended not being recognized - even if it was added correctly.
I use knockoutjs to add another template for phone and desktop.
<div data-bind="template: { name: videoTemplate, afterRender: initializeVideo }">
This dynamically creates a <video > element for me, and then calls initializeVideo() after rendering the template (added to the DOM) to bind events:
$('video#flexVideo').off('ended').on('ended', (evt) => { alert('ended fired'); }).css('border', '2px solid yellow'); // add yellow border so we know element existed
On my screen, the video gets a yellow frame (confirmation that the video was in the DOM and there were no typos). However, for some reason, the browser still cannot attach the ended event to it - I assume that it is not yet initialized.
Chrome debugging tools show that they have been added, but in fact it does not work!

He does this in Firefox, Chrome + IE10, which was a bit surprising.
One solution:
$('video#flexVideo').off('loadstart').on('loadstart', (evt) => { $('video#flexVideo').off('ended').on('ended', (evt) => { alert('ended fired'); }).css('border', '2px solid yellow'); }).css('border', '2px solid orange');
The loadstart event seems to be bound immediately.
Another, maybe just setTimeout - or just binds an event to a game.