2025-09-14 00:24:42 +09:00

516 lines
17 KiB
JavaScript

// ========================================
// FAQ 페이지 전용 JavaScript
// ========================================
document.addEventListener('DOMContentLoaded', function() {
initFAQ();
initSearch();
initCategories();
initAnimations();
initEmailForm();
});
// FAQ 기능 초기화
function initFAQ() {
const faqItems = document.querySelectorAll('.faq-item');
faqItems.forEach(item => {
const question = item.querySelector('.faq-question');
const toggle = item.querySelector('.faq-toggle');
const answer = item.querySelector('.faq-answer');
if (question && toggle && answer) {
question.addEventListener('click', () => toggleFAQ(item));
// 키보드 접근성
question.addEventListener('keydown', (e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
toggleFAQ(item);
}
});
// 포커스 가능하도록 설정
question.setAttribute('tabindex', '0');
question.setAttribute('role', 'button');
question.setAttribute('aria-expanded', 'false');
}
});
}
// FAQ 아이템 토글
function toggleFAQ(item) {
const isActive = item.classList.contains('active');
const answer = item.querySelector('.faq-answer');
const question = item.querySelector('.faq-question');
if (isActive) {
// 닫기
item.classList.remove('active');
question.setAttribute('aria-expanded', 'false');
answer.style.maxHeight = '0';
} else {
// 다른 모든 FAQ 닫기 (아코디언 효과)
document.querySelectorAll('.faq-item.active').forEach(activeItem => {
if (activeItem !== item) {
activeItem.classList.remove('active');
activeItem.querySelector('.faq-question').setAttribute('aria-expanded', 'false');
activeItem.querySelector('.faq-answer').style.maxHeight = '0';
}
});
// 현재 FAQ 열기
item.classList.add('active');
question.setAttribute('aria-expanded', 'true');
// 정확한 높이 계산을 위해 잠시 보이게 한 후 측정
answer.style.maxHeight = 'none';
answer.style.overflow = 'visible';
const height = answer.scrollHeight;
answer.style.maxHeight = '0';
answer.style.overflow = 'hidden';
// 애니메이션을 위해 약간의 지연 후 높이 설정
setTimeout(() => {
answer.style.maxHeight = (height + 50) + 'px';
}, 10);
// 스크롤 애니메이션
setTimeout(() => {
item.scrollIntoView({
behavior: 'smooth',
block: 'nearest'
});
}, 100);
}
}
// 검색 기능 초기화
function initSearch() {
const searchInput = document.getElementById('faqSearch');
const searchBtn = document.querySelector('.search-btn');
const suggestions = document.getElementById('searchSuggestions');
if (searchInput) {
let searchTimeout;
searchInput.addEventListener('input', function() {
clearTimeout(searchTimeout);
searchTimeout = setTimeout(() => {
handleSearch(this.value.trim());
updateSearchSuggestions(this.value.trim());
}, 300);
});
searchInput.addEventListener('keydown', function(e) {
if (e.key === 'Enter') {
e.preventDefault();
handleSearch(this.value.trim());
hideSuggestions();
}
});
// 검색 버튼 클릭
if (searchBtn) {
searchBtn.addEventListener('click', () => {
handleSearch(searchInput.value.trim());
hideSuggestions();
});
}
// 클릭 외부 영역 시 제안 사항 숨기기
document.addEventListener('click', function(e) {
if (!searchInput.contains(e.target) && !suggestions?.contains(e.target)) {
hideSuggestions();
}
});
}
}
// 검색 처리
function handleSearch(query) {
const faqItems = document.querySelectorAll('.faq-item');
const noResults = document.querySelector('.no-results');
let hasResults = false;
if (!query) {
// 검색어가 없으면 모든 항목 표시
faqItems.forEach(item => {
item.classList.remove('hidden');
clearSearchHighlight(item);
});
hideNoResults();
return;
}
const searchRegex = new RegExp(query, 'gi');
faqItems.forEach(item => {
const question = item.querySelector('.faq-question h3');
const answer = item.querySelector('.faq-answer');
const questionText = question.textContent;
const answerText = answer.textContent;
// 검색어 매칭 확인
const questionMatch = searchRegex.test(questionText);
const answerMatch = searchRegex.test(answerText);
if (questionMatch || answerMatch) {
item.classList.remove('hidden');
hasResults = true;
// 검색어 하이라이트
highlightSearchTerm(item, query);
// 답변에 매칭되는 경우 자동으로 열기
if (answerMatch && !questionMatch) {
toggleFAQ(item);
}
} else {
item.classList.add('hidden');
clearSearchHighlight(item);
}
});
// 검색 결과가 없는 경우
if (!hasResults) {
showNoResults(query);
} else {
hideNoResults();
}
}
// 검색어 하이라이트
function highlightSearchTerm(item, term) {
const question = item.querySelector('.faq-question h3');
const answer = item.querySelector('.faq-answer');
const highlightRegex = new RegExp(`(${term})`, 'gi');
// 질문 하이라이트
const originalQuestionText = question.dataset.originalText || question.textContent;
question.dataset.originalText = originalQuestionText;
question.innerHTML = originalQuestionText.replace(highlightRegex, '<span class="search-highlight">$1</span>');
// 답변 하이라이트
const answerElements = answer.querySelectorAll('p, li');
answerElements.forEach(el => {
const originalText = el.dataset.originalText || el.textContent;
el.dataset.originalText = originalText;
el.innerHTML = originalText.replace(highlightRegex, '<span class="search-highlight">$1</span>');
});
}
// 검색 하이라이트 제거
function clearSearchHighlight(item) {
const question = item.querySelector('.faq-question h3');
const answer = item.querySelector('.faq-answer');
// 질문 하이라이트 제거
if (question.dataset.originalText) {
question.textContent = question.dataset.originalText;
delete question.dataset.originalText;
}
// 답변 하이라이트 제거
const answerElements = answer.querySelectorAll('p, li');
answerElements.forEach(el => {
if (el.dataset.originalText) {
el.textContent = el.dataset.originalText;
delete el.dataset.originalText;
}
});
}
// 검색 제안사항 업데이트
function updateSearchSuggestions(query) {
const suggestions = document.getElementById('searchSuggestions');
if (!suggestions || !query || query.length < 2) {
hideSuggestions();
return;
}
// 미리 정의된 검색 키워드
const searchKeywords = [
'예약', '취소', '환불', '요금', '가격', '결제',
'장비', '모션캡쳐', '시간', '인원', '준비물',
'데이터', '파일', '형식', '스트리밍', '버튜버',
'주차', '위치', '견학', '투어', '방역', '코로나'
];
const matchingKeywords = searchKeywords.filter(keyword =>
keyword.includes(query) || query.includes(keyword)
);
if (matchingKeywords.length > 0) {
suggestions.innerHTML = matchingKeywords
.slice(0, 5)
.map(keyword =>
`<div class="suggestion-item" onclick="selectSuggestion('${keyword}')">${keyword}</div>`
).join('');
suggestions.classList.add('active');
} else {
hideSuggestions();
}
}
// 제안사항 선택
function selectSuggestion(keyword) {
const searchInput = document.getElementById('faqSearch');
if (searchInput) {
searchInput.value = keyword;
handleSearch(keyword);
}
hideSuggestions();
}
// 제안사항 숨기기
function hideSuggestions() {
const suggestions = document.getElementById('searchSuggestions');
if (suggestions) {
suggestions.classList.remove('active');
}
}
// 검색 결과 없음 표시
function showNoResults(query) {
let noResults = document.querySelector('.no-results');
if (!noResults) {
noResults = document.createElement('div');
noResults.className = 'no-results';
noResults.innerHTML = `
<div class="no-results-icon">🔍</div>
<h3>검색 결과가 없습니다</h3>
<p><strong>"${query}"</strong>와 관련된 질문을 찾을 수 없습니다.</p>
<p>다른 키워드로 검색해보시거나 <a href="contact.html">직접 문의</a>해 주세요.</p>
`;
document.querySelector('.faq-list').appendChild(noResults);
} else {
noResults.querySelector('p strong').textContent = `"${query}"`;
}
noResults.classList.add('active');
}
// 검색 결과 없음 숨기기
function hideNoResults() {
const noResults = document.querySelector('.no-results');
if (noResults) {
noResults.classList.remove('active');
}
}
// 카테고리 필터 초기화
function initCategories() {
const categoryBtns = document.querySelectorAll('.category-btn');
categoryBtns.forEach(btn => {
btn.addEventListener('click', function() {
const category = this.dataset.category;
// 활성 버튼 업데이트
categoryBtns.forEach(b => b.classList.remove('active'));
this.classList.add('active');
// FAQ 필터링
filterByCategory(category);
// 검색 초기화
const searchInput = document.getElementById('faqSearch');
if (searchInput) {
searchInput.value = '';
}
hideSuggestions();
hideNoResults();
});
});
}
// 카테고리별 필터링
function filterByCategory(category) {
const faqItems = document.querySelectorAll('.faq-item');
faqItems.forEach(item => {
// 모든 FAQ 닫기
item.classList.remove('active');
item.querySelector('.faq-answer').style.maxHeight = '0';
item.querySelector('.faq-question').setAttribute('aria-expanded', 'false');
// 검색 하이라이트 제거
clearSearchHighlight(item);
if (category === 'all' || item.dataset.category === category) {
item.classList.remove('hidden');
} else {
item.classList.add('hidden');
}
});
// 페이지 상단으로 스크롤
document.querySelector('.faq-list').scrollIntoView({
behavior: 'smooth',
block: 'start'
});
}
// 애니메이션 초기화
function initAnimations() {
// Intersection Observer를 사용한 스크롤 애니메이션
const observerOptions = {
threshold: 0.1,
rootMargin: '50px 0px'
};
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry, index) => {
if (entry.isIntersecting) {
setTimeout(() => {
entry.target.style.opacity = '1';
entry.target.style.transform = 'translateY(0)';
}, index * 100);
observer.unobserve(entry.target);
}
});
}, observerOptions);
// FAQ 아이템들에 초기 스타일 적용 및 관찰 시작
const faqItems = document.querySelectorAll('.faq-item');
faqItems.forEach(item => {
item.style.opacity = '0';
item.style.transform = 'translateY(30px)';
item.style.transition = 'all 0.6s cubic-bezier(0.4, 0, 0.2, 1)';
observer.observe(item);
});
}
// URL 해시로 특정 FAQ 열기
function openFAQByHash() {
const hash = window.location.hash.substring(1);
if (hash) {
const faqItem = document.querySelector(`[data-id="${hash}"]`);
if (faqItem) {
toggleFAQ(faqItem);
faqItem.scrollIntoView({ behavior: 'smooth', block: 'center' });
}
}
}
// 페이지 로드 시 해시 확인
window.addEventListener('load', openFAQByHash);
// FAQ 공유 기능 (선택사항)
function shareFAQ(faqId) {
const url = `${window.location.origin}${window.location.pathname}#${faqId}`;
if (navigator.share) {
navigator.share({
title: 'FAQ - 밍글 스튜디오',
url: url
});
} else {
// 클립보드에 복사
navigator.clipboard.writeText(url).then(() => {
showNotification('링크가 클립보드에 복사되었습니다.', 'success');
});
}
}
// 알림 메시지 표시 (contact.js와 동일한 함수)
function showNotification(message, type = 'info') {
const existingNotification = document.querySelector('.notification');
if (existingNotification) {
existingNotification.remove();
}
const notification = document.createElement('div');
notification.className = `notification notification-${type}`;
notification.innerHTML = `
<span class="notification-message">${message}</span>
<button class="notification-close">&times;</button>
`;
Object.assign(notification.style, {
position: 'fixed',
top: '20px',
right: '20px',
padding: '1rem 1.5rem',
borderRadius: '8px',
boxShadow: '0 4px 15px rgba(0, 0, 0, 0.15)',
zIndex: '9999',
maxWidth: '400px',
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
gap: '1rem'
});
const colors = {
success: { bg: '#10b981', color: 'white' },
error: { bg: '#ef4444', color: 'white' },
info: { bg: '#3b82f6', color: 'white' }
};
const color = colors[type] || colors.info;
notification.style.backgroundColor = color.bg;
notification.style.color = color.color;
const closeBtn = notification.querySelector('.notification-close');
Object.assign(closeBtn.style, {
background: 'none',
border: 'none',
color: 'inherit',
fontSize: '1.5rem',
cursor: 'pointer',
padding: '0',
lineHeight: '1'
});
closeBtn.addEventListener('click', () => notification.remove());
document.body.appendChild(notification);
setTimeout(() => {
if (notification.parentNode) {
notification.remove();
}
}, 5000);
}
// 이메일 폼 초기화
function initEmailForm() {
const emailBtn = document.getElementById('showEmailFormFAQ');
const emailForm = document.getElementById('emailFormFAQ');
if (emailBtn && emailForm) {
emailBtn.addEventListener('click', function() {
if (emailForm.style.display === 'none' || !emailForm.style.display) {
emailForm.style.display = 'block';
emailBtn.textContent = '📧 양식 숨기기';
// FAQ 답변 높이 재계산
const faqItem = emailForm.closest('.faq-item');
const answer = faqItem.querySelector('.faq-answer');
if (faqItem.classList.contains('active')) {
// 높이 재계산을 위해 잠시 auto로 설정
answer.style.maxHeight = 'none';
const newHeight = answer.scrollHeight;
answer.style.maxHeight = (newHeight + 100) + 'px'; // 여유분 추가
}
setTimeout(() => {
emailForm.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
}, 100);
} else {
emailForm.style.display = 'none';
emailBtn.innerHTML = '<span>📧</span> 이메일 문의하기';
// FAQ 답변 높이 재계산
const faqItem = emailForm.closest('.faq-item');
const answer = faqItem.querySelector('.faq-answer');
if (faqItem.classList.contains('active')) {
answer.style.maxHeight = 'none';
const newHeight = answer.scrollHeight;
answer.style.maxHeight = (newHeight + 50) + 'px';
}
}
});
}
}