Enhance button and form interaction handling to prevent conflicts with Livewire and Alpine states
This commit is contained in:
@@ -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();
|
||||||
|
|||||||
@@ -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');
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user