PageSourceSearch

https://www.accademiamoda.it/wp-content/themes/iuad-theme/src/js/custom/secondary-menu-anchors.js?ver=1.0.0

js accademiamoda.it collected 2026-10-04 13:24:19 UTC 12,218 bytes, 309 lines download raw bytes

1/**
2 * Secondary Menu Anchors Navigation
3 * Gestisce la navigazione anchor senza modificare l'URL e lo stato "active" basato sulla visibilità
4 */
5
6(function() {
7    'use strict';
8
9    document.addEventListener('DOMContentLoaded', function() {
10        // Seleziona solo il menu con classe "secondary-menu--anchors"
11        const anchorMenu = document.querySelector('.secondary-menu--anchors');
12        
13        if (!anchorMenu) {
14            return; // Se il menu non esiste, esci
15        }
16
17        const anchorLinks = anchorMenu.querySelectorAll('a[data-anchor]');
18        
19        if (anchorLinks.length === 0) {
20            return;
21        }
22
23        // Mappa per associare gli ID delle sezioni ai link
24        const sectionMap = new Map();
25        const sections = [];
26
27        // Prepara la mappa e raccoglie le sezioni
28        anchorLinks.forEach(link => {
29            const anchorId = link.getAttribute('data-anchor');
30            if (anchorId) {
31                const section = document.getElementById(anchorId);
32                if (section) {
33                    sectionMap.set(anchorId, {
34                        link: link,
35                        section: section
36                    });
37                    sections.push(section);
38                }
39            }
40        });
41
42        if (sections.length === 0) {
43            return;
44        }
45
46        /**
47         * Rimuove la classe "active" da tutti i link
48         */
49        function removeActiveClass() {
50            anchorLinks.forEach(link => {
51                link.classList.remove('active');
52            });
53        }
54
55        /**
56         * Aggiunge la classe "active" al link corrispondente
57         */
58        function setActiveLink(anchorId) {
59            removeActiveClass();
60            const mapped = sectionMap.get(anchorId);
61            if (mapped && mapped.link) {
62                mapped.link.classList.add('active');
63            }
64        }
65
66        /**
67         * Scroll smooth alla sezione senza modificare l'URL
68         */
69        function scrollToSection(section) {
70            // Calcola la posizione della sezione
71            const sectionTop = section.getBoundingClientRect().top + window.pageYOffset;
72            
73            // Offset di 50px sopra al div
74            const offset = 50;
75            
76            // Scroll smooth
77            window.scrollTo({
78                top: sectionTop - offset,
79                behavior: 'smooth'
80            });
81        }
82
83        /**
84         * Gestisce il click sui link
85         */
86        anchorLinks.forEach(link => {
87            link.addEventListener('click', function(e) {
88                e.preventDefault();
89                e.stopPropagation();
90                const anchorId = this.getAttribute('data-anchor');
91                const mapped = sectionMap.get(anchorId);
92                
93                if (mapped && mapped.section) {
94                    scrollToSection(mapped.section);
95                    // Imposta temporaneamente come active (l'observer aggiornerà se necessario)
96                    setActiveLink(anchorId);
97                }
98            });
99        });
100
101        /**
102         * Intersection Observer per rilevare quando una sezione è visibile
103         */
104        const observerOptions = {
105            root: null, // viewport
106            rootMargin: '-50px 0px -70% 0px', // Considera l'offset di 50px, la sezione è attiva quando è nel 30% superiore del viewport
107            threshold: [0, 0.1, 0.25, 0.5, 0.75, 1.0] // Threshold multipli per maggiore precisione
108        };
109
110        // Traccia tutte le sezioni visibili con i loro dati
111        const visibleSectionsData = new Map();
112
113        const observer = new IntersectionObserver(function(entries) {
114            // Aggiorna i dati delle sezioni visibili
115            entries.forEach(entry => {
116                const section = entry.target;
117                if (entry.isIntersecting) {
118                    const rect = section.getBoundingClientRect();
119                    const distanceFromTop = Math.abs(rect.top - 50); // Distanza dal top con offset di 50px
120                    const visibleHeight = Math.min(rect.bottom, window.innerHeight) - Math.max(rect.top, 0);
121                    const ratio = rect.height > 0 ? visibleHeight / rect.height : 0;
122                    
123                    visibleSectionsData.set(section, {
124                        distanceFromTop: distanceFromTop,
125                        ratio: ratio,
126                        intersectionRatio: entry.intersectionRatio
127                    });
128                } else {
129                    visibleSectionsData.delete(section);
130                }
131            });
132
133            // Se non ci sono sezioni visibili, rimuovi l'active
134            if (visibleSectionsData.size === 0) {
135                removeActiveClass();
136                return;
137            }
138
139            // Trova la sezione più visibile (quella con il maggior ratio e più vicina al top)
140            let mostVisible = null;
141            let bestScore = -1;
142
143            visibleSectionsData.forEach((data, section) => {
144                // Calcola uno score combinando ratio e distanza dal top
145                // Più alto è il ratio e più vicina è al top, migliore è lo score
146                const score = data.ratio * 100 - (data.distanceFromTop / 10);
147                
148                // Considera solo sezioni con almeno 10% di visibilità e vicine al top (entro 200px)
149                if (data.ratio >= 0.1 && data.distanceFromTop <= 200 && score > bestScore) {
150                    bestScore = score;
151                    mostVisible = section;
152                }
153            });
154
155            // Se c'è una sezione visibile che soddisfa i criteri, attiva il link corrispondente
156            if (mostVisible) {
157                const anchorId = mostVisible.getAttribute('id');
158                if (anchorId) {
159                    setActiveLink(anchorId);
160                }
161            } else {
162                // Nessuna sezione soddisfa i criteri, rimuovi l'active
163                removeActiveClass();
164            }
165        }, observerOptions);
166
167        // Osserva tutte le sezioni
168        sections.forEach(section => {
169            observer.observe(section);
170        });
171
172        // Gestisce anche lo scroll manuale per aggiornare lo stato active
173        let scrollTimeout;
174        window.addEventListener('scroll', function() {
175            clearTimeout(scrollTimeout);
176            scrollTimeout = setTimeout(function() {
177                // Trova la sezione più vicina al top del viewport (considerando l'offset di 50px)
178                let closestSection = null;
179                let closestDistance = Infinity;
180                const offset = 50;
181
182                sections.forEach(section => {
183                    const rect = section.getBoundingClientRect();
184                    // Considera la posizione della sezione rispetto al top del viewport con offset
185                    const sectionTop = rect.top;
186                    
187                    // Una sezione è considerata "attiva" se:
188                    // - È sopra o vicino al top del viewport (con offset di 50px)
189                    // - È almeno parzialmente visibile nel viewport
190                    const isAboveOrNearTop = sectionTop <= offset + 100; // 100px di tolleranza
191                    const isVisible = rect.top < window.innerHeight && rect.bottom > 0;
192                    
193                    if (isVisible && isAboveOrNearTop) {
194                        const distance = Math.abs(sectionTop - offset);
195                        if (distance < closestDistance) {
196                            closestDistance = distance;
197                            closestSection = section;
198                        }
199                    }
200                });
201
202                if (closestSection) {
203                    const anchorId = closestSection.getAttribute('id');
204                    if (anchorId) {
205                        setActiveLink(anchorId);
206                    }
207                } else {
208                    // Nessuna sezione con anchor è visibile o vicina al top, rimuovi l'active
209                    removeActiveClass();
210                }
211            }, 100);
212        });
213
214        // Scroll orizzontale con rotella del mouse e drag per menu scrollable
215        if (anchorMenu.classList.contains('secondary-menu__buttons--scrollable')) {
216            const scrollableMenu = anchorMenu.querySelector('.secondary-menu__buttons');
217            
218            if (scrollableMenu) {
219                // Variabili per il drag
220                let isDragging = false;
221                let startX = 0;
222                let scrollLeft = 0;
223                let hasMoved = false;
224                let preventClick = false;
225
226                // Scroll orizzontale con la rotella del mouse
227                scrollableMenu.addEventListener('wheel', function(e) {
228                    // Previeni lo scroll verticale della pagina quando si scrolla orizzontalmente
229                    if (Math.abs(e.deltaX) < Math.abs(e.deltaY)) {
230                        e.preventDefault();
231                        // Converti lo scroll verticale in orizzontale
232                        scrollableMenu.scrollLeft += e.deltaY;
233                    }
234                }, { passive: false });
235
236                // Drag/Trascinamento per scorrere
237                scrollableMenu.addEventListener('mousedown', function(e) {
238                    // Non attivare il drag se si clicca direttamente su un link
239                    if (e.target.tagName === 'A' || e.target.closest('a')) {
240                        return;
241                    }
242
243                    isDragging = true;
244                    hasMoved = false;
245                    preventClick = false;
246                    startX = e.pageX - scrollableMenu.offsetLeft;
247                    scrollLeft = scrollableMenu.scrollLeft;
248                    
249                    // Cambia il cursore
250                    scrollableMenu.style.cursor = 'grabbing';
251                    scrollableMenu.style.userSelect = 'none';
252
253                    // Previeni il comportamento di default
254                    e.preventDefault();
255                });
256
257                scrollableMenu.addEventListener('mouseleave', function() {
258                    if (isDragging) {
259                        isDragging = false;
260                        scrollableMenu.style.cursor = 'grab';
261                        scrollableMenu.style.userSelect = '';
262                    }
263                });
264
265                scrollableMenu.addEventListener('mouseup', function(e) {
266                    if (isDragging) {
267                        isDragging = false;
268                        scrollableMenu.style.cursor = 'grab';
269                        scrollableMenu.style.userSelect = '';
270                        
271                        // Se c'è stato movimento, previeni il click sui link
272                        if (hasMoved) {
273                            preventClick = true;
274                            setTimeout(function() {
275                                preventClick = false;
276                            }, 100);
277                        }
278                    }
279                });
280
281                scrollableMenu.addEventListener('mousemove', function(e) {
282                    if (!isDragging) {
283                        return;
284                    }
285
286                    e.preventDefault();
287                    hasMoved = true;
288                    const x = e.pageX - scrollableMenu.offsetLeft;
289                    const walk = (x - startX) * 2; // Moltiplica per velocità maggiore
290                    scrollableMenu.scrollLeft = scrollLeft - walk;
291                });
292
293                // Imposta il cursore iniziale
294                scrollableMenu.style.cursor = 'grab';
295
296                // Previeni il click sui link se c'è stato movimento durante il drag
297                anchorLinks.forEach(link => {
298                    link.addEventListener('click', function(e) {
299                        if (preventClick) {
300                            e.preventDefault();
301                            e.stopPropagation();
302                        }
303                    }, true);
304                });
305            }
306        }
307    });
308})();
309

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.