PageSourceSearch

https://adult-learning-derby.org.uk/media/derbyadultlearningservic…/javascript/jquery-accessible-tabs.js

js adult-learning-derby.org.uk collected 2026-09-28 12:41:48 UTC 14,567 bytes, 335 lines download raw bytes

vendor: 14,567 bytes, lines 1-335
1jQuery(document).ready(function($) {
2
3    /*
4     * jQuery Accessible tab panel system, using ARIA
5     * @version v1.6.1
6     * Website: https://a11y.nicolas-hoffmann.net/tabs/
7     * License MIT: https://github.com/nico3333fr/jquery-accessible-tabs-aria/blob/master/LICENSE
8     */
9    // Store current URL hash.
10    var hash = window.location.hash.replace("#", "");
11
12    /* Tabs ------------------------------------------------------------------------------------------------------------ */
13    var $tabs = $(".js-tabs"),
14        $body = $("body");
15
16    if ($tabs.length) {
17
18        var $tab_list = $tabs.find(".js-tablist");
19        $tab_list.each(function() {
20            var $this_tab_list = $(this),
21                options = $this_tab_list.data(),
22                $tabs_prefix_classes = typeof options.tabsPrefixClass !== 'undefined' ? options.tabsPrefixClass + '-' : '',
23                $hx = typeof options.hx !== 'undefined' ? options.hx : '',
24                $existing_hx = typeof options.existingHx !== 'undefined' ? options.existingHx : '',
25                $this_tab_list_items = $this_tab_list.children(".js-tablist__item"),
26                $this_tab_list_links = $this_tab_list.find(".js-tablist__link");
27
28            // roles init
29            $this_tab_list.attr("role", "tablist"); // ul        
30            $this_tab_list_items.attr("role", "presentation"); // li
31            $this_tab_list_links.attr("role", "tab"); // a
32
33            // classes init
34            $this_tab_list.addClass($tabs_prefix_classes + 'tabs__list');
35            $this_tab_list_items.addClass($tabs_prefix_classes + 'tabs__item');
36            $this_tab_list_links.addClass($tabs_prefix_classes + 'tabs__link');
37
38            // controls/tabindex attributes
39            $this_tab_list_links.each(function() {
40                var $this = $(this),
41                    $hx_generated_class = typeof options.tabsGeneratedHxClass !== 'undefined' ? options.tabsGeneratedHxClass : 'invisible',
42                    $href = $this.attr("href"),
43                    $controls = $($href),
44                    $text = $this.text();
45
46                if ($hx !== "") {
47                    $controls.prepend('<' + $hx + ' class="' + $hx_generated_class + '" tabindex="0">' + $text + '</' + $hx + '>');
48                }
49                if ($existing_hx !== "") {
50                    $controls.find($existing_hx + ':first-child').attr('tabindex', 0);
51                }
52                if (typeof $href !== "undefined" && $href !== "" && $href !== "#") {
53                    $this.attr({
54                        "aria-controls": $href.replace("#", ""),
55                        "tabindex": -1,
56                        "aria-selected": "false"
57                    });
58                }
59
60                $this.removeAttr("href");
61
62            });
63        });
64
65        /* Tabs content ---------------------------------------------------------------------------------------------------- */
66        $(".js-tabcontent").attr({
67                "role": "tabpanel", // contents
68                "aria-hidden": "true" // all hidden
69                //"tabindex": 0
70            })
71            .each(function() {
72                var $this = $(this),
73                    $this_id = $this.attr("id"),
74                    $prefix_attribute = $("#label_" + $this_id).closest('.js-tablist').attr('data-tabs-prefix-class'),
75                    $tabs_prefix_classes = typeof $prefix_attribute !== 'undefined' ? $prefix_attribute + '-' : '';
76                // label by link
77                $this.attr("aria-labelledby", "label_" + $this_id);
78
79                $this.addClass($tabs_prefix_classes + 'tabs__content');
80            });
81
82        // search if hash is ON not disabled tab
83        if (hash !== "" && $("#" + hash + ".js-tabcontent").length !== 0) {
84            if ($("#label_" + hash + ".js-tablist__link:not([aria-disabled='true'])").length) {
85                // display not disabled
86                $("#" + hash + ".js-tabcontent").removeAttr("aria-hidden");
87                // selection menu
88                $("#label_" + hash + ".js-tablist__link").attr({
89                    "aria-selected": "true",
90                    "tabindex": 0
91                });
92            }
93
94        }
95        // search if hash is IN not disabled tab
96        if (hash !== "" && $("#" + hash).parents('.js-tabcontent').length) {
97            var $this_hash = $("#" + hash),
98                $tab_content_parent = $this_hash.parents('.js-tabcontent'),
99                $tab_content_parent_id = $tab_content_parent.attr('id');
100
101            if ($("#label_" + $tab_content_parent_id + ".js-tablist__link:not([aria-disabled='true'])").length) {
102                $tab_content_parent.removeAttr("aria-hidden");
103                // selection menu
104                $("#label_" + $tab_content_parent_id + ".js-tablist__link").attr({
105                    "aria-selected": "true",
106                    "tabindex": 0
107                });
108            }
109        }
110
111        // search if data-selected="1" is on a not disabled tab for each tab system
112        $tabs.each(function() {
113            var $this = $(this),
114                $tab_selected = $this.find('.js-tablist__link[aria-selected="true"]'),
115                $tab_data_selected = $this.find('.js-tablist__link[data-selected="1"]:not([aria-disabled="true"]):first'),
116                $tab_data_selected_content = $('#' + $tab_data_selected.attr('aria-controls'));
117
118            if ($tab_selected.length === 0 && $tab_data_selected.length !== 0) {
119                $tab_data_selected.attr({
120                    "aria-selected": "true",
121                    "tabindex": 0
122                });
123                $tab_data_selected_content.removeAttr("aria-hidden");
124            }
125        });
126
127        // if no selected => select first not disabled
128        $tabs.each(function() {
129            var $this = $(this),
130                $tab_selected = $this.find('.js-tablist__link[aria-selected="true"]'),
131                $first_link = $this.find('.js-tablist__link:not([aria-disabled="true"]):first'),
132                $first_content = $('#' + $first_link.attr('aria-controls'));
133
134            if ($tab_selected.length === 0) {
135                $first_link.attr({
136                    "aria-selected": "true",
137                    "tabindex": 0
138                });
139                $first_content.removeAttr("aria-hidden");
140            }
141        });
142
143        /* Events ---------------------------------------------------------------------------------------------------------- */
144        /* click on a tab link */
145        $body.on("click", ".js-tablist__link[aria-disabled='true']", function() {
146            return false;
147        });
148        $body.on("click", ".js-tablist__link:not([aria-disabled='true'])", function(event) {
149                var $this = $(this),
150                    $hash_to_update = $this.attr("aria-controls"),
151                    $tab_content_linked = $("#" + $this.attr("aria-controls")),
152                    $parent = $this.closest(".js-tabs"),
153                    options = $parent.data(),
154                    tabs_disable_fragments = typeof options.tabsDisableFragment !== 'undefined' ? true : false,
155                    $all_tab_links = $parent.find(".js-tablist__link"),
156                    $all_tab_contents = $parent.find(".js-tabcontent");
157
158                // aria selected false on all links
159                $all_tab_links.attr({
160                    "tabindex": -1,
161                    "aria-selected": "false"
162                });
163
164                // add aria selected on $this
165                $this.attr({
166                    "aria-selected": "true",
167                    "tabindex": 0
168                });
169
170                // add aria-hidden on all tabs contents
171                $all_tab_contents.attr("aria-hidden", "true");
172
173                // remove aria-hidden on tab linked
174                $tab_content_linked.removeAttr("aria-hidden");
175
176                // add fragment (timeout for transitions)
177                if (tabs_disable_fragments === false) {
178                    setTimeout(function() {
179                        history.pushState(null, null, location.pathname + location.search + '#' + $hash_to_update)
180                    }, 1000);
181                }
182
183                event.preventDefault();
184            })
185            /* Key down in tabs */
186            .on("keydown", ".js-tablist", function(event) {
187
188                var $parent = $(this).closest('.js-tabs'),
189                    $activated = $parent.find('.js-tablist__link[aria-selected="true"]').parent(),
190                    $last_link = $parent.find('.js-tablist__item:last-child .js-tablist__link'),
191                    $first_link = $parent.find('.js-tablist__item:first-child .js-tablist__link'),
192                    $focus_on_tab_only = false,
193                    $prev = $activated,
194                    $next = $activated;
195
196                // search valid previous 
197                do {
198                    // if we are on first => activate last
199                    if ($prev.is(".js-tablist__item:first-child")) {
200                        $prev = $last_link.parent();
201                    }
202                    // else previous
203                    else {
204                        $prev = $prev.prev();
205                    }
206                }
207                while ($prev.children('.js-tablist__link').attr('aria-disabled') === 'true' && $prev !== $activated);
208
209                // search valid next
210                do {
211                    // if we are on last => activate first
212                    if ($next.is(".js-tablist__item:last-child")) {
213                        $next = $first_link.parent();
214                    }
215                    // else previous
216                    else {
217                        $next = $next.next();
218                    }
219                }
220                while ($next.children('.js-tablist__link').attr('aria-disabled') === 'true' && $next !== $activated);
221
222                // some event should be activated only if the focus is on tabs (not on tabpanel)
223                if ($(document.activeElement).is($parent.find('.js-tablist__link'))) {
224                    $focus_on_tab_only = true;
225                }
226
227                // catch keyboard event only if focus is on tab
228                if ($focus_on_tab_only && !event.ctrlKey) {
229                    // strike up or left in the tab
230                    if (event.keyCode == 37 || event.keyCode == 38) {
231
232                        $prev.children(".js-tablist__link").click().focus();
233
234                        event.preventDefault();
235                    }
236                    // strike down or right in the tab
237                    else if (event.keyCode == 40 || event.keyCode == 39) {
238
239                        $next.children(".js-tablist__link").click().focus();
240
241                        event.preventDefault();
242                    } else if (event.keyCode == 36) {
243                        // activate first tab
244                        $first_link.click().focus();
245                        event.preventDefault();
246                    } else if (event.keyCode == 35) {
247                        // activate last tab
248                        $last_link.click().focus();
249                        event.preventDefault();
250                    }
251
252                }
253
254            })
255            .on("keydown", ".js-tabcontent", function(event) {
256
257                var $this = $(this),
258                    $selector_tab_to_focus = $this.attr('aria-labelledby'),
259                    $tab_to_focus = $("#" + $selector_tab_to_focus),
260                    $parent_item = $tab_to_focus.parent(),
261                    $parent_list = $parent_item.parent(),
262                    $first_item = $parent_list.find('.js-tablist__item:first-child'),
263                    $last_item = $parent_list.find('.js-tablist__item:last-child'),
264                    $prev_item = $parent_item,
265                    $next_item = $parent_item;
266
267                // CTRL up/Left
268                if ((event.keyCode == 37 || event.keyCode == 38) && event.ctrlKey) {
269                    $tab_to_focus.focus();
270                    event.preventDefault();
271                }
272                // CTRL PageUp
273                if (event.keyCode == 33 && event.ctrlKey) {
274                    //$tab_to_focus.focus();
275
276                    // search valid previous 
277                    do {
278                        // if we are on first => last
279                        if ($prev_item.is(".js-tablist__item:first-child")) {
280                            $prev_item = $last_item;
281                        }
282                        // else previous
283                        else {
284                            $prev_item = $prev_item.prev();
285                        }
286                    }
287                    while ($prev_item.children('.js-tablist__link').attr('aria-disabled') === 'true' && $prev_item !== $parent_item);
288
289                    $prev_item.children(".js-tablist__link").click().focus();
290
291                    event.preventDefault();
292                }
293                // CTRL PageDown
294                if (event.keyCode == 34 && event.ctrlKey) {
295                    $tab_to_focus.focus();
296
297                    // search valid next 
298                    do {
299                        // if we are on last => first
300                        if ($next_item.is(".js-tablist__item:last-child")) {
301                            $next_item = $first_item;
302                        }
303                        // else previous
304                        else {
305                            $next_item = $next_item.next();
306                        }
307                    }
308                    while ($next_item.children('.js-tablist__link').attr('aria-disabled') === 'true' && $next_item !== $parent_item);
309
310                    $next_item.children(".js-tablist__link").click().focus();
311
312                    event.preventDefault();
313                }
314
315            })
316            /* click on a tab link */
317            .on("click", ".js-link-to-tab", function() {
318                var $this = $(this),
319                    $tab_to_go = $($this.attr('href')),
320                    $button_to_click = $('#' + $tab_to_go.attr('aria-labelledby'));
321
322                if ($button_to_click.attr('aria-disabled') !== 'true') {
323                    // activate tabs
324                    $button_to_click.click();
325                    // give focus to the good button
326                    setTimeout(function() {
327                        $button_to_click.focus()
328                    }, 10);
329                }
330
331            });
332
333    }
334
335});

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.