如何通过 CSS 动画延迟 Turbo Frame 的加载时机

本文介绍一种纯 css 方案,利用 `max-height` 和 `overflow: hidden` 配合动画,真正延迟 turbo frame 的懒加载触发时机,避免鼠标短暂悬停时发起不必要的网络请求。

在使用 Turbo(如 Hotwire Turbo)构建交互式 UI 时,常借助 实现按需加载。但一个常见误区是:仅靠 opacity: 0 或 visibility: hidden + display: none 切换,并不能阻止 Turbo 的懒加载逻辑——因为 Turbo 内部依赖 IntersectionObserver 检测元素是否“进入视口”,而 opacity、visibility 不影响布局占位,只要元素在 DOM 中且具有可计算的几何尺寸(哪怕完全透明),就可能被判定为“可见”,从而立即触发加载。

✅ 正确思路:让元素在动画完成前「彻底不可见且不占布局空间」

关键在于:在悬停初期,确保 所在容器的 height 为 0 且 overflow: hidden,使其在布局上完全不占据空间、不与视口相交;待延迟动画结束、max-height 展开后,才真正“出现”并触发 Turbo 加载。

以下是推荐的 SCSS 实现(已兼容现代浏览器,无需重复写 -webkit- 等前缀):

.hoverWrapper {
  display: inline-block;
  position: relative;

  &:hover #hoverContent {
    animation: tooltipShow 1.5s forwards; // 延迟 1.5 秒后显示内容
  }
}

#hoverContent {
  position: absolute;
  top: -20px;
  left: 50%;
  transform: translate(-50%, -100%);
  z-index: 99999999;
  min-width: 15rem;
  background-color: var(--color-white);
  color: var(--color-dark);
  border-radius: var(--border-radius);
  box-shadow: var(--shadow-back);
  padding: 0.75rem;
  font-size: var(--font-size-s);
  text-align: center;

  // ? 核心:初始状态为 0 高度 + 隐藏溢出 → 完全脱离布局流
  max-height: 0;
  overflow: hidden;
  opacity: 0;
  transition: opacity 0.3s ease; // 可选:淡入增强体验

  i {
    position: absolute;
    top: 100%;
    left: 50%;
    margin-left: -12px;
    width: 24px;
    height: 12px;
    overflow: hidden;

    &::after {
      content: '';
      position: absolute;
      width: 12px;
      height: 12px;
      left: 50%;
      transform: translate(-50%, -50%) rotate(45deg);
      background-color: var(--color-white);
      box-shadow: var(--shadow-back);
    }
  }
}

@keyframes tooltipShow {
  0% {
    max-height: 0;
    opacity: 0;
  }
  99% {
    max-height: 0;
    opacity: 0;
  }
  100% {
    max-height: 500px; // 设为足够大的值(或用 `fit-content`,但需注意兼容性)
    opacity: 1;
  }
}

对应 HTML 结构保持简洁:

  Hover me
  
    
      Loading...
    
  

⚠️ 注意事项

  • max-height 值需合理设

    :不能设为 none(会破坏动画),建议设为一个明显大于内容实际高度的固定值(如 500px),或使用 max-height: fit-content(注意 Safari 旧版支持有限)。
  • 避免 display: none 直接控制:若在动画中切换 display,会导致动画中断(CSS 动画无法在 display 变化间插值)。
  • loading="lazy" 仍需保留:它与上述 CSS 方案协同工作——只有当元素真正“展开”并进入视口时,Turbo 才会启动请求。
  • 无障碍友好性:记得为触发区域添加 role="button" 和 aria-describedby,确保屏幕阅读器能正确关联提示内容。

✅ 总结

真正的延迟加载 ≠ 视觉延迟,而是要让元素在 DOM 布局层面“消失”。通过 max-height: 0 + overflow: hidden + animation 控制展开时机,可精准匹配用户真实意图(如悬停 ≥1.5s),显著减少无效 Turbo 请求,提升性能与用户体验。该方案零 JS 依赖,轻量、可靠、易于复用。