PageSourceSearch

https://www.porsche-se.com/typo3temp/assets/compressed/merged-a947…8d29cd8646053759dd01a2334ac23.js?1789972931

js porsche-se.com collected 2026-10-02 10:22:43 UTC 8,577 bytes, 223 lines download raw bytes

1
2/**
3 * Focus Trap
4 *
5 * This script provides a focus trap functionality for modal dialogs.
6 *
7 * @usage
8 *   To use this focus trap, call `focusTrap.init()` with the selector of the modal dialog element.
9 *   You can also provide an optional selector for a close button within the modal dialog.
10 *   This will ensure that when the modal dialog is open, the focus remains within the dialog,
11 *   cycling through the focusable elements and preventing focus from leaving the dialog.
12 *
13 *   Example 01:
14 *   focusTrap.init('#myModal', '.close-button');
15 *     This will initialize the focus trap for the modal dialog with the ID `myModal`
16 *     and the close button with the class `close-button`.
17 *
18 *   Example 02:
19 *   focusTrap.init('#myModal');
20 *     Init the focus trap for the modal dialog with the ID `myModal`
21 *     without a close button.
22 *   gallery.listen('destroy', focusTrap.destroy);
23 *     This will destroy the focus trap when the modal dialog is closed.
24 *     Keep in mind, that there are a lot of different modal dialog implementations,
25 *     so you might need to adapt the event registration.
26 *     This example is for PhotoSwipe.
27 *
28 *   Example 03:
29 *   focusTrap.init('#headerSearch');
30 *     This will initialize the focus trap for the modal dialog with the ID `headerSearch`.
31 *   focusTrap.setEscapeKeyListener(function (event) {
32 *       $headersearch.collapse('hide');
33 *   });
34 *     This will set a custom listener for the Escape key,
35 *     which will collapse the header search when the Escape key is pressed.
36 *
37 *  Example 04:
38 *  focusTrap.updateFocusableElements();
39 *    This will update the list of focusable elements within the modal dialog.
40 *    Normally, you do not need to call this function, but it can be useful if the modal dialog content changes
41 *    dynamically or if new focusable elements are added or removed.
42 *
43 */
44(function () {
45    'use strict';
46
47    const focusTrap = {};
48
49    focusTrap.modalElement = null;
50    focusTrap.focusableElements = null;
51    focusTrap.firstElement = null;
52    focusTrap.lastElement = null;
53    focusTrap.closeButton = null;
54    focusTrap.escapeKeyListener = function () {};
55    focusTrap.element = null;
56
57
58    /**
59     * Initializes the focus trap for a modal dialog.
60     *
61     * This function sets up the focus trap by
62     * finding all focusable elements within the modal dialog
63     * and adding an event listener for the keydown event.
64     * It ensures that when the Tab key is pressed,
65     * the focus remains within the modal dialog,
66     * cycling through the focusable elements.
67     *
68     * @param {String} elementSelector
69     *   The selector for the modal dialog element.
70     *   This should be a valid CSS selector
71     *   that matches the modal dialog you want to trap focus within.
72     * @param {String} closeButtonSelector
73     *   Optional selector for a close button within the modal dialog.
74     *   If provided, clicking this button will destroy the focus trap.
75     *   If you do not provide a close button selector,
76     *   you will have to call `focusTrap.destroy()` manually.
77     *
78     * @returns {void}
79     */
80    focusTrap.init = function (elementSelector, closeButtonSelector) {
81        focusTrap.modalElement = document.querySelector(elementSelector);
82        focusTrap.modalElement.addEventListener('keydown', focusTrap.handleTabKeyPress);
83        focusTrap.updateFocusableElements();
84
85        if (closeButtonSelector) {
86            focusTrap.closeButton = focusTrap.modalElement.querySelector(closeButtonSelector);
87            if (focusTrap.closeButton) {
88                focusTrap.closeButton.addEventListener('click', focusTrap.destroy);
89                focusTrap.closeButton.focus();
90            }
91        }
92
93        if (focusTrap.closeButton) {
94            focusTrap.closeButton.focus();
95        } else {
96            focusTrap.firstElement.focus();
97        }
98    }
99
100    /**
101     * Updates the list of focusable elements within the modal dialog.
102     *
103     * This function scans the modal dialog for all focusable elements and updates the `focusableElements`,
104     * `firstElement`, and `lastElement` properties.
105     * It should be called whenever the modal dialog content changes or when the focusable elements need to be refreshed.
106     * This is useful if the modal dialog content is dynamic or if new focu
106sable elements are added or removed.
107     *
108     * @returns {void}
109     */
110    focusTrap.updateFocusableElements = function () {
111        focusTrap.focusableElements = [];
112        let el, parentEl, isFocusable;
113        let possibleElements = focusTrap.modalElement.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
114
115        for (let i = 0; i < possibleElements.length; i++) {
116            el = possibleElements[i];
117            parentEl = el;
118            isFocusable = true;
119            while (parentEl) {
120                // If the element is inside a hidden parent, skip it
121                if (
122                    window.getComputedStyle(parentEl, null).display === 'none'
123                    || parentEl.style.display === 'none'
124                    || parentEl.disabled
125                    || window.getComputedStyle(parentEl, null).visibility === 'hidden'
126                    || parentEl.style.visibility === 'hidden'
127                ) {
128                    isFocusable = false;
129                    break;
130                }
131                parentEl = parentEl.parentElement;
132            }
133            // Check if the element is visible and not disabled
134            if (isFocusable) {
135                focusTrap.focusableElements.push(el);
136            }
137        }
138
139        focusTrap.firstElement = focusTrap.focusableElements[0];
140        focusTrap.lastElement = focusTrap.focusableElements[focusTrap.focusableElements.length - 1];
141    }
142
143    /**
144     * Destroys the focus trap by removing the event listener and resetting the focus trap properties.
145     *
146     * This function should be called when the modal dialog is closed
147     * to clean up the focus trap state.
148     */
149    focusTrap.destroy = function () {
150        if (focusTrap.modalElement) {
151            focusTrap.modalElement.removeEventListener('keydown', focusTrap.handleTabKeyPress);
152        }
153        if (focusTrap.closeButton) {
154            focusTrap.closeButton.removeEventListener('click', focusTrap.destroy);
155        }
156
157        focusTrap.closeButton = null;
158        focusTrap.modalElement = null;
159        focusTrap.focusableElements = null;
160        focusTrap.firstElement = null;
161        focusTrap.lastElement = null;
162        focusTrap.element = null;
163        focusTrap.escapeKeyListener = function () {};
164    }
165
166    /**
167     * Handles the Tab key press to trap focus within the modal dialog.
168     *
169     * This function prevents the focus from leaving the modal dialog
170     * and cycles through the focusable elements.
171     *
172     * @param {KeyboardEvent} event - The keydown event triggered by pressing the Tab key.
173     *
174     * @returns {void}
175     */
176    focusTrap.handleTabKeyPress = function (event) {
177        focusTrap.updateFocusableElements();
178
179        if (event.key === 'Tab') {
180            let activeElem = document.activeElement;
181
182            if (event.shiftKey && activeElem.isEqualNode(focusTrap.firstElement)) {
183                event.preventDefault();
184                focusTrap.lastElement.focus();
185                return;
186            }
187
188            if (!event.shiftKey && activeElem.isEqualNode(focusTrap.lastElement)) {
189                event.preventDefault();
190                focusTrap.firstElement.focus();
191            }
192        }
193
194        if (event.key === 'Escape') {
195            event.preventDefault();
196            focusTrap.escapeKeyListener(event);
197        }
198    }
199
200    /**
201     * Sets a custom listener for the Escape key.
202     *
203     * This function allows you to define a custom action
204     * when the Escape key is pressed while the focus trap is active.
205     * You can use this to close the modal dialog or perform any other action.
206     *
207     * @param {Function} callback
208     *   The callback function to be executed when the Escape key is pressed.
209     *   Takes the {KeyboardEvent} event as a parameter.
210     *
211     * @returns {void}
212     */
213    focusTrap.setEscapeKeyListener = function (callback) {
214        if (typeof callback !== 'function') {
215            throw new Error('focusTrap.setEscapeKeyListener expects a function as an argument.');
216        }
217
218        focusTrap.escapeKeyListener = callback;
219    };
220
221    // Export the focusTrap object
222    window.focusTrap = focusTrap;
223})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.