Refactor code structure for improved readability and maintainability

This commit is contained in:
“VeLiTi”
2026-02-06 11:59:13 +01:00
parent fb5951d202
commit 9212492b75
48 changed files with 13072 additions and 11329 deletions

263
script.js
View File

@@ -1,17 +1,193 @@
if (document.querySelector('.product-img-small')) {
let imgs = document.querySelectorAll('.product-img-small img');
let mainImg = document.querySelector('.product-img-large img');
// Initialize dataLayer for Google Tag Manager
window.dataLayer = window.dataLayer || [];
imgs.forEach(img => {
img.onclick = () => {
// Update main image
document.querySelector('.product-img-large img').src = img.src;
// Update selection
imgs.forEach(i => i.parentElement.classList.remove('selected'));
img.parentElement.classList.add('selected');
};
// Push virtual page view event on page load
window.dataLayer.push({
'event': 'virtualPageview',
'pagePath': window.location.pathname + window.location.search,
'pageTitle': document.title
});
// --- Product Carousel Logic ---
document.addEventListener('DOMContentLoaded', function initProductCarousel() {
const carouselMain = document.querySelector('.carousel-main');
if (!carouselMain) return;
const mainImg = carouselMain.querySelector('img');
const thumbs = Array.from(document.querySelectorAll('.carousel-thumbnails .product-img-small'));
const leftArrow = document.querySelector('.carousel-arrow.left');
const rightArrow = document.querySelector('.carousel-arrow.right');
if (!mainImg || thumbs.length === 0) return;
let current = thumbs.findIndex(t => t.classList.contains('selected'));
if (current === -1) current = 0;
let isAnimating = false;
function updateCarousel(idx, smooth = true) {
if (isAnimating || thumbs.length === 0) return;
// Infinite loop logic
if (idx < 0) idx = thumbs.length - 1;
if (idx >= thumbs.length) idx = 0;
// Animate transition
if (smooth) {
isAnimating = true;
mainImg.style.opacity = '0.5';
}
// Update selected thumbnail
thumbs.forEach(t => t.classList.remove('selected'));
thumbs[idx].classList.add('selected');
// Update main image
const newSrc = thumbs[idx].querySelector('img').src;
if (smooth) {
setTimeout(() => {
mainImg.src = newSrc;
mainImg.style.opacity = '1';
isAnimating = false;
}, 150);
} else {
mainImg.src = newSrc;
}
current = idx;
// Scroll thumbnail into view (centered)
const thumbContainer = document.querySelector('.carousel-thumbnails');
if (thumbContainer) {
const thumbElement = thumbs[idx];
const containerRect = thumbContainer.getBoundingClientRect();
const thumbRect = thumbElement.getBoundingClientRect();
const scrollLeft = thumbElement.offsetLeft - (containerRect.width / 2) + (thumbRect.width / 2);
thumbContainer.scrollTo({ left: scrollLeft, behavior: 'smooth' });
}
}
// Arrow click handlers
if (leftArrow) {
leftArrow.addEventListener('click', (e) => {
e.preventDefault();
updateCarousel(current - 1);
});
}
if (rightArrow) {
rightArrow.addEventListener('click', (e) => {
e.preventDefault();
updateCarousel(current + 1);
});
}
// Thumbnail click handlers
thumbs.forEach((thumb, idx) => {
thumb.addEventListener('click', () => updateCarousel(idx));
});
}
// Swipe support for touch devices
let touchStartX = null;
carouselMain.addEventListener('touchstart', (e) => {
touchStartX = e.touches[0].clientX;
}, { passive: true });
carouselMain.addEventListener('touchend', (e) => {
if (touchStartX === null) return;
const endX = e.changedTouches[0].clientX;
const diffX = endX - touchStartX;
// Require minimum swipe distance
if (Math.abs(diffX) > 50) {
if (diffX > 0) {
updateCarousel(current - 1); // Swipe right = previous
} else {
updateCarousel(current + 1); // Swipe left = next
}
}
touchStartX = null;
}, { passive: true });
// Keyboard navigation
document.addEventListener('keydown', (e) => {
// Only if carousel is visible in viewport
const rect = carouselMain.getBoundingClientRect();
const inViewport = rect.top < window.innerHeight && rect.bottom > 0;
if (!inViewport) return;
if (e.key === 'ArrowLeft') {
e.preventDefault();
updateCarousel(current - 1);
} else if (e.key === 'ArrowRight') {
e.preventDefault();
updateCarousel(current + 1);
}
});
// Initialize first thumbnail as selected
updateCarousel(current, false);
});
// --- Sticky Add to Basket Button ---
(function handleStickyButton() {
const btn = document.querySelector('.add-to-basket-sticky');
if (!btn) return;
// Store original position after page load
let originalBottom = null;
let isInitialized = false;
function initPosition() {
const rect = btn.getBoundingClientRect();
originalBottom = window.scrollY + rect.bottom;
isInitialized = true;
checkSticky();
}
function checkSticky() {
if (!isInitialized) return;
// On tablet/mobile (992px and below), always keep sticky
if (window.innerWidth <= 992) {
btn.classList.add('is-sticky');
return;
}
// On desktop: sticky while user hasn't scrolled past the button's original position
const viewportBottom = window.scrollY + window.innerHeight;
if (viewportBottom < originalBottom) {
// User hasn't scrolled enough to see the button naturally
btn.classList.add('is-sticky');
} else {
// User has scrolled past where the button would naturally be
btn.classList.remove('is-sticky');
}
}
function handleResize() {
// Recalculate position on resize
btn.classList.remove('is-sticky');
setTimeout(() => {
initPosition();
}, 100);
}
// Initialize after DOM is ready and images are loaded
if (document.readyState === 'complete') {
initPosition();
} else {
window.addEventListener('load', initPosition);
}
window.addEventListener('scroll', checkSticky, { passive: true });
window.addEventListener('resize', handleResize, { passive: true });
})();
if (document.querySelector('.product #product-form')) {
let updatePrice = () => {
let price = parseFloat(document.querySelector('.product .price').dataset.price);
@@ -45,6 +221,40 @@ if (document.querySelector('.product #product-form')) {
};
document.querySelectorAll('.product #product-form .option').forEach(ele => ele.onchange = () => updatePrice());
updatePrice();
// Add to cart event
document.querySelector('.product #product-form').addEventListener('submit', function(e) {
e.preventDefault(); // Prevent immediate submission
let productId = document.querySelector('input[name="product[product]"]').value;
let quantity = parseInt(document.querySelector('input[name="product[quantity]"]').value);
let productName = document.querySelector('.product .name').textContent;
let price = parseFloat(document.querySelector('.product .price').dataset.price);
window.dataLayer.push({
'event': 'addToCart',
'ecommerce': {
'add': {
'products': [{
'id': productId,
'name': productName,
'price': price,
'quantity': quantity
}]
}
},
'content_type': 'product',
'content_ids': [productId],
'content_name': productName,
'value': price * quantity,
'currency': 'EUR'
});
// Submit form after a short delay
setTimeout(() => {
e.target.submit();
}, 100);
});
}
if (document.querySelector('.products-form')) {
let products_form_submit = () => {
@@ -74,6 +284,11 @@ if (document.querySelector('.cart .ajax-update')) {
document.querySelectorAll('.product-total').forEach((e,i) => {
e.innerHTML = doc.querySelectorAll('.product-total')[i].innerHTML;
});
// Push cart update event
window.dataLayer.push({
'event': 'updateCart'
});
});
};
});
@@ -94,6 +309,12 @@ const checkoutHandler = () => {
document.querySelector('.total').innerHTML = doc.querySelector('.total').innerHTML;
document.querySelector('.discount-code .result').innerHTML = doc.querySelector('.discount-code .result').innerHTML;
document.querySelector('.shipping-methods-container').innerHTML = doc.querySelector('.shipping-methods-container').innerHTML;
// Push checkout update event
window.dataLayer.push({
'event': 'checkoutUpdate'
});
checkoutHandler();
});
};
@@ -130,6 +351,13 @@ function update(id_large, IMG_large, option_id, price){
function updateOption(id_large, IMG_large){
//change picture
document.getElementById(id_large).src = IMG_large;
// Update the first thumbnail to match the new main image
let firstThumbnail = document.querySelector('.product-img-small img');
if (firstThumbnail) {
firstThumbnail.src = IMG_large;
firstThumbnail.parentElement.classList.add('selected');
}
}
@@ -217,12 +445,5 @@ function initializeCarousels() {
});
}
// Initialize carousels when DOM is loaded
document.addEventListener('DOMContentLoaded', initializeCarousels);
// Also initialize if DOM is already loaded
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initializeCarousels);
} else {
initializeCarousels();
}
// Only initialize carousels once DOM is loaded
// (No duplicate or stray HTML allowed)