PageSourceSearch

https://www.ajec.or.jp/wp/wp-content/themes/ajec3/assets/js/featured-slider.js?ver=1772510962

js ajec.or.jp collected 2026-10-02 19:07:37 UTC 4,786 bytes, 154 lines download raw bytes

1/**
2 * 特集カードスライダー
3 *
4 * CSS scroll-snap ベースの軽量スライダー。
5 * 自動スクロール + ドットナビ + 前後ボタン + スワイプ対応。
6 *
7 * @package ajec3
8 */
9(function () {
10    'use strict';
11
12    var AUTO_INTERVAL = 5000;  // 自動送り間隔(ms)
13    var PAUSE_AFTER   = 8000;  // 手動操作後の再開待ち(ms)
14
15    var slider = document.querySelector('.featured-slider');
16    if (!slider) return;
17
18    var track = slider.querySelector('.featured-slider__track');
19    var dots  = slider.parentElement.querySelectorAll('.featured-slider__dot');
20    var prev  = slider.querySelector('.featured-slider__btn--prev');
21    var next  = slider.querySelector('.featured-slider__btn--next');
22    var cards = track.querySelectorAll('.featured-card');
23
24    if (cards.length < 2) return;
25
26    var autoTimer   = null;
27    var resumeTimer = null;
28    var isPaused    = false;
29
30    /* ---------------------------------------------------------------
31     * ユーティリティ
32     * --------------------------------------------------------------- */
33    function getActiveIndex() {
34        var cardWidth = cards[0].offsetWidth;
35        var gap = parseInt(getComputedStyle(track).gap) || 0;
36        return Math.round(track.scrollLeft / (cardWidth + gap));
37    }
38
39    function updateDots() {
40        var idx = getActiveIndex();
41        dots.forEach(function (dot, i) {
42            dot.classList.toggle('is-active', i === idx);
43        });
44    }
45
46    function scrollToCard(index) {
47        var cardWidth = cards[0].offsetWidth;
48        var gap = parseInt(getComputedStyle(track).gap) || 0;
49        track.scrollTo({
50            left: index * (cardWidth + gap),
51            behavior: 'smooth'
52        });
53    }
54
55    /* ---------------------------------------------------------------
56     * 自動スクロール
57     * --------------------------------------------------------------- */
58    function autoAdvance() {
59        var idx = getActiveIndex();
60        var nextIdx = (idx + 1) % cards.length;
61        scrollToCard(nextIdx);
62    }
63
64    function startAuto() {
65        stopAuto();
66        isPaused = false;
67        autoTimer = setInterval(autoAdvance, AUTO_INTERVAL);
68    }
69
70    function stopAuto() {
71        clearInterval(autoTimer);
72        clearTimeout(resumeTimer);
73        autoTimer = null;
74    }
75
76    /** 手動操作時: 一旦止めて PAUSE_AFTER 後に再開 */
77    function pauseAndResume() {
78        stopAuto();
79        isPaused = true;
80        resumeTimer = setTimeout(startAuto, PAUSE_AFTER);
81    }
82
83    /* ---------------------------------------------------------------
84     * イベント
85     * --------------------------------------------------------------- */
86
87    // スクロール監視
88    var scrollDebounce;
89    track.addEventListener('scroll', function () {
90        clearTimeout(scrollDebounce);
91        scrollDebounce = setTimeout(updateDots, 80);
92    }, { passive: true });
93
94    // ドットクリック
95    dots.forEach(function (dot) {
96        dot.addEventListener('click', function () {
97            scrollToCard(parseInt(this.getAttribute('data-index'), 10));
98            pauseAndResume();
99        });
100    });
101
102    // 前後ボタン
103    if (prev) {
104        prev.addEventListener('click', function () {
105            var idx = getActiveIndex();
106            scrollToCard(idx > 0 ? idx - 1 : cards.length - 1);
107            pauseAndResume();
108        });
109    }
110    if (next) {
111        next.addEventListener('click', function () {
112            var idx = getActiveIndex();
113            scrollToCard((idx + 1) % cards.length);
114            pauseAndResume();
115        });
116    }
117
118    // ホバーで一時停止、離すと再開
119    slider.addEventListener('mouseenter', function () {
120        if (!isPaused) stopAuto();
121    });
122    slider.addEventListener('mouseleave', function () {
123        if (!isPaused) startAuto();
124    });
125
126    // タッチ操作後は再開タイマー
127    track.addEventListener('touchstart', function () {
128        stopAuto();
129        isPaused = true;
130    }, { passive: true });
131    track.addEventListener('touchend', function () {
132        resumeTimer = setTimeout(startAuto, PAUSE_AFTER);
133    }, { passive: true });
134
135    // リサイズ
136    window.addEventListener('resize', function () {
137        clearTimeout(scrollDebounce);
138        scrollDebounce = setTimeout(updateDots, 150);
139    });
140
141    // ページ非表示時は止める
142    document.addEventListener('visibilitychange', function () {
143        if (document.hidden) {
144            stopAuto();
145        } else {
146            startAuto();
147        }
148    });
149
150    /* ---------------------------------------------------------------
151     * 初期化
152     * --------------------------------------------------------------- */
153    startAuto();
154})();

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.