Refactor code structure for improved readability and maintainability
This commit is contained in:
263
script.js
263
script.js
@@ -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)
|
||||
Reference in New Issue
Block a user