为什么?
我创建这个插件是因为我讨厌又丑又占空间的滚动条。其他类似的插件在功能、质量、简洁性、许可协议或浏览器支持方面都无法满足我的需求。
目标与功能
- 简单、强大且文档完善的 API。
- 高浏览器兼容性 —— 支持 Firefox 59+、Chrome 55+、Opera 42+、Edge 15+ 和 Safari 10+。
- 完全无障碍性 —— 完整保留原生的滚动行为。
- 支持服务器端运行(
Node、Deno和Bun) —— 兼容 SSR、SSG 和 ISR。 - 在多种设备上经过测试 —— 包括 移动端、桌面端 和 平板。
- 针对多种(甚至混合)输入方式测试 —— 支持 鼠标、触控 和 手写笔。
- Tree-shaking —— 仅打包真正需要的内容。
- 自动更新检测 —— 无需轮询。
- 利用最新的浏览器功能 —— 在新浏览器中提供最佳性能。
- 与布局流无关 —— 支持所有
direction、flex-direction和writing-mode的取值。 - 支持滚动捕捉(Scroll Snapping)。
- 支持所有 虚拟滚动 库。
- 支持
body元素。 - 滚动条样式易于定制且高效。
- 高度可定制化。
- TypeScript 支持 —— 完全用 TypeScript 编写。
- 无任何外部依赖 —— 100% 自编代码,确保小体积和最佳功能性。
- 为以下框架提供高质量且完全类型化的版本:
react、vue、angular、svelte和solid。
选择你的框架
除了原生 JavaScript 版本外,你还可以使用官方提供的框架组件和工具:
快速入门
npm 和 nodejs
OverlayScrollbars 可以通过 npm 或者你选择的包管理工具下载:
npm install overlayscrollbars
安装完成后,可以导入它:
import 'overlayscrollbars/overlayscrollbars.css'; import { OverlayScrollbars, ScrollbarsHidingPlugin, SizeObserverPlugin, ClickScrollPlugin } from 'overlayscrollbars';
注意:如果路径
'overlayscrollbars/overlayscrollbars.css'无法正常工作,请使用'overlayscrollbars/styles/overlayscrollbars.css'作为 CSS 文件的导入路径。
你可以参考并使用这个 Node 示例 作为起点。
手动下载与嵌入
你可以在没有任何打包工具或包管理器的情况下使用 OverlayScrollbars。
只需从 Releases 下载一个版本,或使用 CDN。
- 使用
.browser扩展名的 JavaScript 文件。 - 如果需要支持较旧的浏览器,请使用
.es5扩展名的 JavaScript 文件,否则使用.es6文件。 - 对于生产环境,请使用
.min扩展名的 JavaScript / 样式表文件。
在 HTML 中手动嵌入 OverlayScrollbars:
<link type="text/css" href="path/to/overlayscrollbars.css" rel="stylesheet" /> <script type="text/javascript" src="path/to/overlayscrollbars.browser.es.js" defer></script>
使用全局变量 OverlayScrollbarsGlobal 访问 API,就像在 Node.js 或模块中一样:
var { OverlayScrollbars, ScrollbarsHidingPlugin, SizeObserverPlugin, ClickScrollPlugin } = OverlayScrollbarsGlobal;
你可以参考这个 浏览器示例 作为参考或起点。
本文档中的示例使用了 import 语法,而不是 OverlayScrollbarsGlobal 对象,但这两种方式是等效的。
初始化
OverlayScrollbars 的初始化是针对每个元素进行的 显式 操作。只有在插件初始化的元素上,滚动条才会被修改。子元素的滚动条将保持不变,除非插件也在这些子元素上初始化。
你可以直接通过 Element 或 Object 初始化一个新实例,使用 Object 可以让你更精细地控制初始化过程。
// 使用元素进行简单初始化 const osInstance = OverlayScrollbars(document.querySelector('#myElement'), {});
避免初始化闪烁
当你初始化 OverlayScrollbars 时,它需要几毫秒来创建并将所有元素添加到 DOM 中。在此过程中,原生滚动条仍然可见,直到初始化完成并切换为 OverlayScrollbars。这会表现为闪烁现象。
为了解决这个问题,可以在目标元素上添加 data-overlayscrollbars-initialize 属性(如果初始化的是 body 元素,还需要在 html 元素上添加该属性)。
<!-- 对于 body 元素 --> <html data-overlayscrollbars-initialize> <head></head> <body data-overlayscrollbars-initialize></body> </html> <!-- 对于其他元素 --> <div data-overlayscrollbars-initialize> OverlayScrollbars 已应用于这个 div </div>
使用对象初始化
这是一个深入的主题。点击这里阅读更多。
唯一必需的字段是 target 字段。这个字段指定了插件应用的目标元素。如果你仅使用 target 字段进行对象初始化,结果与元素初始化是等效的:
// 两种初始化方式的结果是一样的 OverlayScrollbars(document.querySelector('#myElement'), {}); OverlayScrollbars({ target: document.querySelector('#myElement') }, {});
当使用对象初始化时,你可以指定库如何处理生成的元素。例如,你可以指定一个现有元素作为 viewport 元素。这样,库就不会生成该元素,而是使用指定的元素:
OverlayScrollbars({ target: document.querySelector('#target'), elements: { viewport: document.querySelector('#viewport'), }, }, {});
这在你有固定的 DOM 结构并且不希望 OverlayScrollbars 创建自己的元素时非常有用。此类情况通常发生在你希望其他库与 OverlayScrollbars 一起工作时。
你还可以决定将滚动条应用于哪个元素:
OverlayScrollbars({ target: document.querySelector('#target'), scrollbars: { slot: document.querySelector('#target').parentElement, }, }, {});
最后,你还可以决定何时取消初始化:
OverlayScrollbars({ target: document.querySelector('#target'), cancel: { nativeScrollbarsOverlaid: true, body: null, } }, {});
在上述示例中,如果原生滚动条已经叠加,或者目标元素是 body 元素并且插件已确定初始化 body 元素会干扰原生功能(如 window.scrollTo),则初始化将被中止。
配置选项
你可以在初始化时为 OverlayScrollbars 设置一组初始配置选项,并且可以随时通过 options 方法进行更改:
OverlayScrollbars(document.querySelector('#myElement'), { overflow: { x: 'hidden', }, });
配置选项详解
这是一个深入的主题。点击这里阅读更多。
默认的配置选项是:
const defaultOptions = { paddingAbsolute: false, showNativeOverlaidScrollbars: false, update: { elementEvents: [['img', 'load']], debounce: [0, 33], attributes: null, ignoreMutation: null, }, overflow: { x: 'scroll', y: 'scroll', }, scrollbars: { theme: 'os-theme-dark', visibility: 'auto', autoHide: 'never', autoHideDelay: 1300, autoHideSuspend: false, dragScroll: true, clickScroll: false, pointers: ['mouse', 'touch', 'pen'], }, };
paddingAbsolute
| 类型 | 默认值 |
|---|---|
boolean | false |
指示内容的内边距是否应该是绝对定位的。
showNativeOverlaidScrollbars
| 类型 | 默认值 |
|---|---|
boolean | false |
指示是否显示原生的叠加滚动条。
update.elementEvents
| 类型 | 默认值 |
|---|---|
Array<[string, string]> | null | [['img', 'load']] |
一个元组数组。元组中的第一个值是 selector(选择器),第二个值是 event names(事件名称)。当指定选择器的任何元素触发指定事件时,插件将会更新自身。默认值可以理解为:“如果任何 img 元素触发 load 事件,插件会更新自身。”
update.debounce
| 类型 | 默认值 |
|---|---|
[number, number] | number | null | [0, 33] |
注意:如果使用 0 作为超时值,将使用
requestAnimationFrame代替setTimeout进行防抖。
防抖 MutationObserver,该观察者跟踪内容的变化。如果传入的是 元组,则第一个值是超时时间,第二个值是最大等待时间。如果只传入 数字,则它被视为超时时间,且没有最大等待时间。使用 null 表示不进行防抖处理。此选项对性能微调非常有用。
update.attributes
| 类型 | 默认值 |
|---|---|
string[] | null | null |
注意:即使此选项为
null,MutationObserver也会始终观察一组基础属性。
这是一个额外的属性数组,MutationObserver 将会观察这些属性的变化。
update.ignoreMutation
| 类型 | 默认值 |
|---|---|
((mutation) => any) | null | null |
这是一个接收 MutationRecord 作为参数的函数。如果该函数返回一个真值,变动将会被忽略,插件也不会更新。此选项对性能优化非常有用。
overflow.x
| 类型 | 默认值 |
|---|---|
string | 'scroll' |
注意:有效值为:
'hidden'、'scroll'、'visible'、'visible-hidden'和'visible-scroll'。
这是水平(x 轴)溢出行为的配置。
overflow.y
| 类型 | 默认值 |
|---|---|
string | 'scroll' |
注意:有效值为:
'hidden'、'scroll'、'visible'、'visible-hidden'和'visible-scroll'。
这是垂直(y 轴)溢出行为的配置。
scrollbars.theme
| 类型 | 默认值 |
|---|---|
string | null | 'os-theme-dark' |
将指定的主题(类名)应用到滚动条。
scrollbars.visibility
| 类型 | 默认值 |
|---|---|
string | 'auto' |
注意:有效值为:
'visible'、'hidden'和'auto'。
当滚动轴能够有可滚动的溢出时,滚动条的可见性设置。(对于一个轴的可滚动溢出,只有在溢出行为设置为 'scroll' 或 'visible-scroll' 时才可能发生)。
scrollbars.autoHide
| 类型 | 默认值 |
|---|---|
string | 'never' |
注意:有效值为:
'never'、'scroll'、'leave'和'move'。
决定是否在特定的用户操作后自动隐藏可见的滚动条。
scrollbars.autoHideDelay
| 类型 | 默认值 |
|---|---|
number | 1300 |
滚动条在自动隐藏之前的延迟时间(以毫秒为单位)。
scrollbars.autoHideSuspend
| 类型 | 默认值 |
|---|---|
boolean | false |
暂停自动隐藏功能,直到第一次滚动交互发生。
此选项的默认值为 false,为了向后兼容,但为了更好的可访问性,建议将其设置为 true。
scrollbars.dragScroll
| 类型 | 默认值 |
|---|---|
boolean | true |
指示是否可以拖动滚动条滑块进行滚动。
scrollbars.clickScroll
| 类型 | 默认值 |
|---|---|
boolean | 'instant' | false |
注意:如果设置为
true,则需要 ClickScrollPlugin 插件。
指示是否可以点击滚动条轨道进行滚动。
scrollbars.pointers
| 类型 | 默认值 |
|---|---|
string[] | null | ['mouse', 'touch', 'pen'] |
插件应响应的 PointerTypes。
TypeScript
// OverlayScrollbars 实例的配置选项。 type Options = { // 是否使用绝对定位的内边距。 paddingAbsolute: boolean; // 是否显示原生滚动条。仅当原生滚动条被叠加时有效。 showNativeOverlaidScrollbars: boolean; // 自定义自动更新行为。 update: { /** * 来自指定选择器的元素触发的事件将触发更新。 * 适用于 MutationObserver 和 ResizeObserver 无法检测的内容变化 * 例如:图片的 `load` 事件或 `transitionend` / `animationend` 事件。 */ elementEvents: Array<[elementSelector: string, eventNames: string]> | null; /** * 用于检测内容变化的防抖设置。 * 如果传入元组,可以自定义 `timeout` 和 `maxWait`(毫秒)。 * 如果仅传入一个数字,则仅自定义 `timeout`。 * * 如果 `timeout` 为 `0`,则仍会有防抖(并且使用 `requestAnimationFrame` 代替 `setTimeout`)。 */ debounce: [timeout: number, maxWait: number] | number | null; /** * 需要观察的 HTML 属性,若这些属性发生变化将触发更新。 * 基本属性如 `id`、`class`、`style` 等始终会被观察,不需要显式添加。 */ attributes: string[] | null; // 一个函数,允许忽略某个内容变动,如果不需要忽略任何变动则为 null。 ignoreMutation: ((mutation: MutationRecord) => any) | null; }; // 自定义每个轴的溢出行为。 overflow: { // 水平 (x) 轴的溢出行为。 x: OverflowBehavior; // 垂直 (y) 轴的溢出行为。 y: OverflowBehavior; }; // 自定义滚动条的外观。 scrollbars: { // 滚动条的主题。主题值将作为 `class` 添加到实例的所有 `scrollbar` 元素中。 theme: string | null; // 滚动条的可见性行为。 visibility: ScrollbarsVisibilityBehavior; // 滚动条的自动隐藏行为。 autoHide: ScrollbarsAutoHideBehavior; // 滚动条自动隐藏的延迟时间(以毫秒为单位)。 autoHideDelay: number; // 是否暂停滚动条自动隐藏行为,直到发生第一次滚动。 autoHideSuspend: boolean; // 是否可以拖动滚动条滑块来滚动视口。 dragScroll: boolean; // 是否可以点击滚动条轨道来滚动视口。 clickScroll: ScrollbarsClickScrollBehavior; // 支持的指针类型数组。 pointers: string[] | null; }; }; // 轴的溢出行为。 type OverflowBehavior = // 不允许滚动,内容被裁剪。 | 'hidden' // 不允许滚动,内容不被裁剪。 | 'visible' // 如果有溢出,则允许滚动。 | 'scroll' /** * 如果另一轴没有溢出,其行为类似于 `visible`。 * 如果另一轴有溢出,其行为类似于 `hidden`。 */ | 'visible-hidden' /** * 如果另一轴没有溢出,其行为类似于 `visible`。 * 如果另一轴有溢出,其行为类似于 `scroll`。 */ | 'visible-scroll'; // 滚动条的可见性行为。 type ScrollbarsVisibilityBehavior = // 滚动条始终可见。 | 'visible' // 滚动条始终隐藏。 | 'hidden' // 只有在发生溢出时,滚动条才会可见。 | 'auto'; // 滚动条的自动隐藏行为。 type ScrollbarsAutoHideBehavior = // 滚动条永不自动隐藏。 | 'never' // 滚动条在用户滚动时才会显示。 | 'scroll' // 滚动条在鼠标移动到宿主元素或用户滚动时才会显示。 | 'move' // 滚动条在鼠标离开宿主元素或用户未滚动时隐藏。 | 'leave'; // 滚动条点击滚动行为。 type ScrollbarsClickScrollBehavior = boolean | 'instant';
事件
你可以在初始化 OverlayScrollbars 时设置初始的事件,这些事件可以通过 on 和 off 方法随时进行管理:
OverlayScrollbars(document.querySelector('#myElement'), {}, { updated(osInstance, onUpdatedArgs) { // 事件处理逻辑... } });
事件深入解析
这是一个深入的主题。点击此处阅读。
注意:每个事件都会将触发事件的
instance作为第一个参数传递。始终如此。
initialized
| 参数 | 描述 |
|---|---|
instance | 触发事件的实例 |
在所有生成的元素(elements)、观察者(observers)和事件(events)被添加到 DOM 后触发。
updated
| 参数 | 描述 |
|---|---|
instance | 触发事件的实例 |
onUpdatedArgs | 一个 object,详细描述更新内容 |
注意:如果更新被触发但没有任何变化,则该事件不会被触发。
在实例更新后触发。
destroyed
| 参数 | 描述 |
|---|---|
instance | 触发事件的实例 |
canceled | 一个 boolean,指示初始化是否被取消,因此实例被销毁 |
在所有生成的元素(elements)、观察者(observers)和事件(events)被从 DOM 中移除后触发。
scroll
| 参数 | 描述 |
|---|---|
instance | 触发事件的实例 |
event | DOM 事件的原始 event 参数 |
通过滚动视口时触发。
TypeScript
// 事件名称与其监听器参数之间的映射 type EventListenerArgs = { // 在所有元素初始化并添加后触发 initialized: [instance: OverlayScrollbars]; // 在更新后触发 updated: [instance: OverlayScrollbars, onUpdatedArgs: OnUpdatedEventListenerArgs]; // 在所有元素、观察者和事件销毁后触发 destroyed: [instance: OverlayScrollbars, canceled: boolean]; // 在滚动时触发 scroll: [instance: OverlayScrollbars, event: Event]; }; interface OnUpdatedEventListenerArgs { // 描述 DOM 中发生了哪些变化的提示信息 updateHints: { // 主机元素的大小是否发生了变化 sizeChanged: boolean; // 主机元素的方向是否发生了变化 directionChanged: boolean; // 主机元素的内在高度行为是否发生了变化 heightIntrinsicChanged: boolean; // 视口元素的溢出边缘(clientWidth / clientHeight)是否发生了变化 overflowEdgeChanged: boolean; // 溢出量是否发生了变化 overflowAmountChanged: boolean; // 溢出样式是否发生了变化 overflowStyleChanged: boolean; // 滚动坐标是否发生了变化 scrollCoordinatesChanged: boolean; // 主机元素是否发生了变动 hostMutation: boolean; // 内容是否发生了变动 contentMutation: boolean; }; // 变化的选项 changedOptions: PartialOptions; // 是否是强制更新,且缓存被无效化 force: boolean; }
实例
通过调用 OverlayScrollbars 函数并传入一个元素和选项对象,可以创建一个 OverlayScrollbars 实例。
const osInstance = OverlayScrollbars(document.body, {});
实例方法
这是一个深入的主题。点击此处阅读。
options(): Options
获取实例的当前选项。
| 返回值 | 描述 |
|---|---|
Options | 当前的选项 |
options(newOptions, pure?): Options
设置实例的当前选项。
| 参数 | 类型 | 描述 |
|---|---|---|
| newOptions | PartialOptions | 应该应用的新(部分)选项。 |
| pure | boolean | undefined | 是否在添加新选项之前重置现有选项。 |
| 返回值 | 描述 |
|---|---|
Options | 完整的新选项 |
on(eventListeners, pure?): Function
为实例添加事件监听器。
| 参数 | 类型 | 描述 |
|---|---|---|
| eventListeners | EventListeners | 包含添加的监听器的对象。字段是事件名称和监听器。 |
| pure | boolean | undefined | 是否在添加新监听器之前移除所有已经添加的监听器。 |
| 返回值 | 描述 |
|---|---|
Function | 一个函数,用于移除所有已添加的事件监听器。 |
on(name, listener): Function
为实例添加单个事件监听器。
| 参数 | 类型 | 描述 |
|---|---|---|
| name | string | 事件名称 |
| listener | Function | 当事件被触发时调用的函数 |
| 返回值 | 描述 |
|---|---|
Function | 一个函数,用于移除已添加的事件监听器。 |
on(name, listeners): Function
为实例添加多个事件监听器。
| 参数 | 类型 | 描述 |
|---|---|---|
| name | string | 事件名称 |
| listeners | Function[] | 触发事件时调用的函数数组 |
| 返回值 | 描述 |
|---|---|
Function | 一个函数,用于移除已添加的事件监听器。 |
off(name, listener): void
从实例中移除单个事件监听器。
| 参数 | 类型 | 描述 |
|---|---|---|
| name | string | 事件名称 |
| listener | Function | 要移除的函数 |
off(name, listeners): void
从实例中移除多个事件监听器。
| 参数 | 类型 | 描述 |
|---|---|---|
| name | string | 事件名称 |
| listeners | Function[] | 要移除的函数数组 |
update(force?): boolean
更新实例。
| 参数 | 类型 | 描述 |
|---|---|---|
| force | boolean | undefined | 是否强制使缓存失效并进行更新。 |
| 返回值 | 描述 |
|---|---|
boolean | 一个布尔值,指示 update 事件是否通过此更新被触发。 |
state(): State
获取实例的状态。
| 返回值 | 描述 |
|---|---|
State | 描述实例状态的对象 |
elements(): Elements
获取实例的元素。
| 返回值 | 描述 |
|---|---|
Elements | 描述实例元素的对象 |
destroy(): void
销毁实例并移除所有已添加的元素。
plugin(plugin: object): object | undefined
获取传入插件的实例模块。
| 返回值 | 描述 |
|---|---|
object | undefined | 描述插件实例模块的对象,如果未找到实例,则返回 undefined。 |
TypeScript
// 简化版的 OverlayScrollbars TypeScript 接口。 interface OverlayScrollbars { // 获取实例的当前选项。 options(): Options; // 设置实例的当前选项。 options(newOptions: PartialOptions, pure?: boolean): Options; // 为实例添加事件监听器。 on(eventListeners: EventListeners, pure?: boolean): () => void; // 为实例添加单个事件监听器。 on<N extends keyof EventListenerArgs>(name: N, listener: EventListener<N>): () => void; // 为实例添加多个事件监听器。 on<N extends keyof EventListenerArgs>(name: N, listener: EventListener<N>[]): () => void; // 从实例中移除单个事件监听器。 off<N extends keyof EventListenerArgs>(name: N, listener: EventListener<N>): void; // 从实例中移除多个事件监听器。 off<N extends keyof EventListenerArgs>(name: N, listener: EventListener<N>[]): void; // 更新实例。 update(force?: boolean): boolean; // 获取实例的状态。 state(): State; // 获取实例的元素。 elements(): Elements; // 销毁实例并移除所有已添加的元素。 destroy(): void; // 获取传入插件的实例模块。 plugin<P extends InstancePlugin>(osPlugin: P): InferInstancePluginModuleInstance<P> | undefined; } // 描述 OverlayScrollbars 实例的状态。 interface State { // 当前的内边距(单位:像素)。 padding: TRBL; // 当前的内边距是否是绝对定位。 paddingAbsolute: boolean; // 视口的客户端宽度(x)和高度(y)(单位:像素)。 overflowEdge: XY<number>; // 溢出量(单位:像素)。 overflowAmount: XY<number>; // 视口的 CSS 溢出样式。 overflowStyle: XY<OverflowStyle>; // 视口是否有溢出。 hasOverflow: XY<boolean>; // 视口的滚动坐标。 scrollCoordinates: { // 每个轴的起始(原点)滚动坐标。 start: XY<number>; // 每个轴的结束滚动坐标。 end: XY<number>; }; // 方向是否被视为右到左(rtl)。 directionRTL: boolean; // 实例是否被视为销毁。 destroyed: boolean; } // 描述 OverlayScrollbars 实例的元素。 interface Elements { // 应用实例的目标元素。 target: HTMLElement; // 主机元素。它是所有其他元素的根元素。 host: HTMLElement; /** * 负责正确内边距的元素。 * 根据初始化,它可能与视口元素相同。 */ padding: HTMLElement; // 负责滚动的元素。 viewport: HTMLElement; /** * 负责持有实际内容的元素。 * 根据初始化,它可能与视口元素相同。 */ content: HTMLElement; /** * 通过该元素可以获取当前的 `scrollLeft` 或 `scrollTop` 偏移。 * 根据目标元素,它可能与视口元素相同。 */ scrollOffsetElement: HTMLElement; /** * 通过该元素可以添加 `scroll` 事件。 * 根据目标元素,它可能与视口元素相同。 */ scrollEventElement: HTMLElement | Document; // 水平滚动条的元素。 scrollbarHorizontal: CloneableScrollbarElements; // 垂直滚动条的元素。 scrollbarVertical: CloneableScrollbarElements; }
静态对象
静态的 OverlayScrollbars 对象。
OverlayScrollbars.plugin(SomePlugin);
静态对象方法
这是一个深入的主题。点击此处阅读。
valid(osInstance): boolean
检查传入的值是否为有效且未销毁的 OverlayScrollbars 实例。
| 参数 | 类型 | 描述 |
|---|---|---|
| osInstance | any | 需要检查的值 |
| 返回值 | 描述 |
|---|---|
boolean | 传入的值是否是一个有效且未销毁的 OverlayScrollbars 实例。 |
env(): Environment
获取环境信息。
| 返回值 | 描述 |
|---|---|
Environment | 描述环境信息的对象 |
nonce(newNonce): void
设置内联样式的 nonce 属性。
| 参数 | 类型 | 描述 |
|---|---|---|
| newNonce | string | undefined | 内联样式的 nonce 属性 |
plugin(plugin): object | undefined
添加单个插件。
| 参数 | 类型 | 描述 |
|---|---|---|
| plugin | object | 要添加的插件 |
| 返回值 | 描述 |
|---|---|
object | void | 描述插件的静态模块实例的对象,如果未找到实例,则返回 void。 |
plugin(plugins): (object | void)[]
添加多个插件。
| 参数 | 类型 | 描述 |
|---|---|---|
| plugins | object[] | 要添加的插件数组 |
| 返回值 | 描述 |
|---|---|
(object | void)[] | 描述插件的静态模块实例的数组,如果未找到实例,则返回 undefined。 |
TypeScript
// OverlayScrollbars 静态对象 interface OverlayScrollbarsStatic { // 获取传入目标的实例,如果目标没有实例则返回 `undefined`。 (target: InitializationTarget): OverlayScrollbars | undefined; // 使用传入的目标、选项和事件监听器初始化 OverlayScrollbars。 (target: InitializationTarget, options: PartialOptions, eventListeners?: EventListeners): OverlayScrollbars; // 检查传入的值是否为有效且未销毁的 OverlayScrollbars 实例。 valid(osInstance: any): osInstance is OverlayScrollbars; // 获取环境信息。 env(): Environment; // 设置内联样式的 `nonce` 属性。 nonce(newNonce: string | undefined): void; // 添加单个插件。 plugin(plugin: Plugin): InferStaticPluginModuleInstance<Plugin>; // 添加多个插件。 plugin(plugins: Plugin[]): InferStaticPluginModuleInstance<Plugin>[]; } // 描述 OverlayScrollbars 环境。 interface Environment { // 浏览器/系统的原生滚动条大小。 scrollbarsSize: XY<number>; // 原生滚动条是否为叠加式。 scrollbarsOverlaid: XY<boolean>; // 浏览器是否支持原生滚动条隐藏。 scrollbarsHiding: boolean; // 浏览器是否支持 ScrollTimeline API。 scrollTimeline: boolean; // 如果没有其他指定,默认初始化方式。 staticDefaultInitialization: Initialization; // 如果没有其他指定,默认选项。 staticDefaultOptions: Options; // 返回当前默认初始化方式。 getDefaultInitialization(): Initialization; // 返回当前默认选项。 getDefaultOptions(): Options; /** * 设置新的默认初始化方式。 * 如果新的默认初始化方式为部分填充,将与当前默认初始化方式进行深度合并。 * @param newDefaultInitialization 新的默认初始化方式。 * @returns 当前默认初始化方式。 */ setDefaultInitialization(newDefaultInitialization: PartialInitialization): Initialization; /** * 设置新的默认选项。 * 如果新的默认选项为部分填充,将与当前默认选项进行深度合并。 * @param newDefaultOptions 新的默认选项。 * @returns 当前默认选项。 */ setDefaultOptions(newDefaultOptions: PartialOptions): Options; }
样式
OverlayScrollbars 提供了两个主题,分别是 os-theme-dark 和 os-theme-light。您可以使用 scrollbars.theme 选项来更改主题。
自定义主题可以通过多种方式实现。最简单且最快的方法是使用预定义的 CSS Custom Properties,也就是 CSS 变量。如果这些不够用,您还可以添加自定义类名,或者对现有类名添加自定义样式。
样式深入解析
这是一个深入的主题。点击此处阅读。
CSS 自定义属性
OverlayScrollbars 提供了一组 CSS Custom Properties,使得滚动条样式的定制变得简单而快速:
.os-scrollbar { // 滚动条的大小 --os-size: 0; // 滚动条的轴垂直内边距(水平:padding-y,垂直:padding-x) --os-padding-perpendicular: 0; // 滚动条的轴内边距(水平:padding-x,垂直:padding-y) --os-padding-axis: 0; // 滚动条轨道的边框半径 --os-track-border-radius: 0; // 滚动条轨道的背景 --os-track-bg: none; // 滚动条轨道的 :hover 背景 --os-track-bg-hover: none; // 滚动条轨道的 :active 背景 --os-track-bg-active: none; // 滚动条轨道的边框 --os-track-border: none; // 滚动条轨道的 :hover 边框 --os-track-border-hover: none; // 滚动条轨道的 :active 边框 --os-track-border-active: none; // 滚动条滑块的边框半径 --os-handle-border-radius: 0; // 滚动条滑块的背景 --os-handle-bg: none; // 滚动条滑块的 :hover 背景 --os-handle-bg-hover: none; // 滚动条滑块的 :active 背景 --os-handle-bg-active: none; // 滚动条滑块的边框 --os-handle-border: none; // 滚动条滑块的 :hover 边框 --os-handle-border-hover: none; // 滚动条滑块的 :active 边框 --os-handle-border-active: none; // 滚动条滑块的最小尺寸 --os-handle-min-size: 33px; // 滚动条滑块的最大尺寸 --os-handle-max-size: none; // 滚动条滑块的轴垂直尺寸(水平:高度,垂直:宽度) --os-handle-perpendicular-size: 100%; // 滚动条滑块的 :hover 轴垂直尺寸(水平:高度,垂直:宽度) --os-handle-perpendicular-size-hover: 100%; // 滚动条滑块的 :active 轴垂直尺寸(水平:高度,垂直:宽度) --os-handle-perpendicular-size-active: 100%; // 增加滚动条滑块的交互区域。 --os-handle-interactive-area-offset: 0; }
您可以同时为两个滚动条或每个滚动条轴单独更改属性。以下示例中,我选择了 os-theme-custom 作为主题名称:
// 水平和垂直滚动条的大小均为 10px .os-theme-custom { --os-size: 10px; } // 水平滚动条的大小为 10px .os-theme-custom.os-scrollbar-horizontal { --os-size: 10px; } // 垂直滚动条的大小为 20px .os-theme-custom.os-scrollbar-vertical { --os-size: 20px; }
您可以通过将其分配给 scrollbars.theme 选项来使用您的主题:
OverlayScrollbars(document.body, { scrollbars: { theme: 'os-theme-custom' } });
由于滚动条样式通常比较简单,这一组选项应该足以让您实现所需的样式。如果您需要更多自由度,您可以通过向下一个章节描述的基础类名添加样式来建自己的样式。
滚动条结构和 CSS 类名
滚动条的 HTML 标签如下所示:
<div class="os-scrollbar os-scrollbar-horizontal"> <div class="os-scrollbar-track"> <div class="os-scrollbar-handle"> </div> </div> </div> <div class="os-scrollbar os-scrollbar-vertical"> <div class="os-scrollbar-track"> <div class="os-scrollbar-handle"> </div> </div> </div>
这些类名是简化过的,在实际应用中,.os-scrollbar 元素可能会有额外的类名,用于修改外观(主要是可见性和对齐方式)。
以下是您将会遇到的最重要的类名列表:
| CSS 类名 | 描述 |
|---|---|
.os-scrollbar | 滚动条的根元素。 |
.os-scrollbar-rtl | 表示滚动条所属的宿主元素是右至左 (RTL) 排列。 |
.os-scrollbar-horizontal | 水平滚动条的根元素。 |
.os-scrollbar-vertical | 垂直滚动条的根元素。 |
.os-scrollbar-handle-interactive | 表示滚动条内的滚动条滑块是可交互的(即 scrollbars.dragScroll 不为 false)。 |
.os-scrollbar-track-interactive | 表示滚动条内的轨道是可交互的(即 scrollbars.clickScroll 不为 false)。 |
.os-scrollbar-track | 轨道元素。此元素是滑块的轨道。如果 scrollbars.clickScroll 不为 false,这是用户点击来改变滚动偏移的元素。 |
.os-scrollbar-handle | 滑块元素。如果 scrollbars.dragScroll 不为 false,这是用户可以拖动来改变滚动偏移的元素。 |
如果您创建自己的主题,请仅使用上述列出的类名。其他类名是修饰类,用于改变滚动条的可见性、对齐方式和指针事件等。
注意事项
在您的 CSS 文件中选择的主题类名必须与选项中指定的主题名称匹配。如果 CSS 类名是 .my-theme,那么 scrollbars.theme 必须设置为 'my-theme'。
请注意您的技术栈。例如,css-modules 会改变您的类名以避免命名冲突。务必检查您的 CSS 是否符合预期。
插件
任何不被视为核心功能或用于旧版浏览器兼容性的功能,都通过插件进行扩展。这是为了确保所有未使用的插件在树摇(treeshaking)过程中被省略,从而不会出现在最终的打包文件中。OverlayScrollbars 附带了以下插件:
- ScrollbarsHidingPlugin:对于不支持原生滚动条样式的旧浏览器是必需的。您可以在这里找到需要此插件的浏览器列表(注意,即使
iOS Safari >= 14标记为不支持,您只需要此插件针对iOS < 7.1)。 - SizeObserverPlugin:对于不支持
ResizeObserverAPI 的旧浏览器是必需的。您可以在这里找到需要此插件的浏览器列表。 - ClickScrollPlugin:如果您想使用选项
scrollbars: { clickScroll: true }。
使用插件
插件的使用方式如下:
import { OverlayScrollbars, ScrollbarsHidingPlugin, SizeObserverPlugin, ClickScrollPlugin } from 'overlayscrollbars'; // 单个插件 OverlayScrollbars.plugin(ScrollbarsHidingPlugin); // 多个插件 OverlayScrollbars.plugin([SizeObserverPlugin, ClickScrollPlugin]);
插件深入解析
这是一个深入的话题。点击这里阅读。
插件是具有 单一字段 的普通对象,字段的名称就是插件的名称。这个名称是插件的标识符,必须 在所有插件中是唯一的。如果多个插件具有相同的名称,最后添加的插件会覆盖之前使用相同名称添加的插件。
插件模块
插件模块是插件模块实例的构造器。插件模块有两种类型:静态 和 实例。一个插件必须至少有一个模块。插件模块可以返回一个实例,但不一定返回。
静态插件模块
静态 插件模块在使用 OverlayScrollbars.plugin 函数添加插件时被调用。
具有 静态 模块的插件示例:
const staticPlugin = { // 插件名称为 `examplePlugin`。 examplePlugin: { // `static` 函数描述了一个静态模块,并返回模块实例,或者如果不需要实例则返回 `void` 或 `undefined`。 // `osStatic` 参数是全局的 `OverlayScrollbars` 对象。 static: (osStatic) => { let count = 0; const staticPluginModuleInstance = { getCount: () => count, increment: () => { count++ }, } return staticPluginModuleInstance; } } }
当插件通过 OverlayScrollbars.plugin 函数添加时,静态模块实例会被返回:
const staticModuleInstance = OverlayScrollbars.plugin(staticPlugin); // 调用插件的静态模块 staticModuleInstance.count; // 0 staticModuleInstance.increment(); staticModuleInstance.count; // 1
实例插件模块
实例 插件模块在创建新的 OverlayScrollbars 实例时被调用,但在分发 initialized 事件之前。
具有 实例 模块的插件示例:
const instancePlugin = { // 插件名称为 `examplePlugin`。 examplePlugin: { // `instance` 函数描述了一个实例模块,并返回模块实例,或者如果不需要实例则返回 `void` 或 `undefined`。 // `osInstance` 参数是插件绑定到的 `OverlayScrollbar` 实例。 // `event` 参数是一个函数,用于向实例添加事件,这些事件无法在插件外部移除。 // `osStatic` 参数是全局的 `OverlayScrollbar` 对象。 instance: (osInstance, event, osStatic) => { let count = 0; const instancePluginModuleInstance = { getCount: () => count, increment: () => { count++ }, } // 实例初始化时触发的事件。 event('initialized', () => { console.log("实例已初始化"); }); // 视口滚动时触发的事件。 const removeScrollEvent = event('scroll', () => { console.log("视口已滚动"); removeScrollEvent(); // 滚动后移除事件。 }); return instancePluginModuleInstance; } } }
当插件通过 OverlayScrollbars.plugin 函数添加时,从那时起,所有的 OverlayScrollbar 实例将自动添加该插件。之前创建的实例将不会包含该插件。实例模块实例通过 osInstance.plugin 函数返回:
OverlayScrollbars.plugin(instancePlugin); // 插件被添加 const osInstance = OverlayScrollbars(document.body, {}); // 插件的实例模块被调用 const instancePluginInstance = osInstance.plugin(instancePlugin); instancePluginInstance.count; // 0 instancePluginInstance.increment(); instancePluginInstance.count; // 1
TypeScript
// 描述一个 OverlayScrollbar 插件。 type Plugin< // 插件的名称。 Name extends string = string, // 静态模块的模块实例类型。 S extends PluginModuleInstance | void = PluginModuleInstance | void, // 实例模块的模块实例类型。 I extends PluginModuleInstance | void = PluginModuleInstance | void > = { [pluginName in Name]: PluginModule<S, I>; }; // 描述一个只有静态模块的 OverlayScrollbar 插件。 type StaticPlugin< Name extends string = string, T extends PluginModuleInstance = PluginModuleInstance > = Plugin<Name, T, void>; // 描述一个只有实例模块的 OverlayScrollbar 插件。 type InstancePlugin< Name extends string = string, T extends PluginModuleInstance = PluginModuleInstance > = Plugin<Name, void, T>; // 推断传入插件的静态模块实例类型。 type InferStaticPluginModuleInstance<T extends StaticPlugin>; // 推断传入插件的实例模块实例类型。 type InferInstancePluginModuleInstance<T extends InstancePlugin>;
常见问题
如何 获取/设置 我应用了 OverlayScrollbars 的元素的 滚动位置?
获取/设置 我应用了 OverlayScrollbars 的元素的 滚动位置?如果你将 OverlayScrollbars 应用到 body 元素上,你可以使用 window.scrollX、window.scrollY、window.scroll、window.scrollTo、window.scrollBy 或任何其他原生 API。
如果插件应用到其他元素上,你首先需要通过 instance.elements() 函数获取 viewport 元素。获取到这个元素后,你可以使用 element.scrollTop、element.scrollLeft、element.scroll、element.scrollTo、element.scrollBy 或任何其他原生 API。
const { viewport } = osInstance.elements(); const { scrollLeft, scrollTop } = viewport; // 获取滚动偏移 viewport.scrollTo({ top: 0 }); // 设置滚动偏移
是否可以 限制 / 调整滚动条滑块长度?
限制 / 调整滚动条滑块长度?你可以通过设置 min-width / min-height 和 max-width / max-height 样式来调整滚动条的滑块长度:
/* 水平边界 */ .os-scrollbar-horizontal .os-scrollbar-handle { min-width: 50px; max-width: 200px; } /* 垂直边界 */ .os-scrollbar-vertical .os-scrollbar-handle { min-height: 40px; max-height: 40px; }
你可以将两个属性设置为相同的值,以强制滚动条始终保持相同的大小。
设置 width 和 height 属性是无效的,因为这些属性是由插件自动设置的。
与 v1 的功能比较
scroll函数缺失,计划作为一个plugin(正在开发中)。- 目前不支持对
textarea元素的初始化,计划作为一个plugin(正在开发中)。
未来计划
- 提供基于插件的对缺失功能的支持。(可treeshake)
- 定期更新,包括修复 bug 和增强功能。(始终使用最新的浏览器功能)
- 改进测试。(单元测试和浏览器测试)
许可证
MIT
