PageSourceSearch

https://www.city.morioka.iwate.jp/_template_/_site_/_default_/_res/js/tabs_automatic.js

js city.morioka.iwate.jp collected 2026-10-02 05:16:51 UTC 4,038 bytes, 164 lines download raw bytes

1//------------------------------------------------------------------------------
2//	タブ切り替え機能
3//	Copyright (C) 2024 FUTUREINN CO., LTD. All Rights Reserved.
4/*
5 *   This content is licensed according to the W3C Software License at
6 *   https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document
7 *
8 *   File:   tabs-automatic.js
9 *
10 *   Desc:   Tablist widget that implements ARIA Authoring Practices
11 */
12
13 (function(){
14	var $ = FI.jQuery;
15
16	$(function() {
17
18		'use strict';
19		class TabsAutomatic {
20			
21			static tablistCount = 0;
22			
23			initializeTabStructure() {
24				const tablistId = this.tablistNumber;
25				
26				this.$tablistNode.find('> button').each((tabIndex, tabElement) => {
27					const $tab = $(tabElement);
28					const $tabpanel = this.$tablistNode.parent().find('[role="tabpanel"]').eq(tabIndex);
29					
30					if ($tab.length === 0 || $tabpanel.length === 0) {
31						return;
32					}
33					
34					// タブの属性設定
35					const tabId = `for_topics${tablistId}-${tabIndex}`;
36					$tab.attr({
37						'id': tabId,
38						'aria-selected': tabIndex === 0 ? 'true' : 'false',
39						'tabindex': tabIndex === 0 ? '0' : '-1'
40					});
41	
42					// タブのaria-controls設定
43					const panelId = $tabpanel.attr('id') || `topics${tablistId}-${tabIndex}`;
44					$tab.attr('aria-controls', panelId);
45	
46					// パネルの属性設定
47					$tabpanel.attr({
48						'id': panelId,
49						'tabindex': '0',
50						'aria-labelledby': tabId
51					});
52				});
53			}
54			
55			constructor(groupNode) {
56				TabsAutomatic.tablistCount++;
57				this.tablistNumber = TabsAutomatic.tablistCount;
58				this.$tablistNode = $(groupNode);
59				this.initializeTabStructure();
60				this.$tabs = this.$tablistNode.find('[role=tab]');
61				this.$tabpanels = [];
62
63
64				this.$tabs.each((i, tab) => {
65					const $tab = $(tab);
66					const tabpanelId = $tab.attr('aria-controls');
67					const $tabpanel = $(`#${tabpanelId}`);
68
69					this.$tabpanels.push($tabpanel);
70
71					$tab.on('keydown', this.onKeydown.bind(this));
72					$tab.on('click', this.onClick.bind(this));
73
74					if (i === 0) {
75						this.firstTab = $tab;
76					}
77					this.lastTab = $tab;
78				});
79
80				this.setSelectedTab(this.firstTab, false);
81			}
82
83			setSelectedTab($currentTab, setFocus = true) {
84				this.$tabs.each((i, tab) => {
85					const $tab = $(tab);
86					const $tabpanel = this.$tabpanels[i];
87
88					if ($tab.is($currentTab)) {
89						$tab.attr('aria-selected', 'true');
90						$tab.attr('tabindex', '0');
91						$tabpanel.removeClass('is-hidden');
92						$tabpanel.addClass('is-visible');
93						
94						if (setFocus) {
95							$tab.focus();
96						}
97					} else {
98						$tab.attr('aria-selected', 'false');
99						$tab.attr('tabIndex', -1);
100						$tabpanel.addClass('is-hidden');
101						$tabpanel.removeClass('is-visible');
102					}
103				});
104			}
105
106			setSelectedToPreviousTab($currentTab) {
107				const index = this.$tabs.index($currentTab);
108				const $prevTab = index === 0 ? this.lastTab : this.$tabs.eq(index - 1);
109				this.setSelectedTab($prevTab);
110			}
111
112			setSelectedToNextTab($currentTab) {
113				const index = this.$tabs.index($currentTab);
114				const $nextTab = index === this.$tabs.length - 1 ? this.firstTab : this.$tabs.eq(index + 1);
115				this.setSelectedTab($nextTab);
116			}
117
118			onKeydown(event) {
119				const $tgt = $(event.currentTarget);
120				let flag = false;
121
122				switch (event.key) {
123					case 'ArrowLeft':
124						this.setSelectedToPreviousTab($tgt);
125						flag = true;
126						break;
127
128					case 'ArrowRight':
129						this.setSelectedToNextTab($tgt);
130						flag = true;
131						break;
132
133					case 'Home':
134						this.setSelectedTab(this.firstTab);
135						flag = true;
136						break;
137
138					case 'End':
139						this.setSelectedTab(this.lastTab);
140						flag = true;
141						break;
142
143					default:
144						break;
145				}
146
147				if (flag) {
148					event.stopPropagation();
149					event.preventDefault();
150				}
151			}
152
153			onClick(event) {
154				this.setSelectedTab($(event.currentTarget));
155			}
156		}
157
158		// Initialize tablist
159		$('[role=tablist].automatic').each((i, tablist) => {
160			new TabsAutomatic(tablist);
161		});
162
163	});
164})();

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.