aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorSam Potts <sam@potts.es>2020-02-09 21:42:12 +0000
committerSam Potts <sam@potts.es>2020-02-09 21:42:12 +0000
commit2d13ad3d397423e527caa6f3eba9df242505dfff (patch)
tree12de1aa02dbe2d14c93ba9ad6070cb4611f6715d /src
parent74ba6a96fc5df9654766ac21593d5c8274d98fb2 (diff)
downloadplyr-2d13ad3d397423e527caa6f3eba9df242505dfff.tar.lz
plyr-2d13ad3d397423e527caa6f3eba9df242505dfff.tar.xz
plyr-2d13ad3d397423e527caa6f3eba9df242505dfff.zip
Focus trap improvements
Diffstat (limited to 'src')
-rw-r--r--src/js/fullscreen.js173
-rw-r--r--src/js/utils/elements.js35
2 files changed, 99 insertions, 109 deletions
diff --git a/src/js/fullscreen.js b/src/js/fullscreen.js
index 7ae3ff17..c74b3406 100644
--- a/src/js/fullscreen.js
+++ b/src/js/fullscreen.js
@@ -5,79 +5,10 @@
// ==========================================================================
import browser from './utils/browser';
-import { hasClass, toggleClass, trapFocus } from './utils/elements';
+import { getElements, hasClass, toggleClass } from './utils/elements';
import { on, triggerEvent } from './utils/events';
import is from './utils/is';
-function onChange() {
- if (!this.enabled) {
- return;
- }
-
- // Update toggle button
- const button = this.player.elements.buttons.fullscreen;
- if (is.element(button)) {
- button.pressed = this.active;
- }
-
- // Trigger an event
- triggerEvent.call(this.player, this.target, this.active ? 'enterfullscreen' : 'exitfullscreen', true);
-
- // Trap focus in container
- if (!browser.isIos) {
- trapFocus.call(this.player, this.target, this.active);
- }
-}
-
-function toggleFallback(toggle = false) {
- // Store or restore scroll position
- if (toggle) {
- this.scrollPosition = {
- x: window.scrollX || 0,
- y: window.scrollY || 0,
- };
- } else {
- window.scrollTo(this.scrollPosition.x, this.scrollPosition.y);
- }
-
- // Toggle scroll
- document.body.style.overflow = toggle ? 'hidden' : '';
-
- // Toggle class hook
- toggleClass(this.target, this.player.config.classNames.fullscreen.fallback, toggle);
-
- // Force full viewport on iPhone X+
- if (browser.isIos) {
- let viewport = document.head.querySelector('meta[name="viewport"]');
- const property = 'viewport-fit=cover';
-
- // Inject the viewport meta if required
- if (!viewport) {
- viewport = document.createElement('meta');
- viewport.setAttribute('name', 'viewport');
- }
-
- // Check if the property already exists
- const hasProperty = is.string(viewport.content) && viewport.content.includes(property);
-
- if (toggle) {
- this.cleanupViewport = !hasProperty;
-
- if (!hasProperty) {
- viewport.content += `,${property}`;
- }
- } else if (this.cleanupViewport) {
- viewport.content = viewport.content
- .split(',')
- .filter(part => part.trim() !== property)
- .join(',');
- }
- }
-
- // Toggle button and fire events
- onChange.call(this);
-}
-
class Fullscreen {
constructor(player) {
// Keep reference to parent
@@ -101,7 +32,7 @@ class Fullscreen {
this.prefix === 'ms' ? 'MSFullscreenChange' : `${this.prefix}fullscreenchange`,
() => {
// TODO: Filter for target??
- onChange.call(this);
+ this.onChange();
},
);
@@ -115,6 +46,9 @@ class Fullscreen {
this.toggle();
});
+ // Tap focus when in fullscreen
+ on.call(this, this.player.elements.container, 'keydown', event => this.trapFocus(event));
+
// Update the UI
this.update();
}
@@ -194,6 +128,97 @@ class Fullscreen {
: this.player.elements.container;
}
+ onChange() {
+ if (!this.enabled) {
+ return;
+ }
+
+ // Update toggle button
+ const button = this.player.elements.buttons.fullscreen;
+ if (is.element(button)) {
+ button.pressed = this.active;
+ }
+
+ // Trigger an event
+ triggerEvent.call(this.player, this.target, this.active ? 'enterfullscreen' : 'exitfullscreen', true);
+ }
+
+ toggleFallback(toggle = false) {
+ // Store or restore scroll position
+ if (toggle) {
+ this.scrollPosition = {
+ x: window.scrollX || 0,
+ y: window.scrollY || 0,
+ };
+ } else {
+ window.scrollTo(this.scrollPosition.x, this.scrollPosition.y);
+ }
+
+ // Toggle scroll
+ document.body.style.overflow = toggle ? 'hidden' : '';
+
+ // Toggle class hook
+ toggleClass(this.target, this.player.config.classNames.fullscreen.fallback, toggle);
+
+ // Force full viewport on iPhone X+
+ if (browser.isIos) {
+ let viewport = document.head.querySelector('meta[name="viewport"]');
+ const property = 'viewport-fit=cover';
+
+ // Inject the viewport meta if required
+ if (!viewport) {
+ viewport = document.createElement('meta');
+ viewport.setAttribute('name', 'viewport');
+ }
+
+ // Check if the property already exists
+ const hasProperty = is.string(viewport.content) && viewport.content.includes(property);
+
+ if (toggle) {
+ this.cleanupViewport = !hasProperty;
+
+ if (!hasProperty) {
+ viewport.content += `,${property}`;
+ }
+ } else if (this.cleanupViewport) {
+ viewport.content = viewport.content
+ .split(',')
+ .filter(part => part.trim() !== property)
+ .join(',');
+ }
+ }
+
+ // Toggle button and fire events
+ this.onChange();
+ }
+
+ // Trap focus inside container
+ trapFocus(event) {
+ // Bail if iOS, not active, not the tab key
+ if (browser.isIos || !this.active || event.key !== 'Tab' || event.keyCode !== 9) {
+ return;
+ }
+
+ // Get the current focused element
+ const focused = document.activeElement;
+ const focusable = getElements.call(
+ this.player,
+ 'a[href], button:not(:disabled), input:not(:disabled), [tabindex]',
+ );
+ const [first] = focusable;
+ const last = focusable[focusable.length - 1];
+
+ if (focused === last && !event.shiftKey) {
+ // Move focus to first element that can be tabbed if Shift isn't used
+ first.focus();
+ event.preventDefault();
+ } else if (focused === first && event.shiftKey) {
+ // Move focus to last element that can be tabbed if Shift is used
+ last.focus();
+ event.preventDefault();
+ }
+ }
+
// Update UI
update() {
if (this.enabled) {
@@ -226,9 +251,9 @@ class Fullscreen {
if (browser.isIos && this.player.config.fullscreen.iosNative) {
this.target.webkitEnterFullscreen();
} else if (!Fullscreen.native || this.forceFallback) {
- toggleFallback.call(this, true);
+ this.toggleFallback(true);
} else if (!this.prefix) {
- this.target.requestFullscreen({ navigationUI: "hide" });
+ this.target.requestFullscreen({ navigationUI: 'hide' });
} else if (!is.empty(this.prefix)) {
this.target[`${this.prefix}Request${this.property}`]();
}
@@ -245,7 +270,7 @@ class Fullscreen {
this.target.webkitExitFullscreen();
this.player.play();
} else if (!Fullscreen.native || this.forceFallback) {
- toggleFallback.call(this, false);
+ this.toggleFallback(false);
} else if (!this.prefix) {
(document.cancelFullScreen || document.exitFullscreen).call(document);
} else if (!is.empty(this.prefix)) {
diff --git a/src/js/utils/elements.js b/src/js/utils/elements.js
index 921d533a..b88aad0c 100644
--- a/src/js/utils/elements.js
+++ b/src/js/utils/elements.js
@@ -2,7 +2,6 @@
// Element utils
// ==========================================================================
-import { toggleListener } from './events';
import is from './is';
import { extend } from './objects';
@@ -248,40 +247,6 @@ export function getElement(selector) {
return this.elements.container.querySelector(selector);
}
-// Trap focus inside container
-export function trapFocus(element = null, toggle = false) {
- if (!is.element(element)) {
- return;
- }
-
- const focusable = getElements.call(this, 'button:not(:disabled), input:not(:disabled), [tabindex]');
- const first = focusable[0];
- const last = focusable[focusable.length - 1];
- const player = this;
-
- const trap = event => {
- // Bail if not tab key or not fullscreen
- if (event.key !== 'Tab' || event.keyCode !== 9 || !player.fullscreen.active) {
- return;
- }
-
- // Get the current focused element
- const focused = document.activeElement;
-
- if (focused === last && !event.shiftKey) {
- // Move focus to first element that can be tabbed if Shift isn't used
- first.focus();
- event.preventDefault();
- } else if (focused === first && event.shiftKey) {
- // Move focus to last element that can be tabbed if Shift is used
- last.focus();
- event.preventDefault();
- }
- };
-
- toggleListener.call(this, this.elements.container, 'keydown', trap, toggle, false);
-}
-
// Set focus and tab focus class
export function setFocus(element = null, tabFocus = false) {
if (!is.element(element)) {