PageSourceSearch

https://bestfoodnetwork.com/wp-content/plugins/listeo-elementor/as…/js/taxonomy-responsive-slider.js?ver=1.0.0

js bestfoodnetwork.com collected 2026-10-03 01:36:02 UTC 11,029 bytes, 279 lines download raw bytes

1/**
2 * Taxonomy Responsive Slider - Slick Style Navigation
3 * Works with existing category grid structure, moves 1 item at a time
4 * Activates only below 992px with Slick-style dots navigation
5 */
6
7document.addEventListener("DOMContentLoaded", function () {
8    // Only run on screens smaller than 992px
9    function initTaxonomySliders() {
10        if (window.innerWidth >= 992) {
11            return; // Don't initialize on desktop
12        }
13
14        const wrappers = document.querySelectorAll('.taxonomy-slider-wrapper:not(.no-mobile-carousel)');
15
16        wrappers.forEach(function(wrapper) {
17            // Skip if wrapper is hidden by Elementor responsive settings
18            if (wrapper.offsetParent === null || wrapper.offsetWidth === 0 || wrapper.offsetHeight === 0) {
19                return;
20            }
21
22            const slider = wrapper.querySelector('.taxonomy-responsive-slider');
23            const controlsContainer = wrapper.querySelector('.taxonomy-slider-controls-container');
24
25            if (!slider || !controlsContainer) {
26                return;
27            }
28
29            const prevButton = controlsContainer.querySelector('.taxonomy-slide-prev');
30            const nextButton = controlsContainer.querySelector('.taxonomy-slide-next');
31            const dotsContainer = controlsContainer.querySelector('.taxonomy-dots');
32            const items = slider.querySelectorAll('.category-small-box, .category-small-box-alt');
33
34            if (!prevButton || !nextButton || !dotsContainer || items.length === 0) {
35                return;
36            }
37
38            let currentPosition = 0;
39
40            // Calculate actual item width dynamically (responsive width + gap)
41            function getItemWidth() {
42                if (items.length > 0) {
43                    const itemRect = items[0].getBoundingClientRect();
44                    const itemStyle = window.getComputedStyle(items[0]);
45                    const marginRight = parseFloat(itemStyle.marginRight) || 0;
46                    return itemRect.width + marginRight + 20; // Adding gap
47                }
48                // Fallback calculation based on screen width
49                if (window.innerWidth <= 480) {
50                    return (window.innerWidth / 2 - 10) + 20; // 2 items per screen + gap
51                }
52                return (window.innerWidth / 3 - 20) + 20; // 3 items per screen + gap
53            }
54
55            // Calculate maximum allowed movement
56            function getMaxTranslateX() {
57                const itemWidth = getItemWidth();
58                const totalContentWidth = items.length * itemWidth;
59                const visibleWidth = wrapper.clientWidth;
60
61                if (totalContentWidth <= visibleWidth) {
62                    return 0;
63                }
64
65                return Math.max(0, totalContentWidth - visibleWidth - 20);
66            }
67
68            // Calculate visible items
69            function calculateVisibleItems() {
70                const containerWidth = wrapper.clientWidth;
71                const itemWidth = getItemWidth();
72                return Math.floor(containerWidth / itemWidth);
73            }
74
75            // Calculate total number of slides (pages)
76            function getTotalSlides() {
77                const visibleItems = calculateVisibleItems();
78                return Math.ceil(items.length / visibleItems);
79            }
80
81            // Generate dots
82            function generateDots() {
83                const totalSlides = getTotalSlides();
84                dotsContainer.innerHTML = '';
85
86                for (let i = 0; i < totalSlides; i++) {
87                    const li = document.createElement('li');
88                    li.setAttribute('role', 'presentation');
89                    li.setAttribute('aria-selected', i === 0 ? 'true' : 'false');
90                    li.setAttribute('aria-controls', `navigation${i}`);
91                    li.setAttribute('id', `slick-slide${i}`);
92                    if (i === 0) {
93                        li.classList.add('active');
94                    }
95
96                    const button = document.createElement('button');
97                    button.type = 'button';
98                    button.setAttribute('data-role', 'none');
99                    button.setAttribute('role', 'button');
100                    button.setAttribute('tabindex', '0');
101                    button.textContent = (i + 1).toString();
102
103                    li.appendChild(button);
104                    dotsContainer.appendChild(li);
105
106                    // Add click event to dot
107                    li.addEventListener('click', function() {
108                        goToSlide(i);
109                    });
110                }
111            }
112
113            // Go to specific slide
114            function goToSlide(slideIndex) {
115                const visibleItems = calculateVisibleItems();
116                const targetPosition = slideIndex * visibleItems;
117                
118                // Ensure we don't go beyond the last item
119                currentPosition = Math.min(targetPosition, items.length - visibleItems);
120                currentPosition = Math.max(0, currentPosition);
121                
122                updateSliderPosition();
123                updateDots();
124            }
125
126            // Update dots active state
127            function updateDots() {
128                const dots = dotsContainer.querySelectorAll('li');
129                const visibleItems = calculateVisibleItems();
130                const currentSlide = Math.floor(currentPosition / visibleItems);
131
132                dots.forEach((dot, index) => {
133                    dot.classList.remove('active');
134                    dot.setAttribute('aria-selected', 'false');
135                    if (index === currentSlide) {
136                        dot.classList.add('active');
137                        dot.setAttribute('aria-selected', 'true');
138                    }
139                });
140            }
141
142            function updateSliderPosition() {
143                const itemWidth = getItemWidth();
144                const requestedTranslateX = currentPosition * itemWidth;
145                const maxTranslateX = getMaxTranslateX();
146                const actualTranslateX = Math.min(requestedTranslateX, maxTranslateX);
147
148                slider.style.transform = `translateX(-${actualTranslateX}px)`;
149                updateNavigationButtons();
150                updateDots();
151            }
152
153            function updateNavigationButtons() {
154                // Hide prev button if at the beginning
155                if (currentPosition <= 0) {
156                    prevButton.classList.add('slick-disabled');
157                } else {
158                    prevButton.classList.remove('slick-disabled');
159                }
160
161                // Hide next button when the NEXT move would exceed the max translation
162                const itemWidth = getItemWidth();
163                const nextPositionTranslateX = (currentPosition + 1) * itemWidth;
164                const maxTranslateX = getMaxTranslateX();
165
166                if (nextPositionTranslateX > maxTranslateX) {
167                    nextButton.classList.add('slick-disabled');
168                } else {
169                    nextButton.classList.remove('slick-disabled');
170                }
171            }
172
173            // Previous button click - move by 1 item
174            prevButton.addEventListener("click", function (e) {
175                e.preventDefault();
176                e.stopPropagation();
177
178                if (currentPosition > 0) {
179                    currentPosition--;
180                    updateSliderPosition();
181                }
182            });
183
184            // Next button click - simple increment with boundary check
185            nextButton.addEventListener("click", function (e) {
186                e.preventDefault();
187                e.stopPropagation();
188
189                // Check if we can move more (not at max translation)
190                const itemWidth = getItemWidth();
191                const requestedTranslateX = (currentPosition + 1) * itemWidth;
192                const maxTranslateX = getMaxTranslateX();
193
194                if (requestedTranslateX <= maxTranslateX) {
195                    currentPosition++;
196                    updateSliderPosition();
197                }
198            });
199
200            // Handle window resize
201            function handleResize() {
202                if (window.innerWidth >= 992) {
203                    slider.style.transform = 'none';
204                    return;
205                }
206
207                const visibleItems = calculateVisibleItems();
208                currentPosition = Math.min(
209                    currentPosition,
210                    Math.max(0, items.length - visibleItems)
211                );
212                
213                generateDots(); // Regenerate dots on resize
214                updateSliderPosition();
215            }
216
217            window.addEventListener("resize", handleResize);
218
219            // Touch support
220            let startX = 0;
221            let endX = 0;
222            let touchStartTime = 0;
223
224            slider.addEventListener("touchstart", function (e) {
225                startX = e.touches[0].clientX;
226                touchStartTime = Date.now();
227            });
228
229            slider.addEventListener("touchmove", function (e) {
230                endX = e.touches[0].clientX;
231            });
232
233            slider.addEventListener("touchend", function (e) {
234                const deltaX = endX - startX;
235                const touchDuration = Date.now() - touchStartTime;
236
237                // Only treat as swipe if movement is significant and quick
238                if (Math.abs(deltaX) > 50 && touchDuration < 300) {
239                    if (deltaX > 0) {
240                        // Swipe right - go to previous
241                        if (currentPosition > 0) {
242                            currentPosition--;
243                            updateSliderPosition();
244                        }
245                    } else {
246                        // Swipe left - go to next
247                        const itemWidth = getItemWidth();
248                        const requestedTranslateX = (currentPosition + 1) * itemWidth;
249                        const maxTranslateX = getMaxTranslateX();
250
251                        if (requestedTranslateX <= maxTranslateX) {
252                            currentPosition++;
253                            updateSliderPosition();
254                        }
255                    }
256                }
257            });
258
259            // Initialize slider
260            generateDots();
261            updateSliderPosition();
262        });
263    }
264
265    // Initialize sliders
266    initTaxonomySliders();
267
268    // Re-initialize on window resize (in case screen orientation changes)
269    let resizeTimeout;
270    window.addEventListener("resize", function() {
271        clearTimeout(resizeTimeout);
272        resizeTimeout = setTimeout(function() {
273            // Re-initialize if we're now on mobile
274            if (window.innerWidth < 992) {
275                initTaxonomySliders();
276            }
277        }, 250);
278    });
279});

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.