/** * 按钮防抖工具 - 防止短时间内重复点击 */ // 存储按钮点击状态的WeakMap const buttonClickStates = new WeakMap(); /** * 为按钮添加防抖功能 * @param {HTMLElement} button - 按钮元素 * @param {number} delay - 防抖延迟时间(毫秒),默认1000ms * @returns {boolean} - 如果按钮当前可点击返回true,否则返回false */ function preventDoubleClick(button, delay = 1000) { if (!button) return false; const now = Date.now(); const lastClickTime = buttonClickStates.get(button) || 0; // 如果距离上次点击时间小于延迟时间,则阻止点击 if (now - lastClickTime < delay) { return false; } // 记录本次点击时间 buttonClickStates.set(button, now); return true; } /** * 包装按钮点击处理函数,自动添加防抖 * @param {Function} handler - 原始点击处理函数 * @param {number} delay - 防抖延迟时间(毫秒),默认1000ms * @returns {Function} - 包装后的处理函数 */ function debounceClick(handler, delay = 1000) { return function(event) { const button = event.currentTarget || event.target; if (!preventDoubleClick(button, delay)) { event.preventDefault(); event.stopPropagation(); return false; } return handler.call(this, event); }; } /** * 为按钮添加禁用状态的防抖(视觉反馈更明显) * @param {HTMLElement} button - 按钮元素 * @param {Function} handler - 点击处理函数 * @param {number} delay - 禁用时间(毫秒),默认1000ms */ function addButtonDebounce(button, handler, delay = 1000) { if (!button) return; button.addEventListener('click', async function(event) { // 如果按钮已禁用,直接返回 if (button.disabled) { event.preventDefault(); event.stopPropagation(); return; } // 禁用按钮 button.disabled = true; const originalText = button.textContent; const originalHTML = button.innerHTML; try { // 执行处理函数 const result = await handler.call(this, event); return result; } catch (error) { console.error('按钮点击处理错误:', error); throw error; } finally { // 延迟后恢复按钮状态 setTimeout(() => { button.disabled = false; // 恢复原始内容(如果被修改了) if (button.textContent !== originalText && button.innerHTML !== originalHTML) { button.innerHTML = originalHTML; } }, delay); } }); } /** * 全局初始化:为所有按钮自动添加防抖 * @param {number} delay - 防抖延迟时间(毫秒),默认800ms * @param {string} selector - 按钮选择器,默认为所有button和.btn元素 */ function initGlobalButtonDebounce(delay = 800, selector = 'button, .btn, input[type="submit"]') { // 使用事件委托在document级别捕获所有按钮点击 document.addEventListener('click', function(event) { const button = event.target.closest(selector); if (!button) return; // 检查按钮是否有特殊属性跳过防抖 if (button.hasAttribute('data-no-debounce')) { return; } // 检查按钮是否已禁用 if (button.disabled) { event.preventDefault(); event.stopPropagation(); return; } // 应用防抖检查 if (!preventDoubleClick(button, delay)) { event.preventDefault(); event.stopPropagation(); // // console.log('防止重复点击:', button.textContent || button.value); } }, true); // 使用捕获阶段,确保在其他处理器之前执行 } /** * 为表单提交按钮添加防抖和加载状态 * @param {HTMLFormElement} form - 表单元素 * @param {number} delay - 防抖延迟时间(毫秒),默认2000ms */ function debounceFormSubmit(form, delay = 2000) { if (!form) return; const submitButtons = form.querySelectorAll('button[type="submit"], input[type="submit"]'); form.addEventListener('submit', function(event) { submitButtons.forEach(btn => { if (!preventDoubleClick(btn, delay)) { event.preventDefault(); return; } // 禁用按钮并添加加载提示 btn.disabled = true; const originalText = btn.textContent || btn.value; if (btn.tagName === 'BUTTON') { btn.innerHTML = ' 提交中...'; } // 延迟后恢复 setTimeout(() => { btn.disabled = false; if (btn.tagName === 'BUTTON') { btn.textContent = originalText; } }, delay); }); }); } // 导出函数供外部使用 if (typeof module !== 'undefined' && module.exports) { module.exports = { preventDoubleClick, debounceClick, addButtonDebounce, initGlobalButtonDebounce, debounceFormSubmit }; } // 页面加载完成后自动初始化全局按钮防抖 if (typeof window !== 'undefined') { if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => { initGlobalButtonDebounce(800); // // console.log('✓ 全局按钮防抖已启用'); }); } else { initGlobalButtonDebounce(800); // // console.log('✓ 全局按钮防抖已启用'); } }