1/** 2 * Sticky Add to Cart - Mobile Only 3 * Affiche un bouton fixé en bas quand le bouton original a été dépassé (scroll vers le bas) 4 * Compatible avec les changements de variantes PrestaShop (AJAX reload) 5 */ 6 7(function() { 8 'use strict'; 9 10 // Configuration 11 const CONFIG = { 12 mobileBreakpoint: 991, 13 throttleDelay: 100, 14 scrollOffset: 100 // Marge en pixels au-dessus du bouton original 15 }; 16 17 // Ãléments DOM (réinitialisés après chaque update) 18 let originalButton = null; 19 let stickyContainer = null; 20 let stickyButton = null; 21 22 /** 23 * Vérifie si on est sur mobile 24 */ 25 function isMobile() { 26 return window.innerWidth <= CONFIG.mobileBreakpoint; 27 } 28 29 /** 30 * Throttle function pour optimiser les performances 31 */ 32 function throttle(func, limit) { 33 let inThrottle; 34 return function(...args) { 35 if (!inThrottle) { 36 func.apply(this, args); 37 inThrottle = true; 38 setTimeout(() => inThrottle = false, limit); 39 } 40 }; 41 } 42 43 /** 44 * Vérifie si le bouton original a été dépassé (scrollé en dessous) 45 * Retourne true si le bas du bouton est AU-DESSUS du viewport 46 */ 47 function hasScrolledPastButton(el) { 48 if (!el) return false; 49 50 const rect = el.getBoundingClientRect(); 51 return rect.bottom < 0; 52 } 53 54 /** 55 * Scroll vers le bouton original 56 */ 57 function scrollToOriginalButton() { 58 if (!originalButton) return; 59 60 const rect = originalButton.getBoundingClientRect(); 61 const absoluteTop = rect.top + window.pageYOffset; 62 63 window.scrollTo({ 64 top: absoluteTop - CONFIG.scrollOffset, 65 behavior: 'smooth' 66 }); 67 } 68 69 /** 70 * Rafraîchit les références DOM (appelé après chaque update produit) 71 */ 72 function refreshDOMReferences() { 73 originalButton = document.querySelector('#js-original-add-to-cart'); 74 stickyContainer = document.querySelector('#js-sticky-add-to-cart'); 75 stickyButton = document.querySelector('#js-sticky-add-to-cart-btn'); 76 77 // Réattacher le click handler sur le nouveau bouton sticky 78 if (stickyButton) { 79 stickyButton.removeEventListener('click', handleStickyClick); 80 stickyButton.addEventListener('click', handleStickyClick); 81 } 82 83 // Mettre à jour l'état disabled 84 updateStickyButtonState(); 85 86 // Mettre à jour la visibilité 87 updateStickyVisibility(); 88 } 89 90 /** 91 * Met à jour la visibilité du sticky 92 */ 93 function updateStickyVisibility() { 94 if (!isMobile()) { 95 hideStickyButton(); 96 return; 97 } 98 99 // Rafraîchir les références si elles sont nulles 100 if (!originalButton || !stickyContainer) { 101 refreshDOMReferences(); 102 } 103 104 if (!originalButton || !stickyContainer) { 105 return; 106 } 107 108 const hasScrolledPast = hasScrolledPastButton(originalButton); 109 110 if (hasScrolledPast) { 111 showStickyButton(); 112 } else { 113 hideStickyButton(); 114 } 115 } 116 117 /** 118 * Affiche le bouton sticky 119 */ 120 function showStickyButton() { 121 if (stickyContainer && !stickyContainer.classList.contains('is-visible')) { 122 stickyContainer.classList.add('is-visible'); 123 document.body.classList.add('sticky-cart-active'); 124 } 125 } 126 127 /** 128 * Cache le bouton sticky 129 */ 130 function hideStickyButton() { 131 if (stickyContainer && stickyContainer.classList.contains('is-visible')) { 132 stickyContainer.classList.remove('is-visible'); 133 document.body.classList.remove('sticky-cart-active'); 134 } 135 } 136 137 /** 138 * Gère le clic sur le bouton sticky 139 */ 140 function handleStickyClick(e) { 141 e.preventDefault(); 142 143 // Scroll vers le bouton original 144 scrollToOriginalButton(); 145 146 // Trouve le vrai bouton add-to-cart dans le formulaire 147 const realButton = document.querySelector('#js-original-add-to-cart .add-to-cart'); 148 149 if (realButton && !realButton.disabled) { 150 // Petit délai pour laisser le scroll se faire 151 setTimeout(function() { 152 realButton.click(); 153 }, 300); 154 } 155 } 156 157 /** 158 * Met à jour l'état disabled du bouton sticky 159 */ 160 function updateStickyButtonState() { 161 const realButton = document.querySelector('#js-original-add-to-cart .add-to-cart'); 162 163 if (realButton && stickyButton) { 164 stickyButton.disabled = realButton.disabled; 165 } 166 } 167 168 /** 169 * Initialisation 170 */
171 function init() { 172 // Rafraîchir les références DOM 173 refreshDOMReferences(); 174 175 // Event listeners globaux (une seule fois) 176 window.addEventListener('scroll', throttle(updateStickyVisibility, CONFIG.throttleDelay), { passive: true }); 177 window.addEventListener('resize', throttle(updateStickyVisibility, CONFIG.throttleDelay), { passive: true }); 178 179 // Ãcouter les événements PrestaShop de mise à jour produit (changement variante) 180 if (typeof prestashop !== 'undefined') { 181 prestashop.on('updatedProduct', function() { 182 // Attendre que le DOM soit mis à jour 183 setTimeout(function() { 184 refreshDOMReferences(); 185 }, 150); 186 }); 187 } 188 189 // Check initial 190 updateStickyVisibility(); 191 } 192 193 // Lancement 194 if (document.readyState === 'loading') { 195 document.addEventListener('DOMContentLoaded', init); 196 } else { 197 init(); 198 } 199 200})();
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.