diff --git a/src/js/events.js b/src/js/events.js index d581c99e5..fb9ee2972 100644 --- a/src/js/events.js +++ b/src/js/events.js @@ -277,8 +277,9 @@ vjs.trigger = function(elem, event) { elemData.dispatcher.call(elem, event); } - // Unless explicitly stopped, recursively calls this function to bubble the event up the DOM. - if (parent && !event.isPropagationStopped()) { + // Unless explicitly stopped or the event does not bubble (e.g. media events) + // recursively calls this function to bubble the event up the DOM. + if (parent && !event.isPropagationStopped() && event.bubbles !== false) { vjs.trigger(parent, event); // If at the top of the DOM, triggers the default action unless disabled. diff --git a/test/unit/events.js b/test/unit/events.js index 633027e26..60bf91878 100644 --- a/test/unit/events.js +++ b/test/unit/events.js @@ -89,3 +89,28 @@ test('should stop immediate propagtion', function(){ vjs.trigger(el, 'test'); }); + +test('should bubble up DOM unless bubbles == false', function(){ + expect(3); + + var outer = document.createElement('div'); + var inner = outer.appendChild(document.createElement('div')); + + // Verify that if bubbles === true, event bubbles up dom. + vjs.on(inner, 'bubbles', function(e){ + ok(true, 'Inner listener fired'); + }); + vjs.on(outer, 'bubbles', function(e){ + ok(true, 'Outer listener fired'); + }); + vjs.trigger(inner, { type:'bubbles', target:inner, bubbles:true }); + + // Only change 'bubbles' to false, and verify only inner handler is called. + vjs.on(inner, 'nobub', function(e){ + ok(true, 'Inner listener fired'); + }); + vjs.on(outer, 'nobub', function(e){ + ok(false, 'Outer listener fired'); + }); + vjs.trigger(inner, { type:'nobub', target:inner, bubbles:false }); +});