div滚动条是否存在?用 v-scroll-detect 增加一个辅助class

你做了一个列表容器:顶部有吸顶标题,右侧有渐隐提示,底部有"更多内容"的阴影。

产品提了一个要求:只有当容器内部真的可滚动时,才显示这些提示;并且内容会异步加载、筛选、折叠/展开,容器高度也可能随窗口变化而变化。

问题来了:我怎么知道这个元素当前有没有滚动条?

传统做法的坑:你会不断踩同一类问题

常见实现大多从这几条路开始:

  • 监听 scroll:一滚动就更新 UI。
  • 首次 mounted/useEffect 里测一次:scrollHeight > clientHeight
  • 内容变更后手动再测一次:塞进一堆 nextTicksetTimeout、或自己维护"何时需要重新计算"。

这些方案通常会引出痛点:

  • 内容不滚也会变 :异步加载、图片解码、字体加载、折叠/展开,会改变高度,但不一定触发 scroll
  • 容器尺寸也会变 :响应式布局、侧栏收起、窗口缩放,都会改变 clientHeight,你需要额外监听。
  • 性能与维护成本高:到处绑定事件、到处分发"刷新",最后很难保证"任何情况下都正确"。
  • 框架耦合:Vue/React/原生各写一套,重复劳动。

你真正想要的是:只要元素的尺寸或内容布局变化,就自动重新判断一次,并把结果映射成稳定的样式开关。

这个库解决什么:把"滚动条存在性"变成一个 CSS 开关

当你只需要"有/无滚动条"来驱动 UI 时,最佳形态往往是一个 class:

  • 有滚动条 → 添加 class(例如 with-scroll / has-v-scroll
  • 没滚动条 → 移除 class

v-scroll-detect 就是把这件事做成了可复用的工具:

  • ResizeObserver 感知元素布局变化
  • 用一次轻量判断(scrollHeight > clientHeight / scrollWidth > clientWidth)得出结果
  • 自动切换 class
  • 并提供 Vue 指令、React Hook 与原生核心 API

链接与名称

下面从"怎么用"开始,再解释"为什么这样实现更稳"。

快速开始

安装

bash 复制代码
npm i v-scroll-detect

Vue 3:指令(全局注册)

js 复制代码
import { createApp } from 'vue'
import App from './App.vue'
import vScrollDetect from 'v-scroll-detect'

createApp(App).use(vScrollDetect).mount('#app')

模板里直接用:

html 复制代码
<div v-scroll-detect>
  <!-- content -->
</div>

默认会在任意方向出现滚动条时切换 class:with-scroll

Vue 3:自定义 class(字符串)

html 复制代码
<div v-scroll-detect="'has-scroll'">
  <!-- content -->
</div>

Vue 3:分别处理纵/横向(对象)

html 复制代码
<div v-scroll-detect="{ v: 'has-v-scroll', h: 'has-h-scroll' }">
  <!-- content -->
</div>

对象模式同时支持别名:

  • 纵向:vvertical(缺省类名:has-v-scroll
  • 横向:hhorizontal(缺省类名:has-h-scroll

React:Hook

jsx 复制代码
import { useRef } from 'react'
import { useScrollDetect } from 'v-scroll-detect/react'

export function Panel() {
  const ref = useRef(null)
  useScrollDetect(ref, { v: 'has-v-scroll', h: 'has-h-scroll' })
  return <div ref={ref}>...</div>
}

原生/框架无关:核心函数

js 复制代码
import { createScrollDetector } from 'v-scroll-detect/core'

const el = document.querySelector('.my-container')
const detector = createScrollDetector(el, 'has-scroll')

// 手动触发一次检测
detector.check()

// 销毁(解除监听并停止更新)
detector.destroy()

API 设计:用一个 options 覆盖 90% 需求

v-scroll-detect 的核心输入是 options,目标是让"样式开关"足够直观:

  • undefined:使用默认类名 with-scroll
  • string:任一方向有滚动条时切换该类名
  • object:分别配置纵向与横向类名(支持 v/verticalh/horizontal

这种设计的好处是:

  • 只关心"有没有滚动条"时,一行搞定
  • 需要精细 UI(比如仅纵向显示阴影)时,也不用额外写逻辑

实现思路:为什么用 ResizeObserver 更"对味"

滚动条出现/消失,本质上是"内容尺寸"和"容器可视尺寸"的对比结果:

  • 纵向:scrollHeight > clientHeight
  • 横向:scrollWidth > clientWidth

难点并不在判断式,而在"何时判断"。

库的做法是:观察元素尺寸变化,一旦变化就安排一次检查,并用 requestAnimationFrame 合并到下一帧执行,避免在频繁布局变化时抖动更新。最终结果就是稳定地 classList.add/remove

与监听 scroll 相比,它更贴近真实触发源:

  • 内容变化、图片加载、字体变化、容器缩放,都能驱动重新判断
  • 你不需要在业务代码里维护"何时需要 refresh"

适用与注意事项

  • 适用:任何需要根据"是否可滚动"切换样式的场景(阴影、渐隐、边界提示、吸顶边框等)
  • 注意:部分平台的"覆盖式滚动条"可能不占据布局空间,但滚动能力仍然存在;这种情况下判断仍然依赖 scrollHeight/clientHeight,一般是可靠的
  • 兼容:依赖 ResizeObserver,如需兼容较旧浏览器可考虑引入 polyfill

结语

把"滚动条存在性"抽象成 class 开关后,UI 细节会更干净:业务只写样式与结构,滚动能力的判断交给统一工具完成。v-scroll-detect 的价值就在于:让这个判断在动态布局下也足够可靠,并且跨 Vue/React/原生都能复用。

相关推荐
H_z_q24011 小时前
web前端(HTML)银行汇款单的制作
前端·html
小宇的天下1 小时前
Synopsys Technology File and Routing Rules Reference Manual (1)
java·服务器·前端
@PHARAOH1 小时前
WHAT - Vercel react-best-practices 系列(四)
前端·react.js·前端框架
今天也要晒太阳4732 小时前
对el-upload的上传文件显示名做长度限制
前端
Thomas游戏开发2 小时前
分享一个好玩的:一次提示词让AI同时开发双引擎框架
前端·javascript·后端
NEXT062 小时前
别再折磨自己了!放弃 Redux 后,我用 Zustand + TS 爽到起飞
前端·react.js
donecoding2 小时前
Sass 模块化革命:告别 @import,拥抱 @use 和 @forward
前端·css·代码规范
m0_748252382 小时前
Angular 2 数据显示方法
前端·javascript·angular.js
2501_944711432 小时前
现代 React 路由实践指南
前端·react.js·前端框架