如何获取Angular Material Tab组件中mat-tab-body-content的scrollTop值
解决方案
问题原因
- mat-tab默认采用惰性渲染策略,未激活标签的DOM不会初始化,无法提前获取所有
mat-tab-body-content元素 - 你之前添加的
cdkScrollable指令没有绑定到实际产生滚动的mat-tab-body-content元素(该元素由Angular Material内部生成,无法直接在模板中操作),因此ScrollDispatcher无法监听到滚动事件 - 标签页从API异步加载,需要等DOM渲染完成后才能绑定滚动监听
实现方案
方案1:原生滚动事件监听(无需修改CDK配置,兼容性最好)
步骤1:修改模板,给mat-tab-group添加模板引用
<mat-tab-group #tabGroup animationDuration="0ms" (selectedTabChange)="onTabSelected($event)">
如果需要保留所有标签页的DOM不被销毁(禁用惰性加载),给mat-tab添加preserveContent属性:
<mat-tab *ngFor="let tab of tabsContainer.tabsList; trackBy: trackById; index as idx" [label]="tab.label" preserveContent>
步骤2:组件类实现滚动监听逻辑
import { ViewChild, OnDestroy } from '@angular/core'; import { MatTabGroup, MatTabChangeEvent } from '@angular/material/tabs'; export class YourComponent implements OnDestroy { @ViewChild('tabGroup') tabGroup: MatTabGroup; // 存储各标签页的滚动位置,key为标签索引 tabScrollPositions: Record<number, number> = {}; // 存储监听事件移除方法,避免内存泄漏 private scrollListeners: Map<Element, () => void> = new Map(); // 标签页加载完成后触发该方法 initTabScrollListeners() { // 等待DOM渲染完成 setTimeout(() => { // 清除旧的监听事件 this.scrollListeners.forEach(removeFn => removeFn()); this.scrollListeners.clear(); // 获取所有已渲染的标签内容元素 const tabBodyEls = document.querySelectorAll<HTMLElement>('.mat-tab-body-content'); tabBodyEls.forEach((el, index) => { const scrollHandler = () => { this.tabScrollPositions[index] = el.scrollTop; // 可在此处添加自定义逻辑,比如同步到服务存储 console.log(`标签${index}滚动位置:`, this.tabScrollPositions[index]); }; el.addEventListener('scroll', scrollHandler); // 存储移除方法 this.scrollListeners.set(el, () => el.removeEventListener('scroll', scrollHandler)); }); }, 0); } // 切换标签时可恢复之前存储的滚动位置 onTabSelected(event: MatTabChangeEvent) { const targetIndex = event.index; setTimeout(() => { const targetEl = document.querySelectorAll<HTMLElement>('.mat-tab-body-content')[targetIndex]; if (targetEl && this.tabScrollPositions[targetIndex]) { targetEl.scrollTop = this.tabScrollPositions[targetIndex]; } }, 0); } ngOnDestroy() { // 组件销毁时清除所有监听 this.scrollListeners.forEach(removeFn => removeFn()); this.scrollListeners.clear(); } }
步骤3:触发监听初始化
在标签页异步加载完成的节点调用initTabScrollListeners,可以直接在模板的async加载处触发:
<ng-container *ngIf="adressenTabsList$ | async as tabsContainer"> {{ initTabScrollListeners() }} <!-- 原有表单和标签逻辑 --> </ng-container>
方案2:修复CDK Scroll监听
如果要使用ScrollDispatcher监听,需要将滚动容器改为自己可控的元素:
- 每个标签内容外层包裹一个自定义滚动容器,添加
cdkScrollable指令,设置滚动样式:
<ng-container *ngSwitchCase="tabsType.kunde"> <div cdkScrollable style="height: 100%; overflow-y: auto;"> <kunde-tab [kundeForm]="kundeForm"></kunde-tab> </div> </ng-container>
- 确认模块已导入
ScrollingModule,此时你之前写的ScrollDispatcher监听代码即可正常触发。
覆盖场景说明
- 标签切换:每次滚动时实时存储位置,无需监听切换前事件,切换时直接读取已存储的数值即可
- 路由切换:组件销毁时自动清除监听,若需要跨路由保留滚动位置,可将
tabScrollPositions存储到公共服务中 - 动态标签变更:每次标签列表更新时都会调用
initTabScrollListeners重新绑定监听,覆盖新增/删除标签的场景
内容的提问来源于stack exchange,提问作者Francesco
相关产品推荐
相关产品推荐

