You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

JavaScript与CSS问题:菜单展开时过渡动画出现卡顿

Safari中下拉菜单叠加展开时卡顿的原因及修复方案

我仔细看了你的代码和问题描述,这个Safari专属的卡顿问题确实和z-index以及浏览器渲染机制的差异有关,咱们来一步步搞清楚原因和解决办法:

为什么只有Safari会卡顿?

主要有两个核心原因:

  • Safari对堆叠上下文的处理更严格:当你切换菜单的open类时,z-index从无到有(只有open状态才设置z-index:99),Safari会重新计算整个页面的堆叠层级,触发额外的图层重建和重绘,这在元素有position:absolute和过渡动画时,就会出现明显的延迟。而Chrome和Firefox对这种动态z-index的处理更宽松,不会触发这么重的渲染操作。
  • 过渡动画与display:none的冲突:你的.options-container设置了transition: all 0.4s,但display属性是不支持过渡的。当你切换open类时,浏览器瞬间切换display,同时还要尝试过渡其他属性,这在Safari的渲染队列里会造成混乱,导致类似快门延迟的卡顿。

具体修复方案

方案1:优化z-index的层级设置

给所有.options-container设置默认的z-index,避免在显示时突然创建堆叠上下文:

.select-box .options-container {
  /* 保留原有所有属性 */
  z-index: 1; /* 添加默认层级 */
}
.select-box .options-container.open {
  /* 保留原有属性 */
  z-index: 99; /* 展开时提升层级 */
}

方案2:用visibility+opacity替代display实现平滑过渡

既然display:none不能过渡,我们改用visibilityopacity来实现菜单的显示隐藏,配合过渡动画让Safari渲染更顺畅:

.select-box .options-container {
  background: #fff;
  width: 200px;
  /* 替换原来的display:none */
  visibility: hidden;
  opacity: 0;
  transition: all 0.4s;
  position: absolute;
  max-height: 240px;
  border: 1px solid #253e5c;
  border-radius: 0 0 4.5px 4.5px;
  z-index: 1; /* 加上默认z-index */
  pointer-events: none; /* 隐藏时不响应点击 */
}
.select-box .options-container.open {
  border-color: #253e5c;
  /* 替换原来的display:block */
  visibility: visible;
  opacity: 1;
  z-index: 99;
  pointer-events: auto; /* 显示时恢复点击 */
}

方案3:简化JavaScript逻辑(可选)

你的JS里关闭其他菜单的循环可以优化,减少不必要的DOM操作,进一步降低渲染压力:

const selectedElements = document.querySelectorAll(".selected");
const optionsContainers = document.querySelectorAll(".options-container");

// 处理菜单点击逻辑
selectedElements.forEach((selected, index) => {
  selected.addEventListener("click", () => {
    const isCurrentOpen = selected.classList.contains("open");
    
    // 先关闭所有其他已打开的菜单
    selectedElements.forEach((otherSel, otherIdx) => {
      if (otherIdx !== index && otherSel.classList.contains("open")) {
        otherSel.classList.remove("open");
        optionsContainers[otherIdx].classList.remove("open");
      }
    });
    
    // 再切换当前菜单的状态(如果之前是关闭的就打开)
    if (!isCurrentOpen) {
      selected.classList.add("open");
      optionsContainers[index].classList.add("open");
    }
  });
});

// 处理选项点击逻辑
optionsContainers.forEach((container, index) => {
  container.querySelectorAll(".options").forEach(option => {
    option.addEventListener("click", () => {
      selectedElements[index].innerHTML = option.querySelector("label").innerHTML;
      selectedElements[index].classList.remove("open");
      container.classList.remove("open");
      
      // 简化tier的显示判断
      document.getElementById("tier").style.display = 
        document.getElementById("level").innerText.includes('one') ? "none" : "grid";
    });
  });
});

修复后的效果

这些改动主要是让Safari的渲染引擎更高效地处理元素状态变化:

  • 固定默认z-index避免了堆叠上下文的频繁重建
  • 用visibility+opacity替代display,让过渡动画符合浏览器的渲染规则,不会触发突然的重绘
  • 简化DOM操作次数,减少渲染队列的任务量

内容的提问来源于stack exchange,提问作者code noob

火山引擎 最新活动