File manager - Edit - /home/webapp69.cm.in.th/u69319090012/portfolio/script.js
Back
document.addEventListener('DOMContentLoaded', () => { /* ========================================================================== 1. MOBILE HAMBURGER MENU ========================================================================== */ const hamburger = document.getElementById('hamburger-menu-toggle'); const navMenu = document.getElementById('nav-menu-list'); const navLinks = document.querySelectorAll('.nav-link'); const bottomNavLinks = document.querySelectorAll('.bottom-nav-item'); const toggleMenu = () => { hamburger.classList.toggle('active'); navMenu.classList.toggle('active'); // Prevent body scroll when menu is active document.body.style.overflow = navMenu.classList.contains('active') ? 'hidden' : ''; }; const closeMenu = () => { hamburger.classList.remove('active'); navMenu.classList.remove('active'); document.body.style.overflow = ''; }; if (hamburger && navMenu) { hamburger.addEventListener('click', toggleMenu); } // Close menu when links are clicked navLinks.forEach(link => { link.addEventListener('click', closeMenu); }); // Handle bottom navigation active state on click bottomNavLinks.forEach(link => { link.addEventListener('click', () => { bottomNavLinks.forEach(item => item.classList.remove('active')); link.classList.add('active'); }); }); /* ========================================================================== 2. SCROLL HEADER STYLING ========================================================================== */ const header = document.getElementById('main-header'); const handleScroll = () => { if (window.scrollY > 50) { header.classList.add('scrolled'); } else { header.classList.remove('scrolled'); } }; window.addEventListener('scroll', handleScroll); handleScroll(); // Initial check /* ========================================================================== 3. ACTIVE LINK HIGHLIGHT ON SCROLL (Intersection Observer) ========================================================================== */ const sections = document.querySelectorAll('section[id]'); const observerOptions = { root: null, rootMargin: '-20% 0px -60% 0px', // Trigger when section occupies center part threshold: 0 }; const observerCallback = (entries) => { entries.forEach(entry => { if (entry.isIntersecting) { const id = entry.target.getAttribute('id'); navLinks.forEach(link => { link.classList.remove('active'); if (link.getAttribute('href') === `#${id}`) { link.classList.add('active'); } }); bottomNavLinks.forEach(link => { link.classList.remove('active'); if (link.getAttribute('href') === `#${id}`) { link.classList.add('active'); } }); } }); }; const sectionObserver = new IntersectionObserver(observerCallback, observerOptions); sections.forEach(section => { sectionObserver.observe(section); }); /* ========================================================================== 4. SCROLL REVEAL ANIMATION (Intersection Observer) ========================================================================== */ const revealElements = document.querySelectorAll('.reveal'); const revealObserverOptions = { root: null, rootMargin: '0px 0px -80px 0px', threshold: 0.15 }; const revealCallback = (entries, observer) => { entries.forEach(entry => { if (entry.isIntersecting) { entry.target.classList.add('revealed'); // Once animate is done, no need to watch it again observer.unobserve(entry.target); } }); }; const revealObserver = new IntersectionObserver(revealCallback, revealObserverOptions); revealElements.forEach(el => { revealObserver.observe(el); }); /* ========================================================================== 5. PORTFOLIO FILTERING SYSTEM ========================================================================== */ const filterButtons = document.querySelectorAll('.filter-btn'); const projectCards = document.querySelectorAll('.project-card'); filterButtons.forEach(btn => { btn.addEventListener('click', () => { // Update active state on buttons filterButtons.forEach(b => b.classList.remove('active')); btn.classList.add('active'); const filterValue = btn.getAttribute('data-filter'); projectCards.forEach(card => { const category = card.getAttribute('data-category'); // Add exit transition first card.style.opacity = '0'; card.style.transform = 'scale(0.9) translateY(10px)'; setTimeout(() => { if (filterValue === 'all' || category === filterValue) { card.style.display = 'flex'; // Re-trigger visual style setTimeout(() => { card.style.opacity = '1'; card.style.transform = 'scale(1) translateY(0)'; }, 50); } else { card.style.display = 'none'; } }, 300); }); }); }); /* ========================================================================== 6. CONTACT FORM SUBMISSION TOAST SIMULATION ========================================================================== */ const contactForm = document.getElementById('contact-form-element'); const toast = document.getElementById('success-toast'); if (contactForm && toast) { contactForm.addEventListener('submit', (e) => { e.preventDefault(); // Perform visual submit animations const submitBtn = document.getElementById('form-submit-btn'); const originalContent = submitBtn.innerHTML; submitBtn.disabled = true; submitBtn.innerHTML = '<i class="fa-solid fa-spinner fa-spin"></i> กำลังส่ง...'; // Simulate server network latency setTimeout(() => { // Success actions submitBtn.disabled = false; submitBtn.innerHTML = originalContent; // Reset form fields contactForm.reset(); // Trigger Toast Notification toast.classList.add('show'); // Hide Toast after 4 seconds setTimeout(() => { toast.classList.remove('show'); }, 4000); }, 1500); }); } // Add CSS transition values inline for dynamic filtering projectCards.forEach(card => { card.style.transition = 'opacity 0.3s ease, transform 0.3s ease, border-color 0.4s ease, box-shadow 0.4s ease'; }); // Theme Toggle Logic const themeToggleBtn = document.getElementById('theme-toggle'); if (themeToggleBtn) { const themeIcon = themeToggleBtn.querySelector('i'); const savedTheme = localStorage.getItem('portfolio-theme') || 'dark'; if (savedTheme === 'light') { document.body.classList.add('light-theme'); if (themeIcon) themeIcon.className = 'fa-solid fa-moon'; } else { document.body.classList.remove('light-theme'); if (themeIcon) themeIcon.className = 'fa-solid fa-sun'; } themeToggleBtn.addEventListener('click', () => { document.body.classList.toggle('light-theme'); const isLight = document.body.classList.contains('light-theme'); if (isLight) { if (themeIcon) themeIcon.className = 'fa-solid fa-moon'; localStorage.setItem('portfolio-theme', 'light'); } else { if (themeIcon) themeIcon.className = 'fa-solid fa-sun'; localStorage.setItem('portfolio-theme', 'dark'); } }); } });
| ver. 1.4 |
Github
|
.
| PHP 7.4.33 | Generation time: 0.33 |
proxy
|
phpinfo
|
Settings