|
19 | 19 |
|
20 | 20 | // --------------------------------------------------------------------------- |
21 | 21 | // Shared state |
| 22 | + // |
| 23 | + // NOTE on hoisting: prefersReducedMotion and REDUCED_MOTION need to be |
| 24 | + // available the moment boot() runs. boot() is called from line ~17 (in the |
| 25 | + // readyState branch above) BEFORE this section is executed top-to-bottom. |
| 26 | + // Function declarations (function foo() {}) are fully hoisted; var |
| 27 | + // assignments (var foo = function () {}) are not — only the `var foo` |
| 28 | + // hoists, the value stays undefined until the line is executed. So: |
| 29 | + // - prefersReducedMotion uses `function` declaration form (hoisted). |
| 30 | + // - REDUCED_MOTION is wrapped in a memoised lazy getter so the actual |
| 31 | + // matchMedia call happens at first use (after the IIFE has finished |
| 32 | + // initialising), not at the top-of-file evaluation order. |
22 | 33 | // --------------------------------------------------------------------------- |
23 | | - var REDUCED_MOTION = window.matchMedia |
24 | | - ? window.matchMedia('(prefers-reduced-motion: reduce)') |
25 | | - : { matches: false, addEventListener: function () {}, addListener: function () {} }; |
| 34 | + var _REDUCED_MOTION = null; |
| 35 | + function getReducedMotion() { |
| 36 | + if (_REDUCED_MOTION) return _REDUCED_MOTION; |
| 37 | + _REDUCED_MOTION = window.matchMedia |
| 38 | + ? window.matchMedia('(prefers-reduced-motion: reduce)') |
| 39 | + : { matches: false, addEventListener: function () {}, addListener: function () {} }; |
| 40 | + return _REDUCED_MOTION; |
| 41 | + } |
26 | 42 |
|
27 | | - var prefersReducedMotion = function () { return !!REDUCED_MOTION.matches; }; |
| 43 | + function prefersReducedMotion() { return !!getReducedMotion().matches; } |
28 | 44 |
|
29 | 45 | // Cross-browser "media query change" subscription. Returns an unsubscribe fn. |
30 | 46 | function onMotionChange(handler) { |
31 | | - if (REDUCED_MOTION.addEventListener) { |
32 | | - REDUCED_MOTION.addEventListener('change', handler); |
33 | | - return function () { REDUCED_MOTION.removeEventListener('change', handler); }; |
| 47 | + var mql = getReducedMotion(); |
| 48 | + if (mql.addEventListener) { |
| 49 | + mql.addEventListener('change', handler); |
| 50 | + return function () { mql.removeEventListener('change', handler); }; |
34 | 51 | } |
35 | | - if (REDUCED_MOTION.addListener) { |
36 | | - REDUCED_MOTION.addListener(handler); |
37 | | - return function () { REDUCED_MOTION.removeListener(handler); }; |
| 52 | + if (mql.addListener) { |
| 53 | + mql.addListener(handler); |
| 54 | + return function () { mql.removeListener(handler); }; |
38 | 55 | } |
39 | 56 | return function () {}; |
40 | 57 | } |
|
0 commit comments