PageSourceSearch

https://next-level.biz/wp-content/themes/next-level/js/bootstrap-vanilla.js

js next-level.biz collected 2026-09-28 10:21:57 UTC 5,670 bytes, 178 lines download raw bytes

1/**
2 * Bootstrap Vanilla JS Alternative
3 * 最小限のBootstrap機能をVanilla JSで実装
4 */
5
6// Tooltip機能
7class SimpleTooltip {
8    constructor() {
9        this.init();
10    }
11
12    init() {
13        // data-toggle="tooltip"を持つ要素を取得
14        const tooltipElements = document.querySelectorAll('[data-toggle="tooltip"]');
15        
16        tooltipElements.forEach(element => {
17            this.setupTooltip(element);
18        });
19    }
20
21    setupTooltip(element) {
22        let tooltip = null;
23        
24        // マウスエンター時
25        element.addEventListener('mouseenter', (e) => {
26            const title = element.getAttribute('title') || element.getAttribute('data-original-title');
27            if (!title) return;
28            
29            // 既存のtooltipを削除
30            this.removeTooltip();
31            
32            // 新しいtooltipを作成
33            tooltip = document.createElement('div');
34            tooltip.className = 'tooltip fade in';
35            tooltip.setAttribute('role', 'tooltip');
36            
37            const placement = element.getAttribute('data-placement') || 'top';
38            tooltip.classList.add('tooltip-' + placement);
39            
40            // tooltipの内容を設定
41            const tooltipInner = document.createElement('div');
42            tooltipInner.className = 'tooltip-inner';
43            tooltipInner.textContent = title;
44            
45            const tooltipArrow = document.createElement('div');
46            tooltipArrow.className = 'tooltip-arrow';
47            
48            tooltip.appendChild(tooltipArrow);
49            tooltip.appendChild(tooltipInner);
50            
51            // bodyに追加
52            document.body.appendChild(tooltip);
53            
54            // 位置を計算
55            this.positionTooltip(element, tooltip, placement);
56            
57            // originalタイトルを保存してtitleを削除
58            if (element.getAttribute('title')) {
59                element.setAttribute('data-original-title', title);
60                element.removeAttribute('title');
61            }
62        });
63        
64        // マウスリーブ時
65        element.addEventListener('mouseleave', () => {
66            this.removeTooltip();
67        });
68    }
69
70    positionTooltip(element, tooltip, placement) {
71        const rect = element.getBoundingClientRect();
72        const tooltipRect = tooltip.getBoundingClientRect();
73        
74        let top, left;
75        
76        switch(placement) {
77            case 'top':
78                top = rect.top + window.scrollY - tooltipRect.height - 5;
79                left = rect.left + window.scrollX + (rect.width / 2) - (tooltipRect.width / 2);
80                break;
81            case 'bottom':
82                top = rect.bottom + window.scrollY + 5;
83                left = rect.left + window.scrollX + (rect.width / 2) - (tooltipRect.width / 2);
84                break;
85            case 'left':
86                top = rect.top + window.scrollY + (rect.height / 2) - (tooltipRect.height / 2);
87                left = rect.left + window.scrollX - tooltipRect.width - 5;
88                break;
89            case 'right':
90                top = rect.top + window.scrollY + (rect.height / 2) - (tooltipRect.height / 2);
91                left = rect.right + window.scrollX + 5;
92                break;
93            default:
94                top = rect.top + window.scrollY - tooltipRect.height - 5;
95                left = rect.left + window.scrollX + (rect.width / 2) - (tooltipRect.width / 2);
96        }
97        
98        tooltip.style.top = top + 'px';
99        tooltip.style.left = left + 'px';
100    }
101
102    removeTooltip() {
103        const existingTooltip = document.querySelector('.tooltip');
104        if (existingTooltip) {
105            existingTooltip.remove();
106        }
107    }
108}
109
110// Alert機能(必要に応じて)
111class SimpleAlert {
112    constructor() {
113        this.init();
114    }
115
116    init() {
117        // data-dismiss="alert"を持つ要素を取得
118        document.addEventListener('click', (e) => {
119            if (e.target.getAttribute('data-dismiss') === 'alert') {
120                const alert = e.target.closest('.alert');
121                if (alert) {
122                    alert.style.opacity = '0';
123                    setTimeout(() => {
124                        alert.remove();
125                    }, 150);
126                }
127            }
128        });
129    }
130}
131
132// Collapse機能(必要に応じて)
133class SimpleCollapse {
134    constructor() {
135        this.init();
136    }
137
138    init() {
139        document.addEventListener('click', (e) => {
140            if (e.target.getAttribute('data-toggle') === 'collapse') {
141                e.preventDefault();
142                const targetSelector = e.target.getAttribute('data-target') || e.target.getAttribute('href');
143                const target = document.querySelector(targetSelector);
144                
145                if (target) {
146                    this.toggle(target);
147                }
148            }
149        });
150    }
151
152    toggle(element) {
153        if (element.style.display === 'none' || !element.style.display) {
154            element.style.display = 'block';
155            element.classList.add('in');
156        } else {
157            element.style.display = 'none';
158            element.classList.remove('in');
159        }
160    }
161}
162
163// 初期化
164document.addEventListener('DOMContentLoaded', () => {
165    new SimpleTooltip();
166    new SimpleAlert();
167    new SimpleCollapse();
168});
169
170// 動的に追加される要素にも対応
171const observer = new MutationObserver(() => {
172    new SimpleTooltip();
173});
174
175observer.observe(document.body, {
176    childList: true,
177    subtree: true
178});

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.