You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何获取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监听,需要将滚动容器改为自己可控的元素:

  1. 每个标签内容外层包裹一个自定义滚动容器,添加cdkScrollable指令,设置滚动样式:
<ng-container *ngSwitchCase="tabsType.kunde">
  <div cdkScrollable style="height: 100%; overflow-y: auto;">
    <kunde-tab [kundeForm]="kundeForm"></kunde-tab>
  </div>
</ng-container>
  1. 确认模块已导入ScrollingModule,此时你之前写的ScrollDispatcher监听代码即可正常触发。

覆盖场景说明

  • 标签切换:每次滚动时实时存储位置,无需监听切换前事件,切换时直接读取已存储的数值即可
  • 路由切换:组件销毁时自动清除监听,若需要跨路由保留滚动位置,可将tabScrollPositions存储到公共服务中
  • 动态标签变更:每次标签列表更新时都会调用initTabScrollListeners重新绑定监听,覆盖新增/删除标签的场景

内容的提问来源于stack exchange,提问作者Francesco

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.08 06:24:04