Enhance button and form interaction handling to prevent conflicts with Livewire and Alpine states

This commit is contained in:
2026-02-02 17:28:39 -05:00
parent 19ccfaa27f
commit 22b7bbe780
2 changed files with 56 additions and 2 deletions

View File

@@ -1,5 +1,7 @@
// Optimized app.js // Optimized app.js
const passiveOptions = { passive: true }; const passiveOptions = { passive: true };
// Non-passive options for handlers that need to call preventDefault()
const activeOptions = { passive: false };
document.addEventListener('livewire:init', () => { document.addEventListener('livewire:init', () => {
Livewire.hook('request', ({ fail }) => { Livewire.hook('request', ({ fail }) => {
@@ -148,11 +150,21 @@ function initRevealAnimations() {
} }
const buttonClickStates = new WeakMap(); const buttonClickStates = new WeakMap();
const buttonClickHandlers = new WeakMap();
function preventDoubleClick(selector) { function preventDoubleClick(selector) {
const buttons = document.querySelectorAll(selector); const buttons = document.querySelectorAll(selector);
buttons.forEach(button => { buttons.forEach(button => {
if (buttonClickStates.has(button)) return; // Skip if already has a handler attached
if (buttonClickHandlers.has(button)) return;
// Skip Livewire/Alpine handled elements - let them handle their own state
if (button.hasAttribute('wire:click') ||
button.hasAttribute('x-on:click') ||
button.hasAttribute('@click')) {
return;
}
const clickHandler = function(e) { const clickHandler = function(e) {
if (buttonClickStates.get(button)) { if (buttonClickStates.get(button)) {
@@ -176,8 +188,10 @@ function preventDoubleClick(selector) {
} }
}; };
button.addEventListener('click', clickHandler, passiveOptions); // Use non-passive listener since we need to call preventDefault()
button.addEventListener('click', clickHandler, activeOptions);
buttonClickStates.set(button, false); buttonClickStates.set(button, false);
buttonClickHandlers.set(button, clickHandler);
}); });
} }
@@ -228,10 +242,20 @@ function getDevicePerformance() {
return devicePerformanceLevel; return devicePerformanceLevel;
} }
const formSubmitHandlers = new WeakMap();
function optimizeFormInteractions() { function optimizeFormInteractions() {
const forms = document.querySelectorAll('form'); const forms = document.querySelectorAll('form');
forms.forEach(form => { forms.forEach(form => {
// Skip if already has a handler attached
if (formSubmitHandlers.has(form)) return;
// Skip Livewire forms - they handle their own submit state
if (form.hasAttribute('wire:submit') || form.closest('[wire\\:id]')) {
return;
}
const submitBtn = form.querySelector('button[type="submit"], input[type="submit"]'); const submitBtn = form.querySelector('button[type="submit"], input[type="submit"]');
if (!submitBtn) return; if (!submitBtn) return;
@@ -246,6 +270,7 @@ function optimizeFormInteractions() {
}; };
form.addEventListener('submit', submitHandler, passiveOptions); form.addEventListener('submit', submitHandler, passiveOptions);
formSubmitHandlers.set(form, submitHandler);
}); });
} }
@@ -361,6 +386,26 @@ if (document.readyState === 'loading') {
initializeApp(); initializeApp();
} }
// Reinitialize after Livewire navigation (wire:navigate)
document.addEventListener('livewire:navigated', () => {
// Re-run initialization for dynamically loaded content
updateDateTime();
const performance = getDevicePerformance();
if (performance === 'high') {
initRevealAnimations();
initLazyLoading();
} else {
document.querySelectorAll('.reveal-on-scroll').forEach(el => {
el.classList.add('revealed');
});
}
preventDoubleClick('.btn-primary, [href*="checkout"], [href*="add-to-cart"]');
optimizeFormInteractions();
});
window.addEventListener('beforeunload', () => { window.addEventListener('beforeunload', () => {
if (timeUpdateInterval) clearInterval(timeUpdateInterval); if (timeUpdateInterval) clearInterval(timeUpdateInterval);
if (revealObserver) revealObserver.disconnect(); if (revealObserver) revealObserver.disconnect();

View File

@@ -329,6 +329,15 @@
} }
document.addEventListener('click', function(e) { document.addEventListener('click', function(e) {
// Skip Livewire/Alpine handled buttons - they manage their own loading states
if (e.target.hasAttribute('wire:click') ||
e.target.hasAttribute('wire:submit') ||
e.target.hasAttribute('x-on:click') ||
e.target.hasAttribute('@click') ||
e.target.closest('[wire\\:submit]')) {
return;
}
if (e.target.matches('button[type="submit"], .btn-submit')) { if (e.target.matches('button[type="submit"], .btn-submit')) {
e.target.classList.add('btn-loading'); e.target.classList.add('btn-loading');