Focus outside the player (e.g. click anywhere in the page) and press a hotkey (e.g. space bar).
+ The player hotkey should trigger and perform the action.
+
By setting globalHotkeys: true extends the normal hotkey behavior at the global level. Therefore
+ hotkeys: true also need to be set.
+
Note that if the focus is on an interactive element in the page, the global hotkey behavior would not trigger.
+
+
+
+
Player with default hotkeys
+
+
+
+
+
+
+
Player with 'x' button linked to play/pause
+
+
+
+
+
+
+
+
+
+
+
Test Interactive Area
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
This is a sample paragraph with some interactive content.
+
+
+
+
+
Item 1
+
Item 2
+
Item 3
+
+
+
+
+
+
+
+
+
diff --git a/src/js/player.js b/src/js/player.js
index 0345f93e5a..b298c6c59e 100644
--- a/src/js/player.js
+++ b/src/js/player.js
@@ -374,6 +374,8 @@ class Player extends Component {
this.boundUpdatePlayerHeightOnAudioOnlyMode_ = (e) => this.updatePlayerHeightOnAudioOnlyMode_(e);
+ this.boundGlobalKeydown_ = (e) => this.handleGlobalKeydown_(e);
+
// default isFullscreen_ to false
this.isFullscreen_ = false;
@@ -615,6 +617,10 @@ class Player extends Component {
this.on('keydown', (e) => this.handleKeyDown(e));
this.on('languagechange', (e) => this.handleLanguagechange(e));
+ if (this.isGlobalHotKeysEnabled()) {
+ Events.on(document.body, 'keydown', this.boundGlobalKeydown_);
+ }
+
this.breakpoints(this.options_.breakpoints);
this.responsive(this.options_.responsive);
@@ -650,6 +656,7 @@ class Player extends Component {
// Make sure all player-specific document listeners are unbound. This is
Events.off(document, this.fsApi_.fullscreenchange, this.boundDocumentFullscreenChange_);
Events.off(document, 'keydown', this.boundFullWindowOnEscKey_);
+ Events.off(document.body, 'keydown', this.boundGlobalKeydown_);
if (this.styleEl_ && this.styleEl_.parentNode) {
this.styleEl_.parentNode.removeChild(this.styleEl_);
@@ -2251,6 +2258,12 @@ class Player extends Component {
this.trigger('textdata', data);
}
+ handleGlobalKeydown_(event) {
+ if (event.target === document.body) {
+ this.handleKeyDown(event);
+ }
+ }
+
/**
* Get object for cached values.
*
@@ -4574,6 +4587,10 @@ class Player extends Component {
this.height(this.audioOnlyCache_.controlBarHeight);
}
+ isGlobalHotKeysEnabled() {
+ return !!(this.options_ && this.options_.userActions && this.options_.userActions.globalHotkeys);
+ }
+
enableAudioOnlyUI_() {
// Update styling immediately to show the control bar so we can get its height
this.addClass('vjs-audio-only-mode');
diff --git a/test/unit/player-user-actions.test.js b/test/unit/player-user-actions.test.js
index d639434edc..35a5614e2a 100644
--- a/test/unit/player-user-actions.test.js
+++ b/test/unit/player-user-actions.test.js
@@ -639,3 +639,58 @@ QUnit.test('hotkeys are NOT ignored when focus is on a button input', function(a
defaultKeyTests.mute(this.player, assert, true);
defaultKeyTests.playPause(this.player, assert, true);
});
+
+QUnit.module('Player: User Actions: Global Hotkeys', {
+
+ beforeEach() {
+ this.clock = sinon.useFakeTimers();
+ this.player = TestHelpers.makePlayer();
+ },
+
+ afterEach() {
+ this.player.dispose();
+ this.clock.restore();
+ }
+});
+
+QUnit.test('when userActions.globalHotkeys is true, hotkeys are enabled at document.body level', function(assert) {
+ this.player.dispose();
+ this.player = TestHelpers.makePlayer({
+ controls: true,
+ userActions: {
+ globalHotkeys: true,
+ hotkeys: true
+ }
+ });
+
+ this.player.requestFullscreen = sinon.spy();
+
+ const event = new KeyboardEvent('keydown', { // eslint-disable-line no-undef
+ key: 'f'
+ });
+
+ document.body.dispatchEvent(event);
+
+ assert.strictEqual(this.player.requestFullscreen.callCount, 1, 'has gone fullscreen');
+});
+
+QUnit.test('when userActions.globalHotkeys is NOT true, hotkeys are NOT enabled at document.body level', function(assert) {
+ this.player.dispose();
+ this.player = TestHelpers.makePlayer({
+ controls: true,
+ userActions: {
+ globalHotkeys: false,
+ hotkeys: true
+ }
+ });
+
+ this.player.requestFullscreen = sinon.spy();
+
+ const event = new KeyboardEvent('keydown', { // eslint-disable-line no-undef
+ key: 'f'
+ });
+
+ document.body.dispatchEvent(event);
+
+ assert.strictEqual(this.player.requestFullscreen.callCount, 0, 'has not gone fullscreen');
+});