PageSourceSearch

https://www.sun.ac.jp/assets/js/common.js

js sun.ac.jp collected 2026-10-01 12:02:33 UTC 19,418 bytes, 571 lines download raw bytes

1var mql_h = window.matchMedia('screen and (max-width: 1024px)');
2var mediaPrint = window.matchMedia('print');
3const activeClass = 'is_active';
4const openClass = 'is_open';
5let last_mql_hMatches = mql_h.matches;
6
7const elementToMove_lang = document.getElementById('header_btn_language');
8const elementToMove_nav = document.getElementById('header_menu_other');     
9const elementToMove_lnav = document.getElementById('cmn_nav_local');
10//DOMContentLoaded
11window.addEventListener('DOMContentLoaded', function () {
12  mql_h.addEventListener('change', function(e) {
13    if (mediaPrint.matches) {
14      return;
15    }
16    if (e.matches !== last_mql_hMatches) {
17      location.reload();
18    }
19  });
20
21  function checkBreakPointMenu() {
22    if (!mql_h.matches) {
23      // PCレイアウト時の移動先
24      const destination_lang = document.getElementById('header_menu_sub');
25      const destination_nav = document.getElementById('header_gnav');
26      const destination_lnav = document.getElementById('primary');
27
28      if(elementToMove_lang && destination_lang) destination_lang.appendChild(elementToMove_lang);
29      if(elementToMove_nav && destination_nav) destination_nav.appendChild(elementToMove_nav);
30      if(elementToMove_lnav && destination_lnav) destination_lnav.appendChild(elementToMove_lnav);
31    }
32  }
33  checkBreakPointMenu();
34  mql_h.addEventListener('change', checkBreakPointMenu);
35
36  if(elementToMove_lnav){
37  if(!elementToMove_lnav.querySelector('ul')){elementToMove_lnav.style.display="none";}
38  }
39
40  //acd
41  const acdButtons = document.querySelectorAll(".cmn_acd dt > button");
42  acdButtons.forEach((button) => {
43    button.addEventListener("click", function () {
44      const ttl = button.closest(".acd_ttl");
45      const item = ttl.nextElementSibling;
46      if (!item) return;
47      const isOpen = button.classList.contains(openClass);
48      if (isOpen) {
49        closeAccordion(button, item);
50      } else {
51        openAccordion(button, item);
52      }
53    });
54  });
55
56  //タブ初期化
57  initTabSwitches(document);
58
59  //?tab=panel_idでタブを開いた状態に
60  const urlParams = new URLSearchParams(window.location.search);
61  const tabParam = urlParams.get('tab');
62
63  if (tabParam) {
64    const targetButton = document.querySelector(`.js_cmn_tabchenge .list_tab button[aria-controls="${tabParam}"]`);   
65    if (targetButton) {
66      targetButton.click();
67      setTimeout(() => {
68        targetButton.scrollIntoView({
69          behavior: 'smooth', // 滑らかにスクロール
70          block: 'start'      // 画面の最上部に合わせる
71        });
72      }, 100);
73    }
74  }  
75
76  /*history.back*/
77  let ans;let pageStaying=true;const ref=document.referrer;const re=new RegExp(location.hostname,'i');ans=re.test(ref);window.addEventListener('beforeunload',()=>{pageStaying=false;});document.querySelectorAll('.historyback').forEach(el=>{el.addEventListener('click',e=>{e.preventDefault();const fallbackUrl=el.getAttribute('href');if(ans){pageStaying=true;history.back();setTimeout(()=>{if(pageStaying){location.href=fallbackUrl;}},200);}else{location.href=fallbackUrl;}});});
78
79  document.querySelectorAll('.cmn_scroll_area').forEach(function(area) {
80  if (area.offsetWidth < area.scrollWidth) {
81    area.insertAdjacentHTML('beforeend', '<span class="ico_scroll"><img src="/assets/img/ico_scrollhint.png" alt=""></span>');   
82    const scrollIcon = area.querySelector('.ico_scroll');
83    const handleScroll = function() {
84      if (scrollIcon) {
85        scrollIcon.style.transition = 'opacity 0.5s';
86        scrollIcon.style.opacity = '0';
87        setTimeout(() => scrollIcon.remove(), 500);
88      }
89      area.removeEventListener('scroll', handleScroll);
90    };
91    area.addEventListener('scroll', handleScroll);
92  }
93});
94
95  // グロナビアクティブ
96  const wrapper = document.querySelector('.wrapper');
97  if (!wrapper) return;
98  const targetId = wrapper.id;
99  const directMenuItems = document.querySelectorAll('#header_menu_main > li');
100  directMenuItems.forEach(function(item) {
101    if (item.classList.contains(targetId)) {
102      item.classList.add(activeClass);
103    }
104  });
105});
106
107/*
108* ==========================================================
109* 切替タブ
110* @param {HTMLElement} wrapper - タブ要素が含まれる親要素
111* ==========================================================
112*/
113const initTabSwitches = (wrapper) => {
114  const tabSwitches = wrapper.querySelectorAll(".js_cmn_tabchenge");
115  tabSwitches.forEach(tabSwitch => {
116    const buttons = tabSwitch.querySelectorAll('.list_tab button');
117    const panels = tabSwitch.querySelectorAll('.area_tab_contents > div');
118    buttons.forEach(button => {
119      button.addEventListener('click', () => {
120        // 全てのボタンとパネルをリセット
121        buttons.forEach(btn => {
122          btn.setAttribute("aria-selected", "false");
123          const li = btn.closest("li");
124          if (li) li.classList.remove("is_active");
125        });
126        panels.forEach(panel => {
127          panel.setAttribute("aria-hidden", "true");
128        });
129        // クリックされたボタンをアクティブにする
130        button.setAttribute("aria-selected", "true");
131        const targetLi = button.closest("li");
132        if (targetLi) targetLi.classList.add("is_active");
133        const targetId = button.getAttribute("aria-controls");
134        // document全体から探すと元々の要素と重複するため、tabSwitch内から探す
135        const targetPanel = tabSwitch.querySelector(`#${targetId}`);
136        if (targetPanel) {
137          targetPanel.setAttribute("aria-hidden", "false");
138        }
139      });
140    });
141  });
142};
143/*
144* ==========================================================
145* ナビ関連アコーディオン共通関数
146* ==========================================================
147*/
148function accordionButton(buttons){
149    buttons.forEach((button) => {
150      button.addEventListener("click", (e) => {
151        e.preventDefault();
152        const item = button.nextElementSibling;
153        console.log(item);
154        if (!item) return;
155        const expanded = button.getAttribute('aria-expanded') === 'true';
156
157        if (expanded) {
158          // --- 閉じる処理 ---
159          closeAccordion(button, item)
160        } else {
161          // --- 開く処理 ---
162          openAccordion(button, item)
163        }
164      });
165    });
166}
167//openAccordion
168function openAccordion(button, item) {
169  item.style.visibility = "visible";
170  item.style.maxHeight = "0px";
171  item.style.opacity = "0";
172  item.offsetHeight; // reflow
173  item.style.maxHeight = item.scrollHeight + "px";
174  item.style.opacity = "1";
175  item.setAttribute('aria-hidden', 'false');
176  button.classList.add(openClass);
177  button.setAttribute('aria-expanded', 'true');
178
179  const showEnd = () => {
180    if (button.getAttribute('aria-expanded') === 'true') {
181        item.style.maxHeight = "none";
182    }
183    item.classList.add(openClass);
184    item.removeEventListener("transitionend", showEnd);
185  };
186  item.addEventListener("transitionend", showEnd);
187}
188//closeAccordion
189function closeAccordion(button, item) {
190  item.style.maxHeight = item.scrollHeight + "px";
191  item.offsetHeight; // reflow
192  item.style.maxHeight = "0px";
193  item.style.opacity = "0";
194  item.setAttribute('aria-hidden', 'true');
195  button.classList.remove(openClass);
196  button.setAttribute('aria-expanded', 'false');
197
198  const hideEnd = () => {
199    item.classList.remove(openClass);
200    item.style.visibility = "hidden";
201    item.removeEventListener("transitionend", hideEnd);
202  };
203  item.addEventListener("transitionend", hideEnd);
204}
205/*
206* ==========================================================
207* ローカルナビ
208* ==========================================================
209*/
210window.addEventListener('DOMContentLoaded', function () {
211  const accordionButtons = document.querySelectorAll('.nav_local_third button[aria-expanded]');
212  accordionButton(accordionButtons);
213
214  if (mql_h.matches) {
215      let isMenuOpened = false;
216      const lnavBtn = document.getElementById("btn_second");
217      if (!lnavBtn) return;
218      const item = lnavBtn.nextElementSibling;
219      
220      const open = () => {
221        lnavBtn.setAttribute('aria-expanded', 'true');
222        if(item) item.classList.add(openClass);
223        isMenuOpened = true;
224        document.body.style.overflow = "hidden";
225      };
226      const close = () => {
227        lnavBtn.setAttribute('aria-expanded', 'false');
228        if(item) item.classList.remove(openClass);
229        isMenuOpened = false;
230        document.body.style.overflow = ""; 
231      };
232      
233      lnavBtn.addEventListener("click", function (e) {
234        e.stopPropagation();
235        if (!isMenuOpened) {
236          open();
237        } else {
238          close();
239        }
240      });
241
242      document.addEventListener('click', (e) => {
243        if (!isMenuOpened) return;
244        const isNavButton = e.target.closest('.nav_local_third button, #btn_second');
245        const isNavContent = e.target.closest('.nav_local_second'); // コンテンツ内クリックも考慮
246        if (!isNavButton && !isNavContent) {
247          close();
248        }
249      });
250    }
251    
252}); 
253
254/*
255* ==========================================================
256* グロナビ
257* ==========================================================
258*/
259const header = document.getElementById('header');
260window.addEventListener('DOMContentLoaded', function () {
261  //検索等
262  const header = document.getElementById('header');
263  const btnContainers = header.querySelectorAll('.btn');
264  const triggers = header.querySelectorAll('.js_btn_header');
265  const closeAll = () => {
266    btnContainers.forEach(el => el.classList.remove(activeClass));
267  };
268  triggers.forEach(trigger => {
269    trigger.addEventListener('click', (e) => {
270      e.stopPropagation();
271      const container = trigger.closest('.btn');
272      const isOpen = container.classList.contains(activeClass);
273      closeAll();
274      if (!isOpen) {
275        container.classList.add(activeClass);
276      }
277    });
278  });
279  document.addEventListener('click', (e) => {
280    // 外側クリックで閉じる
281    if (!e.target.closest('.btn')) {
282      closeAll();
283    }
284  });  
285  
286  //PChover
287  if (!mql_h.matches) {
288    const links = document.querySelectorAll('#header_menu_main .link');
289    const container = document.getElementById('js-menu-container');
290    if (!container) return;
291
292    let leaveTimer;
293    let enterTimer;
294    let currentActiveMenu = null;
295
296    document.querySelectorAll('.area_lowermenu').forEach(menu => {
297        menu._originalParent = menu.parentElement;
298    });
299
300    links.forEach(link => {
301        const parentLi = link.parentElement;
302        const subMenu = parentLi.querySelector('.area_lowermenu');
303
304        if (!subMenu) return;
305
306        link.addEventListener('mouseenter', () => {
307            clearTimeout(leaveTimer);
308            clearTimeout(enterTimer);
309
310            // メニューを表示する実処理を関数化
311            const showMenu = () => {
312                // 検索ボタンなどの他のアクティブ状態を解除
313                const trigger = document.querySelectorAll('.js_btn_header');
314                if (trigger) {
315                    trigger.forEach(e => {
316                        const containerBtn = e.closest('.btn');
317                        if (containerBtn) containerBtn.classList.remove(activeClass);
318                    });
319                }
320
321                // すでに別のメニューが開いている場合は一旦戻す
322                if (currentActiveMenu && currentActiveMenu !== subMenu) {
323                    returnMenuToOriginal();
324                }
325
326                container.appendChild(subMenu);
327                container.classList.add(openClass);
328                container.setAttribute('aria-hidden', 'false');
329                subMenu.setAttribute('aria-hidden', 'false');
330                currentActiveMenu = subMenu;
331            };
332            if (container.classList.contains(openClass)) {
333                showMenu();
334            } else {
335                enterTimer = setTimeout(showMenu, 200);
336            }
337        });
338
339        link.addEventListener('mouseleave', () => {
340            clearTimeout(enterTimer);
341            startCloseTimer();
342        });
343    });
344
345    container.addEventListener('mouseenter', () => {
346        clearTimeout(leaveTimer);
347        clearTimeout(enterTimer);
348    });
349
350    container.addEventListener('mouseleave', () => {
351        startCloseTimer();
352    });
353
354    function startCloseTimer() {
355        clearTimeout(leaveTimer);
356        leaveTimer = setTimeout(() => {
357            closeMenu();
358        }, 300);
359    }
360
361    function closeMenu() {
362        container.classList.remove(openClass);
363        container.setAttribute('aria-hidden', 'true');
364        setTimeout(() => {
365            // アニメーション時間(300ms)待機後、まだ閉じられたままなら元の場所に戻す
366            if (!container.classList.contains(openClass)) {
367                returnMenuToOriginal();
368            }
369        }, 300);
370    }
371
372    function returnMenuToOriginal() {
373        if (currentActiveMenu && currentActiveMenu._originalParent) {
374            currentActiveMenu.setAttribute('aria-hidden', 'true');
375            currentActiveMenu._originalParent.appendChild(currentActiveMenu);
376            currentActiveMenu = null;
377        }
378    }
379}
380  
381
382});
383
384/*
385* ==========================================================
386* SPメニュー
387* ==========================================================
388*/
389window.addEventListener('DOMContentLoaded', function () {
390  let isMenuOpened = false;
391  const menuButton = document.getElementById("btn_menu");
392  const header = document.querySelector(".wrap_header");
393  const menuLabel = document.getElementById("menuButtonLabel");
394
395  if (mql_h.matches && menuButton) {
396    menuButton.addEventListener("click", function () {
397      if (!isMenuOpened) {
398        menuButton.classList.add(openClass);
399        if(menuLabel) menuLabel.textContent = "メニューを閉じる";
400        document.body.style.overflow = "hidden";
401        header.classList.add(openClass);
402        isMenuOpened = true;
403      } else {
404        menuButton.classList.remove(openClass);    
405        if(menuLabel) menuLabel.textContent = "メニューを開く";
406        document.body.style.overflow = "";
407        header.classList.remove(openClass);   
408        isMenuOpened = false;
409      }
410    });
411  }
412
413  const accordionButtons = document.querySelectorAll('#header_menu_main .btn_acd');
414  accordionButton(accordionButtons);
415});
416
417/*
418* ==========================================================
419* ヘッダー IntersectionObserver
420* ==========================================================
421*/
422const headerEl = document.querySelector('#header');
423const sentinel = document.querySelector('#header_sentinel');
424const wrapper = document.querySelector('.wrapper');
425if (headerEl && sentinel) {
426  const observer = new IntersectionObserver((entries) => {
427    entries.forEach(entry => {
428      var ratio = entry.intersectionRatio;
429      const isSmall = headerEl.classList.contains('is_small');
430      if (ratio < 1) {
431        if(!isSmall){
432          headerEl.classList.add('is_small');
433          wrapper.classList.add('is_header_small');
434        }
435      }
436      else{
437        if(isSmall){
438          headerEl.classList.remove('is_small');
439          wrapper.classList.remove('is_header_small');
440        }
441      }
442    });
443  }, {
444    threshold:[0,1]
445  });
446  observer.observe(sentinel);
447}
448
449/*
450* ==========================================================
451* スライダー
452* ==========================================================
453*/
454document.addEventListener('DOMContentLoaded', function () {
455  // スライダーのグループをすべて取得
456  const sliderGroups = document.querySelectorAll('.cmn_slider_group');
457
458  sliderGroups.forEach((group) => {
459    const mainEl = group.querySelector('.cmn_slider_main');
460    const thumbEl = group.querySelector('.cmn_slider_thumb');
461
462    if (!mainEl || !thumbEl) return;
463
464    // --- サムネイルの自動生成 ---
465    const mainImages = mainEl.querySelectorAll('.splide__list img');
466    const thumbList = thumbEl.querySelector('.splide__list');
467
468    // メインスライダーの画像を元に、サムネイルのHTMLを作成
469    mainImages.forEach((img) => {
470      const li = document.createElement('li');
471      li.classList.add('splide__slide');
472      const thumbImg = document.createElement('img');
473      thumbImg.src = img.src;
474      thumbImg.alt = ""; // サムネイルのaltは空、もしくは適宜設定
475      li.appendChild(thumbImg);
476      thumbList.appendChild(li);
477    });
478
479    // --- Splideの初期化 ---
480    const mainSplide = new Splide(mainEl, {
481      type: 'fade',
482      pagination: false,
483      arrows: true,
484      speed: 500,
485      fixedWidth:"100%"
486    });
487    const thumbSplide = new Splide(thumbEl, {
488      type: 'loop',
489      focus:'center',
490      fixedWidth: 154,
491      fixedHeight: 103,
492      gap: 8,
493      pagination: false,
494      arrows: false,
495      isNavigation: true,
496      autoplay: true,
497      interval: 3000,
498      speed: 500,
499      breakpoints: {
500        835: {
501          fixedWidth: 81,
502          fixedHeight: 54,
503        },
504      },
505    });
506    // メインとサムネイルを同期させてマウント
507    mainSplide.sync(thumbSplide);
508    mainSplide.mount();
509    thumbSplide.mount();
510    
511  });
512
513  /*
514  * ================
515  * バナースライダー
516  * ================
517  */
518  const splide_bnr = document.querySelector(".splide_bnr");
519  if (splide_bnr) {
520    var splideBnr = new Splide('.splide_bnr', {
521      type: 'loop',
522      autoWidth: true,
523      gap:40,
524      pagination: true,
525      arrows: false,
526      isNavigation: true,
527      autoplay: true,
528      interval: 3000,
529      speed: 500,
530      focus: 'center',
531      breakpoints: {
532        835: {
533          gap: 20,
534        },
535      }
536    } );
537    splideBnr.on( 'overflow', function ( isOverflow ) {
538      splideBnr.root.classList.toggle('is_centered', !isOverflow);
539
540      splideBnr.go( 0 );
541      splideBnr.options = {
542        pagination: isOverflow,
543        drag: isOverflow,
544        isNavigation: isOverflow,
545        clones    : isOverflow ? undefined : 0,
546        autoplay    : isOverflow,
547      };
548      const Autoplay = splideBnr.Components.Autoplay;
549      if (Autoplay) {
550        isOverflow ? Autoplay.play() : Autoplay.pause();
551      }
552      splideBnr.refresh();      
553    } );
554    splideBnr.mount();
555  }  
556});
557
558
559
560
561/*getParameter
562------------------------------------------------------*/
563function getParameter(name,url) {
564    if (!url) url = window.location.href;
565    name = name.replace(/[\[\]]/g, "\\$&");
566    var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
567        results = regex.exec(url);
568    if (!results) return null;
569    if (!results[2]) return '';
570    return decodeURIComponent(results[2].replace(/\+/g, " "));
vendor: 1 bytes, line 571
571}

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.