PageSourceSearch

https://www.mablouseblanche.fr/js/autocomplete_mbb.js?ver=1790911304

js mablouseblanche.fr collected 2026-10-02 03:22:05 UTC 8,063 bytes, 228 lines download raw bytes

1// Autocomplete pour le champ de recherche de ville
2(function() {
3    const input = document.getElementById('citySearch');
4    if (!input) return;
5
6    const API_URL = '/comparator-new/api/api_autocomplete_cities.php';
7    const MIN_CHARS = 3;
8    const LISTBOX_ID = 'autocomplete-listbox';
9    
10    let debounceTimer = null;
11    let autocompleteList = null;
12
13
14    // ARIA : accessibilité pour le seo
15    // ARIA : Configurer l'input comme combobox
16    input.setAttribute('role', 'combobox');
17    input.setAttribute('aria-autocomplete', 'list');
18    input.setAttribute('aria-expanded', 'false');
19    input.setAttribute('aria-controls', LISTBOX_ID);
20    input.setAttribute('aria-haspopup', 'listbox');
21
22    // Créer la liste de suggestions (flottante)
23    function createAutocompleteList() {
24        // Supprimer l'ancienne liste si elle existe
25        removeAutocompleteList();
26        
27        autocompleteList = document.createElement('div');
28        autocompleteList.setAttribute('id', LISTBOX_ID);
29        autocompleteList.setAttribute('class', 'autocomplete-items');
30        autocompleteList.setAttribute('role', 'listbox');
31        autocompleteList.setAttribute('aria-label', 'Suggestions de villes');
32        
33        // Ajouter au body pour qu'elle flotte
34        document.body.appendChild(autocompleteList);
35        
36        // ARIA : Marquer la liste comme ouverte
37        input.setAttribute('aria-expanded', 'true');
38        
39        // Positionner sous l'input
40        positionAutocompleteList();
41    }
42    
43    // Positionner la liste sous l'input
44    function positionAutocompleteList() {
45        if (!autocompleteList) return;
46        
47        const rect = input.getBoundingClientRect();
48        const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
49        const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft;
50        
51        autocompleteList.style.cssText = `
52            position: absolute;
53            top: ${rect.bottom + scrollTop}px;
54            left: ${rect.left + scrollLeft}px;
55            width: ${Math.max(rect.width, 300)}px;
56            z-index: 9999;
57            background: #fff;
58            border: 1px solid #ddd;
59            border-radius: 8px;
60            box-shadow: 0 4px 12px rgba(0,0,0,0.15);
61            max-height: 250px;
62            overflow-y: auto;
63        `;
64    }
65
66    // Supprimer la liste de suggestions
67    function removeAutocompleteList() {
68        if (autocompleteList) {
69            autocompleteList.remove();
70            autocompleteList = null;
71        }
72        // ARIA : Marquer la liste comme fermée
73        input.setAttribute('aria-expanded', 'false');
74        input.removeAttribute('aria-activedescendant');
75    }
76
77    // Afficher les suggestions
78    function showSuggestions(cities) {
79        createAutocompleteList();
80        
81        if (cities.length === 0) {
82            removeAutocompleteList();
83            return;
84        }
85
86        cities.forEach(function(city, index) {
87            const item = document.createElement('div');
88            const itemId = 'autocomplete-option-' + index;
89            item.setAttribute('id', itemId);
90            item.setAttribute('role', 'option');
91            item.setAttribute('aria-selected', 'false');
92            item.classList.add('autocomplete-item');
93            item.style.cssText = `
94                padding: 10px 15px;
95                cursor: pointer;
96                border-bottom: 1px solid #f0f0f0;
97            `;
98            item.innerHTML = '<strong>' + city.postalCode + '</strong> - ' + city.ville;
99            
100            // Hover effect
101            item.addEventListener('mouseenter', function() {
102                this.style.backgroundColor = '#e9f7f6';
103            });
104            item.addEventListener('mouseleave', function() {
105                if (!this.classList.contains('autocomplete-active')) {
106                    this.style.backgroundColor = '';
107                }
108            });
109            
110            // Stocker les données
111            item.dataset.ville = city.ville;
112            item.dataset.postalCode = city.postalCode;
113            
114            // Au clic sur une suggestion
115            item.addEventListener('click', function() {
116                input.value = this.dataset.ville;
117                
118                // Remplir le champ CP s'il existe
119                const cpField = document.getElementById('cp');
120                if (cpField) {
121                    cpField.value = this.dataset.postalCode;
122                }
123                
124                removeAutocompleteList();
125                
126                // Déclencher un événement change pour notifier d'autres scripts
127                input.dispatchEvent(new Event('change', { bubbles: true }));
128            });
129            
130            autocompleteList.appendChild(item);
131        });
132    }
133
134    // Appeler l'API
135    function fetchCities(query) {
136        fetch(API_URL + '?mot=' + encodeURIComponent(query))
137            .then(function(response) {
138                return response.json();
139            })
140            .then(function(data) {
141                showSuggestions(data);
142            })
143            .catch(function(error) {
144                console.error('Erreur autocomplete:', error);
145                removeAutocompleteList();
146            });
147    }
148
149    // Écouter les frappes clavier
150    input.addEventListener('input', function() {
151        const query = this.value.trim();
152        
153        // Annuler le timer précédent
154        clearTimeout(debounceTimer);
155        
156        // Minimum 3 caractères
157        if (query.length < MIN_CHARS) {
158            removeAutocompleteList();
159            return;
160        }
161        
162        // Debounce de 300ms pour éviter trop de requêtes
163        debounceTimer = setTimeout(function() {
164            fetchCities(query);
165        }, 300);
166    });
167
168    // Fermer la liste si on clique ailleurs
169    document.addEventListener('click', function(e) {
170        if (e.target !== input && !autocompleteList?.contains(e.target)) {
171            removeAutocompleteList();
172        }
173    });
174    
175    // Repositionner lors du scroll ou resize
176    window.addEventListener('scroll', positionAutocompleteList);
177    window.addEventListener('resize', positionAutocompleteList);
178
179    // Navigation au clavier
180    input.addEventListener('keydown', function(e) {
181        if (!autocompleteList) return;
182        
183        const items = autocompleteList.querySelectorAll('.autocomplete-item');
184        let currentIndex = -1;
185        
186        items.forEach(function(item, index) {
187            if (item.classList.contains('autocomplete-active')) {
188                currentIndex = index;
189            }
190        });
191
192        if (e.key === 'ArrowDown') {
193            e.preventDefault();
194            currentIndex = (currentIndex + 1) % items.length;
195            setActiveItem(items, currentIndex);
196        } 
197        else if (e.key === 'ArrowUp') {
198            e.preventDefault();
199            currentIndex = currentIndex <= 0 ? items.length - 1 : currentIndex - 1;
200            setActiveItem(items, currentIndex);
201        } 
202        else if (e.key === 'Enter') {
203            e.preventDefault();
204            if (currentIndex > -1 && items[currentIndex]) {
205                items[currentIndex].click();
206            }
207        }
208        else if (e.key === 'Escape') {
209            removeAutocompleteList();
210        }
211    });
212
213    // Mettre en surbrillance l'élément actif
214    function setActiveItem(items, index) {
215        items.forEach(function(item) {
216            item.classList.remove('autocomplete-active');
217            item.setAttribute('aria-selected', 'false');
218            item.style.backgroundColor = '';
219        });
220        if (items[index]) {
221            items[index].classList.add('autocomplete-active');
222            items[index].setAttribute('aria-selected', 'true');
223            items[index].style.backgroundColor = '#e9f7f6';
224            // ARIA : Indiquer l'élément actif au lecteur d'écran
225            input.setAttribute('aria-activedescendant', items[index].id);
226        }
227    }
228})();

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.