PageSourceSearch

https://moneypex.com/js/mobile-navbar-fix.js?v=7YSBNQkQFEQZAwoEmiz…7YSBNQkQFEQZAwoEmizFZKkb-Og7HIQdLR_l2CbO4Sk

js moneypex.com collected 2026-10-04 00:19:51 UTC 15,158 bytes, 319 lines download raw bytes

1// Mobile Navbar Dropdown Fix for UK and PK Layouts
2// Ensures Bootstrap collapse and accordion work properly on mobile
3
4(function() {
5    'use strict';
6
7    // Mobile browsers can restore DOM state (bfcache / pageshow) and/or some code may
8    // initialize Bootstrap Collapse with default toggle:true, which can auto-open.
9    // This helper force-closes the mobile nav safely (without blocking user clicks).
10    let userToggledMobileNav = false;
11    let isAutoClosing = false;
12
13    function forceCloseMobileNavbar() {
14        const mobileNavbar = document.getElementById('mobileNavbar');
15        if (!mobileNavbar) return;
16
17        const menuIcon = document.getElementById('menuIcon');
18        const closeIcon = document.getElementById('closeIcon');
19        const toggleButton = document.querySelector('[data-bs-target="#mobileNavbar"]');
20
21        // If the user already interacted, don't fight them.
22        if (userToggledMobileNav) return;
23        if (isAutoClosing) return;
24        isAutoClosing = true;
25
26        // Prefer Bootstrap API when available.
27        if (window.bootstrap && window.bootstrap.Collapse) {
28            try {
29                const instance = window.bootstrap.Collapse.getOrCreateInstance(mobileNavbar, { toggle: false });
30                instance.hide();
31            } catch (e) {
32                // Fall back to class cleanup below.
33            }
34        }
35
36        // Hard cleanup (covers cases where Bootstrap isn't available yet or state is stale).
37        mobileNavbar.classList.remove('show', 'collapsing');
38        mobileNavbar.classList.add('collapse');
39        mobileNavbar.style.removeProperty('height');
40
41        if (toggleButton) {
42            toggleButton.setAttribute('aria-expanded', 'false');
43            toggleButton.classList.add('collapsed');
44        }
45
46        if (menuIcon) menuIcon.style.display = 'block';
47        if (closeIcon) closeIcon.style.display = 'none';
48
49        // Allow future auto-closes (if another script re-opens during load)
50        setTimeout(() => { isAutoClosing = false; }, 50);
51    }
52
53    function scheduleForceClose() {
54        // Run a few times to beat any late initializers.
55        [0, 50, 150, 350].forEach(ms => setTimeout(forceCloseMobileNavbar, ms));
56    }
57    
58    document.addEventListener('DOMContentLoaded', function() {
59        const mobileNavbar = document.getElementById('mobileNavbar');
60        const menuIcon = document.getElementById('menuIcon');
61        const closeIcon = document.getElementById('closeIcon');
62        const toggleButton = document.querySelector('[data-bs-target="#mobileNavbar"]');
63        
64        if (!mobileNavbar) return;
65        
66        // CRITICAL: Ensure mobile navbar is closed by default (and stays closed on refresh)
67        scheduleForceClose();
68        
69        // Initialize icon states
70        if (menuIcon && closeIcon) {
71            menuIcon.style.display = 'block';
72            closeIcon.style.display = 'none';
73        }
74
75        // Mark when the user actually toggles the menu (so we don't auto-close afterwards)
76        if (toggleButton) {
77            toggleButton.addEventListener('click', function() {
78                userToggledMobileNav = true;
79            }, { passive: true });
80        }
81
82        // If ANY script adds `.show` during initial load/refresh, close it immediately.
83        // This is the robust fix for the "menu opens on refresh" issue.
84        try {
85            const observer = new MutationObserver(function(mutations) {
86                if (userToggledMobileNav) return;
87
88                for (const m of mutations) {
89                    if (m.type === 'attributes' && m.attributeName === 'class') {
90                        if (mobileNavbar.classList.contains('show')) {
91                            forceCloseMobileNavbar();
92                        }
93                    }
94                }
95            });
96
97            observer.observe(mobileNavbar, { attributes: true, attributeFilter: ['class'] });
98
99            // Stop watching after load settles (prevents overhead)
100            setTimeout(() => observer.disconnect(), 2500);
101        } catch (e) {
102            // Ignore if MutationObserver not supported (very old browsers)
103        }
104        
105        // Handle Bootstrap collapse events for main menu
106        if (window.bootstrap && window.bootstrap.Collapse) {
107            // Create collapse instance with toggle:false to prevent auto-opening
108            const collapseInstance = window.bootstrap.Collapse.getOrCreateInstance(mobileNavbar, {
109                toggle: false
110            });
111            
112            // Listen for show event
113            mobileNavbar.addEventListener('show.bs.collapse', function() {
114                if (menuIcon) menuIcon.style.display = 'none';
115                if (closeIcon) closeIcon.style.display = 'block';
116                if (toggleButton) toggleButton.setAttribute('aria-expanded', 'true');
117            });
118            
119            // Listen for hide event
120            mobileNavbar.addEventListener('hide.bs.collapse', function() {
121                if (menuIcon) menuIcon.style.display = 'block';
122                if (closeIcon) closeIcon.style.display = 'none';
123                if (toggleButton) toggleButton.setAttribute('aria-expanded', 'false');
124            });
125            
126            // Listen for shown event (after animation completes)
127            mobileNavbar.addEventListener('shown.bs.collapse', function() {
128                // Ensure all accordion items are properly initialized
129                const accordionItems = mobileNavbar.querySelectorAll('.accordion-collapse');
130                accordionItems.forEach(item => {
131                    if (window.bootstrap && window.bootstrap.Collapse) {
132                        // toggle:false prevents accordion items from auto-opening on init
133                        window.bootstrap.Collapse.getOrCreateInstance(item, { toggle: false });
134                    }
135                });
136            });
137        } else {
138            // Fallback if Bootstrap is not loaded yet
139            if (toggleButton) {
140                toggleButton.addEventListener('click', function() {
141                    userToggledMobileNav = true;
142                    setTimeout(function() {
143                        const isExpanded = mobileNavbar.classList.contains('show');
144                        if (isExpanded) {
145                            if (menuIcon) menuIcon.style.display = 'none';
146                            if (closeIcon) closeIcon.style.display = 'block';
147                        } else {
148                            if (menuIcon) menuIcon.style.display = 'block';
149                            if (closeIcon) closeIcon.style.display = 'none';
150                        }
151                    }, 10);
152                });
153            }
154        }
155        
156        // Handle nested POS Solutions dropdown
157        const posSolutionsToggle = mobileNavbar.querySelector('.pos-solutions-toggle');
158        const posSolutionsCollapse = mobileNavbar.querySelector('#mobileCollapsePOS');
159        if (posSolutionsToggle && posSolutionsCollapse) {
160            if (window.bootstrap && window.bootstrap.Collapse) {
161                window.bootstrap.Collapse.getOrCreateInstance(posSolutionsCollapse, {
162                    toggle: false
163                });
164            }
165            
166            posSolutionsToggle.addEventListener('click', function(e) {
167                e.preventDefault();
168                e.stopPropagation();
169                
170                if (window.bootstrap && window.bootstrap.Collapse) {
171                    const collapseInstance = window.bootstrap.Collapse.getInstance(posSolutionsCollapse);
172                    if (collapseInstance) {
173                        collapseInstance.toggle();
174                    }
175                } else {
176                    // Fallback
177                    const isExpanded = posSolutionsCollapse.classList.contains('show');
178                    if (isExpanded) {
179                        posSolutionsCollapse.classList.remove('show');
180                        posSolutionsToggle.setAttribute('aria-expanded', 'false');
181                    } else {
182                        posSolutionsCollapse.classList.add('show');
183                        posSolutionsToggle.setAttribute('aria-expanded', 'true');
184                    }
185                }
186            });
187        }
188        
189        // Ensure accordion buttons work properly
190        const accordionButtons = mobileNavbar.querySelectorAll('.accordion-button[data-bs-toggle="collapse"]');
191        accordionButtons.forEach(button => {
192            button.addEventListener('click', function(e) {
193                // Prevent default only if Bootstrap is handling it
194                if (!window.bootstrap || !window.bootstrap.Collapse) {
195                    e.preventDefault();
196                    const targetId = button.getAttribute('data-bs-target');
197                    if (targetId) {
198                        const target = document.querySelector(targetId);
199                        if (target) {
200                            const isExpanded = target.classList.contains('show');
201                            if (isExpanded) {
202                                target.classList.remove('show');
203                                button.classList.add('collapsed');
204                                button.setAttribute('aria-expanded', 'false');
205                            } else {
206                                // Close other accordion items in the same parent
207                                const parent = button.closest('.accordion');
208                                if (parent) {
209                                    const otherItems = parent.querySelectorAll('.accordion-collapse.show');
210                                    otherItems.forEach(item => {
211                                        if (item !== target) {
212                                            item.classList.remove('show');
213                                            const otherButton = parent.querySelector(`[data-bs-target="#${item.id}"]`);
214                                            if (otherButton) {
215                                                otherButton.classList.add('collapsed');
216                                                otherButton.setAttribute('aria-expanded', 'false');
217                                            }
218                                        }
219                                    });
220                                }
221                                target.classList.add('show');
222                                button.classList.remove('collapsed');
223                                button.setAttribute('aria-expanded', 'true');
224                            }
225                        }
226                    }
227                }
228            });
229        });
230        
231        // Close mobile menu when clicking on links with data-close-mobile-nav
232        const closeLinks = mobileNavbar.querySelectorAll('[data-close-mobile-nav="true"]');
233        closeLinks.forEach(link => {
234            link.addEventListener('click', function(e) {
235                const href = link.getAttribute('href');
236                // Don't close for placeholder links
237                if (!href || href === '#' || href === '#!') {
238                    return;
239                }
240                
241                // Close the mobile menu
242                if (window.bootstrap && window.bootstrap.Collapse) {
243                    const collapseInstance = window.bootstrap.Collapse.getInstance(mobileNavbar);
244                    if (collapseInstance) {
245                        collapseInstance.hide();
246                    }
247                } else {
248                    mobileNavbar.classList.remove('show');
249                    if (menuIcon) menuIcon.style.display = 'block';
250                    if (closeIcon) closeIcon.style.display = 'none';
251                    if (toggleButton) toggleButton.setAttribute('aria-expanded', 'false');
252                }
253            });
254        });
255        
256        // Close menu when clicking outside
257        document.addEventListener('click', function(event) {
258            if (!mobileNavbar.contains(event.target) && 
259                (!toggleButton || !toggleButton.contains(event.target))) {
260                if (mobileNavbar.classList.contains('show')) {
261                    if (window.bootstrap && window.bootstrap.Collapse) {
262                        const collapseInstance = window.bootstrap.Collapse.getInstance(mobileNavbar);
263                        if (collapseInstance) {
264                            collapseInstance.hide();
265                        }
266                    } else {
267                        mobileNavbar.classList.remove('show');
268                        if (menuIcon) menuIcon.style.display = 'block';
269                        if (closeIcon) closeIcon.style.display = 'none';
270                        if (toggleButton) toggleButton.setAttribute('aria-expanded', 'false');
271                    }
272                }
273            }
274        });
275        
276        // Ensure Bootstrap Collapse is initialized for all accordion items
277        function initAccordionCollapses() {
278            if (window.bootstrap && window.bootstrap.Collapse) {
279                const accordionCollapses = mobileNavbar.querySelectorAll('.accordion-collapse');
280                accordionCollapses.forEach(collapse => {
281                    try {
282                        window.bootstrap.Collapse.getOrCreateInstance(collapse, {
283                            toggle: false
284                        });
285                    } catch (e) {
286                        console.warn('Could not initialize collapse for:', collapse, e);
287                    }
288                });
289                
290                // Also initialize nested POS Solutions collapse
291                const posSolutionsCollapse = mobileNavbar.querySelector('#mobileCollapsePOS');
292                if (posSolutionsCollapse) {
293                    try {
294                        window.bootstrap.Collapse.getOrCreateInstance(posSolutionsCollapse, {
295                            toggle: false
296                        });
297                    } catch (e) {
298                        console.warn('Could not initialize POS Solutions collapse:', e);
299                    }
300                }
301            }
302        }
303        
304        // Initialize accordion collapses after a short delay to ensure Bootstrap is loaded
305        setTimeout(initAccordionCollapses, 100);
306        
307        // Re-initialize on window resize (in case Bootstrap loads late)
308        window.addEventListener('load', function() {
309            initAccordionCollapses();
310        });
311    });
312
313    // Important for mobile emulation + Safari/iOS bfcache: DOM may be restored without DOMContentLoaded.
314    window.addEventListener('pageshow', function() {
315        userToggledMobileNav = false;
316        scheduleForceClose();
317    });
318})();
319

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.