You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

原生JS中DOM元素类切换异常问题求助

问题分析与修复方案

问题根源

  1. 重复绑定transitionend事件:每次执行隐藏逻辑时,都会给第一个sidebar-element绑定新的transitionend回调,旧的回调不会自动销毁。多次点击后,该元素会存在多个transitionend监听,导致sidebar的active类被多次移除,后续点击时误判状态重复添加。
  2. 无状态锁导致动画冲突:动画未完成时点击按钮,之前的定时器回调仍会执行,元素的active类状态混乱,进一步导致sidebar的active类判断出错。

修复后的代码

JavaScript 部分

const SIDEBAR_DELAY_TIME = 150;
let isAnimating = false; // 状态锁,防止动画期间重复点击

function handleSidebar() {
  const btn = document.querySelector('button');
  const sidebar = document.querySelector('.sidebar');
  const contents = document.querySelectorAll('.sidebar-element');

  if (!btn || !sidebar) return;

  // 封装移除sidebar active类的函数,避免重复绑定
  function removeSidebarActive() {
    sidebar.classList.remove('active');
    contents[0].removeEventListener('transitionend', removeSidebarActive);
  }

  btn.addEventListener('click', () => {
    if (isAnimating) return; // 动画期间禁止点击
    isAnimating = true;

    if (!sidebar.classList.contains('active')) {
      sidebar.classList.add('active');
      contents.forEach((item, i) => {
        setTimeout(() => {
          item.classList.add('active');
          // 最后一个元素动画完成后解锁
          if (i === contents.length - 1) {
            isAnimating = false;
          }
        }, i * SIDEBAR_DELAY_TIME);
      });
    } else {
      const keys = Array.from({ length: contents.length }, (_, i) => i).reverse();
      let lastCallbackDone = false; // 标记最后一个隐藏回调是否执行

      keys.forEach((key, i) => {
        setTimeout(() => {
          contents[key].classList.remove('active');
          // 最后一个元素隐藏后,绑定transitionend事件移除sidebar的active
          if (key === 0 && !lastCallbackDone) {
            lastCallbackDone = true;
            contents[0].addEventListener('transitionend', removeSidebarActive, { once: true }); // 使用once确保只执行一次
          }
          // 所有隐藏定时器执行完成后解锁
          if (i === keys.length - 1) {
            setTimeout(() => {
              isAnimating = false;
            }, SIDEBAR_DELAY_TIME);
          }
        }, i * SIDEBAR_DELAY_TIME);
      });
    }
  });
}

handleSidebar();

关键修改点

  • 添加isAnimating状态锁,动画未完成时禁止触发新的点击逻辑,避免状态混乱。
  • 将移除sidebar的active类逻辑封装为独立函数removeSidebarActive,并在执行后移除事件监听,同时使用{ once: true }确保事件只触发一次,彻底解决重复绑定问题。
  • 在显示/隐藏动画的最后一步解锁状态锁,确保只有动画完全结束后才能再次操作。

验证效果

修改后,无论点击多少次按钮,sidebar的active类都只会在正确的时机添加/移除,不会出现重复添加的情况,同时动画期间点击按钮也不会导致状态错乱。

内容的提问来源于stack exchange,提问作者HJ.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 15:18:27