PageSourceSearch

https://www.nishikofu.co.jp/wp_nkofu/wp-content/themes/mod_nkofu_tm/js/post_sns.js

js nishikofu.co.jp collected 2026-10-08 02:40:53 UTC 7,497 bytes, 240 lines download raw bytes

1document.addEventListener('DOMContentLoaded',function() {
2	/**
3	 * HP:https://ja.splidejs.com
4	 * GitHub:https://github.com/Splidejs/splide
5	 */
6	// let sliderElement = document.querySelectorAll('.splide');
7	let sliderElement = document.getElementsByClassName('splide');
8	if (sliderElement.length) {
9		Splide.defaults = {
10			// type: 'loop',
11			// speed: 500,
12			perPage: 1,
13			perMove: 1,
14			focus : 0,
15			// gap: 20,
16			// padding: 20,
17			// arrows: false,
18			// pagination: false,
19			flickMaxPages: 1,
20			// autoplay: true,
21			interval: 1500,
22			// pauseOnHover: false,
23			// pauseOnFocus: false,
24			// lazyLoad: 'nearby', // img要素にdata-splide-lazy属性(画像ファイルパス)が必要
25			// omitEnd: true,
26			/*
27			breakpoints: {
28				480: {
29					perPage: 1,
30					padding: 0,
31				}
32			},
33			*/
34			video: { // 動画オプション
35				// autoplay: true, // 自動再生
36				// hideControls: true, // 各種動画プレイヤのUIを非表示
37				// loop: true, // ループ再生
38				// mute: true, // ミュート(音量0)状態
39				playerOptions: { // 各種プレイヤのオプション
40					// youtube: { ... },
41				},
42			},
43			// video:play { ... }
44		}
45		var splide = {};
46		for (let i = 0; i < sliderElement.length; i++) {
47			splide[i] = new Splide(sliderElement[i]);
48			splide[i].on('overflow',function (isOverflow) {
49				// splide[i].go(0);
50				splide[i].options = {
51					arrows: isOverflow,
52					pagination: isOverflow,
53					drag: isOverflow,
54					clones: isOverflow ? undefined : 0
55				};
56				if (isOverflow) {
57				}
58				else {
59				}
60			});
61			// splide[i].mount();
62			splide[i].mount(window.splide.Extensions); // エクステンションを使用する場合(別途CSSとJavaScriptが必要)
63		}
64	}
65
66	// タイトル移動
67	const elementToMove = document.querySelectorAll('.instagram-head');
68	const elementToMoveLength = elementToMove.length;
69	const mediaQuery = window.matchMedia('(max-width:834px)');
70	handle(mediaQuery);
71	mediaQuery.addListener(handle);
72	function handle(mm) {
73		if (mm.matches) {
74			for (let i = 0; i < elementToMoveLength; i++) {
75				elementToMove[i].closest('.instagram-layout').prepend(elementToMove[i]);
76			}
77		}
78		else {
79			for (let i = 0; i < elementToMoveLength; i++) {
80				const children = elementToMove[i].closest('.instagram-layout').children;
81				const childrenLength = children.length;
82				for (let n = 0; n < childrenLength; n++) {
83					if (children[n].className.split(' ').indexOf('instagram-content') !== -1) {
84						children[n].prepend(elementToMove[i]);
85					}
86				}
87			}
88		}
89	}
90
91	// すべての<a>タグを選択し、対象のaタグすべてにtarget="_blank"を設定する
92	document.querySelectorAll('.instagram-caption a').forEach(function(link) {
93		// console.log(link.href);
94		if (link.href.indexOf(location.hostname) === -1) {
95			// 外部リンクの場合のみtarget="_blank"を付与
96			link.setAttribute('target','_blank');
97		}
98	});
99
100	/**
101	 * SNS用モーダルウィンドウ処理
102	 */
103	function initSnsModal() {
104		const modalWrap = document.querySelector('.sns-list');
105		if (!modalWrap) return;
106
107		const ajaxUrl = modalWrap.getAttribute('data-ajax-url');
108		if (!ajaxUrl) return;
109
110		let currentSplide = null;
111		let currentAbortController = null;
112
113		// モーダルを閉じる処理をまとめる
114		const closeModal = (modal) => {
115			if (currentAbortController) {
116				currentAbortController.abort();
117				currentAbortController = null;
118			}
119
120			modal.classList.remove('is-active');
121			document.body.style.overflow = '';
122
123			if (currentSplide) {
124				currentSplide.destroy();
125				currentSplide = null;
126			}
127
128			setTimeout(() => modal.remove(), 300);
129		};
130
131		// 親要素でまとめてクリックを検知(イベントデリゲーション)
132		modalWrap.addEventListener('click', async (e) => {
133			const btn = e.target.closest('.js-sns-modal-open');
134			if (!btn) return;
135
136			e.preventDefault();
137
138			const postId = btn.getAttribute('data-post-id');
139			if (!postId) return;
140
141			// 既存の通信があればキャンセル
142			if (currentAbortController) {
143				currentAbortController.abort();
144			}
145			currentAbortController = new AbortController();
146
147			// ① 既存のモーダルがあれば削除(オプショナルチェーンで簡潔に)
148			document.getElementById('sns-common-modal')?.remove();
149
150			// ② モーダルの枠組みを生成してBodyに追加
151			const modalHtml = `
152			<div id="sns-common-modal" class="sns-modal">
153				<div class="sns-modal-overlay"></div>
154				<button class="sns-modal-close-btn js-sns-modal-close" aria-label="閉じる">
155					<svg viewBox="0 0 22 22" width="18" height="18"><path d="M2 2 L20 20 M20 2 L2 20" stroke="currentColor" stroke-width="3" /></svg>
156				</button>
157				<div class="sns-modal-content" id="sns-modal-render-area">
158					<div class="sns-loading-wrap">
159						<span class="sns-loading-text">読み込み中</span>
160						<span class="sns-loading-dots"><span></span><span></span><span></span></span>
161					</div>
162				</div>
163			</div>`;
164			document.body.insertAdjacentHTML('beforeend', modalHtml);
165
166			const modal = document.getElementById('sns-common-modal');
167			const renderArea = document.getElementById('sns-modal-render-area');
168
169			requestAnimationFrame(() => {
170				modal.classList.add('is-active');
171				document.body.style.overflow = 'hidden';
172			});
173
174			// ③ 閉じるボタン等にイベントを付与
175			modal.addEventListener('click', (event) => {
176				const isCloseTrigger = event.target.closest('.js-sns-modal-close') ||
177									event.target.classList.contains('sns-modal-overlay') ||
178									event.target.classList.contains('sns-modal-content-wrap');
179
180				if (isCloseTrigger) {
181					event.preventDefault();
182					closeModal(modal);
183				}
184			});
185
186			// ④ Ajax通信を開始(async / await を使ってスッキリ記述)
187			try {
188				const formData = new URLSearchParams();
189				formData.append('action', 'get_sns_modal');
190				formData.append('post_id', postId);
191
192				const response = await fetch(ajaxUrl, {
193					method: 'POST',
194					body: formData,
195					signal: currentAbortController.signal
196				});
197
198				if (!response.ok) throw new Error('Network Error');
199
200				const data = await response.json();
201				currentAbortController = null;
202
203				if (data.success && data.data.html) {
204					renderArea.innerHTML = data.data.html;
205
206					// Splideの初期化
207					const splideEl = renderArea.querySelector('.js-splide-modal');
208					if (splideEl) {
209						const slideCount = splideEl.querySelectorAll('.splide__slide').length;
210						const isSingle = slideCount <= 1;
211
212						const exts = (window.splide?.Extensions) || (window.Splide?.Extensions) || {};
213
214						currentSplide = new Splide(splideEl, {
215							type: isSingle ? 'slide' : 'loop',
216							pagination: !isSingle,
217							arrows: !isSingle,
218							drag: !isSingle,
219							video: { disableOverlayUI: false }
220						}).mount(exts);
221					}
222				} else {
223					renderArea.innerHTML = '<p class="sns-error-wrap">データの取得に失敗しました。</p>';
224				}
225			} catch (error) {
226				if (error.name === 'AbortError') return; // 意図的なキャンセルの場合は何もしない
227				currentAbortController = null;
228				renderArea.innerHTML = '<p class="sns-error-wrap">通信エラーが発生しました。</p>';
229			}
230		});
231	}
232
233	// 読み込み完了を待ってから実行(これで環境差分によるエラーを防ぎます)
234	if (document.readyState === 'loading') {
235		document.addEventListener('DOMContentLoaded', initSnsModal);
236	} else {
237		initSnsModal();
238	}
239
240});

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.