PAYATHON 2026

为什么通过其他元素切换 tabs 时没有显示 active 样式?

排障赵师傅

原代码把 active 加到了处理点击事件的元素上。点击图片所在的 div 时,得到 active 的是这个 div,对应的 tabs 按钮不会自动更新。要同步按钮状态,需要根据目标面板的 ID 找到对应按钮,再更新它的类名。

为什么点击按钮正常,点击图片却不正常?

问题出在这一行:

evt.currentTarget.className += " active";

evt.currentTarget 指向当前执行事件处理器的元素:

  • 点击 tabs 按钮时,它是绑定了 onclick 的 button。
  • 点击切换图片时,它是绑定了 onclick 的 div,即使实际点击的是其中的图片。

CSS 只匹配 .tab 内带有 active 类的按钮:

.tab button.active {
  background-color: #ffb100;
}

给切换用的 div 添加 active,因此不会让 tabs 按钮变色。面板内容仍能正常切换,因为它通过 cityName 决定显示哪个面板,与添加 active 的逻辑相互独立。

根据目标面板同步按钮状态

给每个 tabs 按钮添加 data-tab,值与它控制的面板 ID 一致。切换函数只接收面板 ID,不再依赖点击事件。

需要修改以下几处:

  1. 为 tabs 按钮添加 data-tab。
  2. 将所有 openCity(event, '...') 改为 openCity('...')。
  3. 在函数中比较 data-tab 和目标 ID,通过 classList.toggle() 更新按钮状态。

可用以下代码替换原有的 CSS、HTML 和 JavaScript:

<style>
  .tab button:hover,
  .tab button.active {
    background-color: #ffb100;
  }
</style>

<div class="container">
  <div class="tab">
    <button
      type="button"
      class="tablinks"
      data-tab="two"
      onclick="openCity('two')"
    >Two</button>

    <button
      type="button"
      class="tablinks"
      data-tab="three"
      onclick="openCity('three')"
    >Three</button>
  </div>

  <div id="one" class="tabcontent" style="display: none;">
    (I'm one)
    <div class="arrowright" onclick="openCity('two')">
      arrowright
    </div>
  </div>

  <div id="two" class="tabcontent" style="display: none;">
    (I'm two)
    <div class="arrowleft" onclick="openCity('one')">
      arrowleft
    </div>
    <div class="arrowright" onclick="openCity('three')">
      arrowright
    </div>
  </div>

  <div id="three" class="tabcontent" style="display: none;">
    (I'm three)
    <div class="arrowleft" onclick="openCity('two')">
      arrowleft
    </div>
  </div>
</div>

<script>
  function openCity(cityName) {
    const target = document.getElementById(cityName);

    // 目标不存在时,保留当前显示状态。
    if (!target || !target.classList.contains("tabcontent")) {
      return;
    }

    document.querySelectorAll(".tabcontent").forEach(function (panel) {
      panel.style.display = "none";
    });

    target.style.display = "block";

    document.querySelectorAll(".tab button.tablinks").forEach(function (button) {
      button.classList.toggle("active", button.dataset.tab === cityName);
    });
  }

  function initTabs() {
    openCity("one");
  }

  if (document.readyState === "loading") {
    document.addEventListener("DOMContentLoaded", initTabs);
  } else {
    initTabs();
  }
</script>

按钮的 active 类由这一行控制:

button.classList.toggle("active", button.dataset.tab === cityName);

条件为 true 时添加类,为 false 时移除类。无论通过按钮、图片,还是直接在代码中调用 openCity("two"),都会同步显示 two 面板并选中 Two 按钮。

初始化代码也需要修改

原来的初始化写法有一个问题:

document.body.addEventListener('DOMContentLoaded', openCity(event, 'one'));

openCity(event, 'one') 会立即执行,返回值随后被传给 addEventListener(),因此没有注册等待事件触发的回调。此时也不能假定有可用的点击事件 event。

应在 document 上监听 DOMContentLoaded,并将函数作为回调传入:

document.addEventListener("DOMContentLoaded", function () {
  openCity("one");
});

完整示例还检查了 document.readyState。如果 DOM 已经解析完成,就直接初始化,避免错过 DOMContentLoaded 事件。

使用时注意

原始 HTML 只有 Two 和 Three 两个按钮,没有对应 one 的按钮。因此,显示 one 时,两个按钮上的 active 都会被移除。如果希望 One 也有选中状态,需要在 .tab 内补上:

<button
  type="button"
  class="tablinks"
  data-tab="one"
  onclick="openCity('one')"
>One</button>

data-tab 的值必须与面板 id 完全一致。如果页面有多组选项卡,还应将面板和按钮的查询范围限制在各自的容器内,避免切换一组时影响其他组。

备注:内容仅供参考。