跳转到内容

InfiniteScroll 无限滚动 ​

获取下一页的通用触发器,滚动只是默认的触发方式。

用法 ​

哨兵滚进可视区即派发 load,取数完成后把 loading 写回 false

第 1 条
第 2 条
第 3 条
第 4 条
第 5 条
第 6 条
第 7 条
第 8 条
第 9 条
第 10 条
第 11 条
第 12 条

组件结构 ​

加粗的是必需部件。

data-scope="infinite-scroll":root · sentinel · load-more-trigger

示例 ​

提前量 ​

distance 把可视区沿块轴向外扩展,哨兵尚未出现就先取下一页

第 1 条
第 2 条
第 3 条
第 4 条
第 5 条
第 6 条
第 7 条
第 8 条
第 9 条
第 10 条
第 11 条
第 12 条

没有更多数据 ​

最后一页取完后开启 disabled,哨兵不再被观察,load 也不再派发

第 1 条
第 2 条
第 3 条
第 4 条
第 5 条
第 6 条
第 7 条
第 8 条
第 9 条
第 10 条
第 1 / 3 页 · 共 10 条

状态透出 ​

phase / loading / disabled 由组件交给宿主,加载提示与结束语都由宿主自行放置

第 1 条
第 2 条
第 3 条
第 4 条
第 5 条
第 6 条
第 7 条
第 8 条
第 9 条
第 10 条

继续往下滚(当前 idle)

取下一页的按钮 ​

与哨兵同一条通路:读屏在虚拟光标模式下不产生滚动事件,该按钮是它的键盘等价入口

第 1 条
第 2 条
第 3 条
第 4 条
第 5 条
第 6 条
第 7 条
第 8 条
第 9 条
第 10 条

设计指引 ​

何时使用 ​

  • 时间流、消息列表等用户只需要继续加载的内容。

何时不用 ​

  • 用户需要跳到确定位置或分享某一页时,使用分页。
  • 页面有页脚需要可达时,无限滚动会使页脚无法到达。

特性 ​

  • distance 是提前量:距底部该距离时触发,用户感觉不到等待。
  • loading 与 disabled 由组件交给宿主,加载提示与结束语由宿主放置。
  • 加载完成后关闭即可,不会再触发。
  • load-more-trigger 是同一通路的另一个入口:一个真实按钮,取数中与关闭时自动停用。它是铺满一行的独立动作条目:宽度由容器给、高度随内容,中性描边与透明底,按下只换面不缩放。

组合 ​

  • 与列表、骨架屏配合。
  • 与虚拟滚动组合为边滚边取的长列表:target 指向虚拟滚动的视口,哨兵放在内容层之后;示例见虚拟滚动页面。

最佳实践 ​

  • 提供明确的结束提示:“没有更多了”优于无声停止。
  • 加载失败时可以重试,不静默停止。
  • 放置一个 load-more-trigger:读屏在虚拟光标模式下不产生滚动事件,只靠哨兵无法获取第二页。
  • 按钮的文案写在按钮内,组件不代填名称,读屏读出的与视觉一致。

反模式 ​

  • 页面底部有重要内容(页脚、版权、联系方式)却用无限滚动。
  • 不提供结束提示,用户持续向下滚动。
  • 与虚拟滚动合用时把哨兵放在条目之间:窗口外的条目不渲染,哨兵永远无法进入可视区。
  • 与虚拟滚动合用时不提供 target:提前量按整页可视区计算,而实际滚动的是虚拟滚动的视口。

API 参考 ​

产物 ​

层值
自定义元素<xh-infinite-scroll>
Vue 组件XhInfiniteScrollLoadMoreTrigger XhInfiniteScrollRoot XhInfiniteScrollSentinel
组合式函数useInfiniteScroll
状态机infiniteScrollMachine
皮肤@xihan-ui/styles/infinite-scroll.css

Props ​

属性类型必填说明
distancenumber提前量(px):哨兵距可视区该距离即视为进入,默认 0(实际出现才计)。扩展的是 getTargetEl 给出的可视区。
disabledboolean关闭:不再观察,也不再触发。列表已没有下一页时使用。
loadingboolean正在取数:期间不观察、不重复触发。取完由宿主写回 false。
onLoad() => void应取下一页。

事件 ​

自定义元素将载荷放在 detail;Vue 使用同名 emit。

事件载荷说明
load``应取下一页

插槽 ​

仅列出带载荷的插槽。

Vue 组件插槽载荷说明
XhInfiniteScrollRootdefaultInfiniteScrollRootSlotProps

React 适配器 props ​

只列各组件自己声明的那些:继承自 ComponentPropsWithRef 的 DOM 属性不在其中,根组件上与上面 Props 表同名的也不重复列。Vue 的对应物是上面的插槽表。

React 组件属性类型必填说明
XhInfiniteScrollRoottargetHTMLElement | null裁剪出可视区的滚动容器,默认即整页滚动;distance 的提前量扩展的正是这块区域。
XhInfiniteScrollRootchildrenSlotChildren<InfiniteScrollRootSlotProps>

状态 ​

以下名称仅用于内部状态机。

状态:idle · loading · paused

事件:SENTINEL.ENTER · LOAD · MODE.SYNC · PRESS.START · PRESS.END

判据:isPaused · isLoading · canPress

connect API ​

getXxxProps() 返回对应部件的宿主属性。

成员类型说明
phaseInfiniteScrollPhase
loadingboolean正在取数。
disabledboolean已关闭,不再观察。
getRootProps() => T['element']
getSentinelProps() => T['element']
getLoadMoreTriggerProps() => T['button']取下一页的按钮。文案由作者写在按钮中,组件不代填。

无障碍 ​

键盘 ​

规格出处:W3C APG

按键生效条件行为
Enter / Spaceheld in load-more-trigger, 未关闭且未在取数按住期间 load-more-trigger 投影 data-pressed,与指针 :active 同一副按压面(row 档只换面不缩放);抬起、失焦或进入取数 / 关闭撤下

ARIA ​

以下属性由 connect 生成。

部件属性值
rootaria-busy'true' | undefined
sentinelaria-hidden'true'

样式参考 ​

皮肤 ​

@xihan-ui/styles/infinite-scroll.css 使用 [data-scope="infinite-scroll"][data-part="root"] 部件选择器,位于 xihan.components 层。覆盖样式使用 xihan.overrides。

数据属性 ​

由 connect 生成;条件不成立时不输出无值属性。

部件属性值
rootdata-disabled''(条件成立时才出现)
rootdata-loading''(条件成立时才出现)
load-more-triggerdata-disabled''(条件成立时才出现)
load-more-triggerdata-loading''(条件成立时才出现)
load-more-triggerdata-pressed''(条件成立时才出现)
load-more-triggerdata-xh-action-control''
load-more-triggerdata-xh-action-display'always'
load-more-triggerdata-xh-action-profile'row'
load-more-triggerdata-xh-action-size'md'
load-more-triggerdata-xh-action-variant'outline'

CSS 变量 ​

本组件公开覆盖槽由独立皮肤的实际消费位生成;默认来源、作用部件和状态均与 CSS 同源。

变量部件CSS 属性状态默认来源说明
--xh-infinite-scroll-load-more-bgload-more-trigger--xh-ink-surface
background-color
default
xh-ink-surface
--xh-_action-variant-bg-restinfinite-scroll 的 load-more-trigger 部件 --xh-ink-surface、background-color 覆盖槽。
--xh-infinite-scroll-load-more-bg-activeload-more-triggerbackground-colordisabled
is(:active, [data-pressed])
loading
not([data-disabled])
not([data-loading])
pressed
--xh-_action-variant-bg-pressedinfinite-scroll 的 load-more-trigger 部件 background-color 覆盖槽。
--xh-infinite-scroll-load-more-bg-hoverload-more-triggerbackground-colordisabled
hover
loading
not([data-disabled])
not([data-loading])
--xh-_action-variant-bg-hoverinfinite-scroll 的 load-more-trigger 部件 background-color 覆盖槽。
--xh-infinite-scroll-load-more-borderload-more-triggerborderdefault--xh-_action-variant-border-restinfinite-scroll 的 load-more-trigger 部件 border 覆盖槽。
--xh-infinite-scroll-load-more-border-hoverload-more-triggerborder-colordisabled
hover
is(:active, [data-pressed])
loading
not([data-disabled])
not([data-loading])
pressed
--xh-_action-variant-border-hover
--xh-_action-variant-border-pressed
infinite-scroll 的 load-more-trigger 部件 border-color 覆盖槽。
--xh-infinite-scroll-load-more-fgload-more-triggercolordefault
disabled
hover
is(:active, [data-pressed])
loading
not([data-disabled])
not([data-loading])
pressed
--xh-_action-variant-fg-hover
--xh-_action-variant-fg-pressed
--xh-_action-variant-fg-rest
infinite-scroll 的 load-more-trigger 部件 color 覆盖槽。
--xh-infinite-scroll-load-more-font-sizeload-more-triggerfont-sizedefault--xh-text-body-sizeinfinite-scroll 的 load-more-trigger 部件 font-size 覆盖槽。
--xh-infinite-scroll-load-more-gapload-more-triggergapdefault--xh-_action-profile-gapinfinite-scroll 的 load-more-trigger 部件 gap 覆盖槽。
--xh-infinite-scroll-load-more-hload-more-triggerblock-size
min-block-size
default
xh-action-profile=row
--xh-_action-profile-visual-sizeinfinite-scroll 的 load-more-trigger 部件 block-size、min-block-size 覆盖槽。
--xh-infinite-scroll-load-more-icon-sizeload-more-trigger--xh-icon-sizedefault--xh-_action-profile-glyph-sizeinfinite-scroll 的 load-more-trigger 部件 --xh-icon-size 覆盖槽。
--xh-infinite-scroll-load-more-pxload-more-triggerpadding-inlinedefault--xh-_action-profile-padding-inlineinfinite-scroll 的 load-more-trigger 部件 padding-inline 覆盖槽。
--xh-infinite-scroll-load-more-radiusload-more-triggerborder-radiusdefault--xh-_action-profile-radiusinfinite-scroll 的 load-more-trigger 部件 border-radius 覆盖槽。
--xh-infinite-scroll-sentinel-sizesentinelblock-sizedefault--xh-stroke-thininfinite-scroll 的 sentinel 部件 block-size 覆盖槽。

动效 ​

动效角色:按压 · 状态(见动效规范)。

本组件皮肤不含过渡与关键帧,也没有脚本驱动的动效:状态一变,外观立即到位。

RTL ​

皮肤用逻辑属性排布(inline-start 一族),dir="rtl" 下自动镜像。

Released under The MIT License