Proyectos de Subversion Moodle

Rev

Rev 1 | Mostrar el archivo completo | | | Autoría | Ultima modificación | Ver Log |

Rev 1 Rev 1441
Línea 44... Línea 44...
44
 * user-initiated focus change, then instead jump to the last element in the lock region.
44
 * user-initiated focus change, then instead jump to the last element in the lock region.
45
 *
45
 *
46
 * This gives us a solution which supports focus locking of any kind, which loops in both directions, and which
46
 * This gives us a solution which supports focus locking of any kind, which loops in both directions, and which
47
 * prevents the lock from escaping the modal entirely.
47
 * prevents the lock from escaping the modal entirely.
48
 *
48
 *
-
 
49
 * If no event is supplied then this function can be used to focus the first element in the lock region, or the
-
 
50
 * last element if the first element is already focused.
-
 
51
 *
49
 * @method
52
 * @method
50
 * @param {Event} event The event from the focus change
53
 * @param {Event} [event] The event from the focus change
51
 */
54
 */
52
const lockHandler = event => {
55
const lockHandler = event => {
53
    if (ignoreFocusChanges) {
56
    if (ignoreFocusChanges) {
54
        // The focus change was made by an internal call to set focus.
57
        // The focus change was made by an internal call to set focus.
55
        return;
58
        return;
Línea 69... Línea 72...
69
    }
72
    }
70
    if (!lockRegion) {
73
    if (!lockRegion) {
71
        return;
74
        return;
72
    }
75
    }
Línea 73... Línea 76...
73
 
76
 
74
    if (lockRegion.contains(event.target)) {
77
    if (event && lockRegion.contains(event.target)) {
75
        lastFocus = event.target;
78
        lastFocus = event.target;
76
    } else {
79
    } else {
77
        focusFirstDescendant();
80
        focusFirstDescendant();
78
        if (lastFocus == document.activeElement) {
81
        if (lastFocus == document.activeElement) {
Línea 81... Línea 84...
81
        lastFocus = document.activeElement;
84
        lastFocus = document.activeElement;
82
    }
85
    }
83
};
86
};
Línea 84... Línea 87...
84
 
87
 
-
 
88
/**
-
 
89
 * Gets all the focusable elements in the document that are not set to display:none. This is useful
-
 
90
 * because sometimes, a nested modal dialog may be left in the DOM but set to display:none, and you
-
 
91
 * can't actually focus display:none elements.
-
 
92
 *
-
 
93
 * @returns {HTMLElement[]} All focusable elements that aren't display:none, in DOM order
-
 
94
 */
-
 
95
const getAllFocusableElements = () => {
-
 
96
    const allFocusable = document.querySelectorAll(Selectors.elements.focusable);
-
 
97
    // The offsetParent check is a well-perfoming way to ensure that an element in the document
-
 
98
    // does not have display:none.
-
 
99
    return Array.from(allFocusable).filter(focusable => !!focusable.offsetParent);
-
 
100
};
-
 
101
 
-
 
102
/**
-
 
103
 * Catch event for any keydown during focus lock.
-
 
104
 *
-
 
105
 * This is used to detect situations when the user would be tabbing out to the browser UI. In that
-
 
106
 * case, no 'focus' event is generated, so we need to trap it before it happens via the keydown
-
 
107
 * event.
-
 
108
 *
-
 
109
 * @param {KeyboardEvent} event
-
 
110
 */
-
 
111
const keyDownHandler = event => {
-
 
112
    // We only care about Tab keypresses and only if there is a current lock region.
-
 
113
    if (event.key !== 'Tab' || !getCurrentLockRegion()) {
-
 
114
        return;
-
 
115
    }
-
 
116
 
-
 
117
    if (!event.shiftKey) {
-
 
118
        // Have they already focused the last focusable element in the document?
-
 
119
        const allFocusable = getAllFocusableElements();
-
 
120
        if (document.activeElement === allFocusable[allFocusable.length - 1]) {
-
 
121
            // When the last thing is focused, focus would go to browser UI next, instead use
-
 
122
            // lockHandler to put focus back on the first element in lock region.
-
 
123
            lockHandler();
-
 
124
            event.preventDefault();
-
 
125
        }
-
 
126
    } else {
-
 
127
        // Have they already focused the first focusable element in the lock region?
-
 
128
        const lockRegion = getCurrentLockRegion();
-
 
129
        const firstFocusable = lockRegion.querySelector(Selectors.elements.focusable);
-
 
130
        if (document.activeElement === firstFocusable) {
-
 
131
            // When the first thing is focused, use lockHandler which will focus the last element
-
 
132
            // in lock region. We do this here rather than using lockHandler to get the focus event
-
 
133
            // because (a) there would be no focus event if the current element is the first in
-
 
134
            // document, and (b) temporarily focusing outside the region could result in unexpected
-
 
135
            // scrolling.
-
 
136
            lockHandler();
-
 
137
            event.preventDefault();
-
 
138
        }
-
 
139
    }
-
 
140
};
-
 
141
 
85
/**
142
/**
86
 * Focus the first descendant of the current lock region.
143
 * Focus the first descendant of the current lock region.
87
 *
144
 *
88
 * @method
145
 * @method
89
 * @returns {Bool} Whether a node was focused
146
 * @returns {Bool} Whether a node was focused
Línea 274... Línea 331...
274
    addLockRegionToStack(newLockRegion);
331
    addLockRegionToStack(newLockRegion);
Línea 275... Línea 332...
275
 
332
 
276
    if (!isLocked) {
333
    if (!isLocked) {
277
        // Add the focus handler.
334
        // Add the focus handler.
-
 
335
        document.addEventListener('focus', lockHandler, true);
278
        document.addEventListener('focus', lockHandler, true);
336
        document.addEventListener('keydown', keyDownHandler, true);
Línea 279... Línea 337...
279
    }
337
    }
280
 
338
 
281
    // Attempt to focus on the first item in the lock region.
339
    // Attempt to focus on the first item in the lock region.
Línea 310... Línea 368...
310
        // The focus manager still has items in the stack.
368
        // The focus manager still has items in the stack.
311
        return;
369
        return;
312
    }
370
    }
Línea 313... Línea 371...
313
 
371
 
-
 
372
    document.removeEventListener('focus', lockHandler, true);
Línea 314... Línea 373...
314
    document.removeEventListener('focus', lockHandler, true);
373
    document.removeEventListener('keydown', keyDownHandler, true);
315
 
374
 
316
    lastFocus = null;
375
    lastFocus = null;
317
    ignoreFocusChanges = false;
376
    ignoreFocusChanges = false;