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

如何获取routerLinkActive的ElementRef,定位Material Design墨水栏

如何获取routerLinkActive对应的ElementRef来定位Material侧边栏的墨水栏

我之前在自定义Material侧边导航的墨水栏位置时,也遇到过完全一样的需求,下面是几个经过验证的可行方案,你可以根据自己的场景选择:

方案一:通过RouterLinkActive指令实例获取ElementRef

这种方式更符合Angular的组件化思路,能直接拿到指令绑定的元素引用,避免直接操作DOM的潜在问题。

步骤说明:

  • 给模板中的routerLinkActive指令添加模板引用变量,暴露指令实例
  • 在组件类中通过@ViewChildren获取所有该指令的实例,筛选出激活状态的那个,进而拿到对应的ElementRef

代码示例:

模板部分:

<mat-sidenav fxLayout='column' fxLayoutAlign='start center' #sidenav mode="over" [(opened)]="opened" position="end" class="nav-sidenav">
  <div class="nav-sidenav-container" fxFlex='1 1 100%'>
    <div class="ink-bar" #inkBar></div>
    <!-- 给每个导航项的routerLinkActive添加模板引用变量 -->
    <a routerLink="/home" routerLinkActive="active-link" #rla="routerLinkActive" class="nav-item">首页</a>
    <a routerLink="/profile" routerLinkActive="active-link" #rla="routerLinkActive" class="nav-item">个人中心</a>
    <a routerLink="/settings" routerLinkActive="active-link" #rla="routerLinkActive" class="nav-item">设置</a>
  </div>
</mat-sidenav>

组件类部分:

import { Component, ViewChildren, ElementRef, QueryList, AfterViewInit, Renderer2 } from '@angular/core';
import { RouterLinkActive } from '@angular/router';

@Component({
  selector: 'app-side-nav',
  templateUrl: './side-nav.component.html',
  styleUrls: ['./side-nav.component.scss']
})
export class SideNavComponent implements AfterViewInit {
  @ViewChildren('rla') routerLinkActives!: QueryList<RouterLinkActive>;
  @ViewChildren('inkBar') inkBar!: ElementRef<HTMLElement>;

  constructor(private renderer: Renderer2) {}

  ngAfterViewInit(): void {
    // 监听路由激活状态变化,实时更新墨水栏位置
    this.routerLinkActives.changes.subscribe(() => {
      this.updateInkBarPosition();
    });

    // 组件初始化时先设置一次初始位置
    this.updateInkBarPosition();
  }

  private updateInkBarPosition(): void {
    // 筛选出当前处于激活状态的路由链接指令
    const activeLink = this.routerLinkActives.find(rla => rla.isActive);
    if (activeLink && this.inkBar) {
      const activeEl = activeLink.elementRef.nativeElement;
      const inkBarEl = this.inkBar.nativeElement;
      const sidenavRect = activeEl.parentElement?.getBoundingClientRect();
      
      if (sidenavRect) {
        // 计算激活元素相对于侧边栏容器的位置,设置墨水栏样式
        const rect = activeEl.getBoundingClientRect();
        this.renderer.setStyle(inkBarEl, 'top', `${rect.top - sidenavRect.top}px`);
        this.renderer.setStyle(inkBarEl, 'left', `${rect.left - sidenavRect.left}px`);
        this.renderer.setStyle(inkBarEl, 'width', `${rect.width}px`);
        this.renderer.setStyle(inkBarEl, 'height', `${rect.height}px`);
      }
    }
  }
}

方案二:利用routerLinkActive自动添加的类名查询DOM

如果你的场景比较简单,也可以直接通过routerLinkActive默认添加的router-link-active类名(或你自定义的类名)来查询激活元素,实现起来更快捷。

组件类部分关键代码:

import { Component, ElementRef, AfterViewInit, Renderer2 } from '@angular/core';
import { Router, NavigationEnd } from '@angular/router';
import { filter } from 'rxjs';

@Component({
  selector: 'app-side-nav',
  templateUrl: './side-nav.component.html',
  styleUrls: ['./side-nav.component.scss']
})
export class SideNavComponent implements AfterViewInit {
  @ViewChildren('inkBar') inkBar!: ElementRef<HTMLElement>;

  constructor(private router: Router, private renderer: Renderer2) {}

  ngAfterViewInit(): void {
    // 监听路由跳转结束事件,更新墨水栏位置
    this.router.events.pipe(
      filter(event => event instanceof NavigationEnd)
    ).subscribe(() => {
      this.updateInkBarPosition();
    });

    // 初始化设置位置
    this.updateInkBarPosition();
  }

  private updateInkBarPosition(): void {
    // 查询带有激活类名的元素(这里用默认的router-link-active,自定义类名的话替换成你的)
    const activeEl = document.querySelector('.router-link-active');
    if (activeEl && this.inkBar) {
      const inkBarEl = this.inkBar.nativeElement;
      const rect = activeEl.getBoundingClientRect();
      const sidenavRect = activeEl.parentElement?.getBoundingClientRect();
      
      if (sidenavRect) {
        this.renderer.setStyle(inkBarEl, 'top', `${rect.top - sidenavRect.top}px`);
        this.renderer.setStyle(inkBarEl, 'left', `${rect.left - sidenavRect.left}px`);
        this.renderer.setStyle(inkBarEl, 'width', `${rect.width}px`);
        this.renderer.setStyle(inkBarEl, 'height', `${rect.height}px`);
      }
    }
  }
}

额外注意事项

  • 如果你给routerLinkActive自定义了类名,记得把查询的类名改成你设置的(比如模板里的active-link,查询时就用.active-link)
  • 处理嵌套路由时,需要给routerLinkActive添加[routerLinkActiveOptions]="{exact: true}"确保精确匹配,避免多个导航项同时被标记为激活
  • 墨水栏的基础样式需要设置为绝对定位,示例SCSS:
.nav-sidenav-container {
  position: relative;
  .ink-bar {
    position: absolute;
    background-color: #1976d2; // Material主题色,可按需修改
    border-radius: 4px;
    transition: all 0.3s ease; // 添加过渡动画,贴合Material风格
    pointer-events: none; // 避免遮挡导航项的点击事件
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 06:43:57