Angular Material Sidenav路由切换后CSS异常问题求助
我使用mat-sidenav实现路由跳转,切换新路由后遇到CSS异常问题:通过sidenav点击进入某路由页面,再次打开sidenav准备切换路由时,第一个路由项会出现hover/focus的背景效果,视觉体验不佳。
html template file
<mat-sidenav-container class="sidenav-container"> <mat-sidenav #drawer class="sidenav" fixedInViewport [attr.role]="(isHandset$ | async) ? 'dialog' : 'navigation'" [mode]="(isHandset$ | async) ? 'over' : 'side'" [opened]="(isHandset$ | async) === false"> <div class="d-flex flex-column justify-content-center align-items-center py-2"> <img class="avatar my-2" src="./assets/Images/DefaultPicture.jpg" /> <div class="d-flex justify-content-center flex-column text-center"> <p class="name md-info sm-header mb-0">{{ProfileDatails?.title[0]?.id != 'other' ? ProfileDatails?.title[0]?.title_name: ProfileDatails?.other_title }}</p> <h5 class="">{{ProfileDatails?.first_name}} {{ ProfileDatails?.last_name }}</h5> </div> </div> <mat-divider></mat-divider> <mat-nav-list> <a routerLinkActive="active_list_item" mat-list-item routerLink="appointments"> <mat-icon class="me-2 post-rel-top-5 fw-bold">list_alt</mat-icon> <span>Poojas</span> </a> <a routerLinkActive="active_list_item" mat-list-item routerLink="host"> <mat-icon class="me-2 post-rel-top-5">group</mat-icon> <span>Yajmans</span> </a> <a routerLinkActive="active_list_item" mat-list-item routerLink="pooja"> <mat-icon class="me-2 post-rel-top-5">edit_square</mat-icon> <span>Pooja's & Material</span> </a> <a routerLinkActive="active_list_item" mat-list-item routerLink="settings"> <mat-icon class="me-2 post-rel-top-5">settings</mat-icon> <span>Settings</span> </a> </mat-nav-list> <mat-divider></mat-divider> <mat-nav-list> <a mat-list-item (click)="openDialog()"> <mat-icon class="me-2 post-rel-top-5">logout</mat-icon> <span>Logout</span> </a> </mat-nav-list> </mat-sidenav> <mat-sidenav-content class="overflow-x-hidden"> <mat-toolbar color="primary" class="z-3"> <button type="button" aria-label="Toggle sidenav" mat-icon-button (click)="drawer.toggle()" *ngIf="isHandset$ | async"> <mat-icon aria-label="Side nav toggle icon">menu</mat-icon> </button> </mat-toolbar> <mat-progress-bar class="position-absolute" color="accent" mode="indeterminate" value="40" *ngIf="this.ds.progressbar"></mat-progress-bar> <router-outlet></router-outlet> </mat-sidenav-content> </mat-sidenav-container>
ts File
import { AfterViewChecked, AfterViewInit, ChangeDetectorRef, Component, OnChanges, OnInit, SimpleChanges, ViewChild, inject } from '@angular/core'; import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout'; import { Observable, firstValueFrom } from 'rxjs'; import { filter, map, shareReplay, withLatestFrom } from 'rxjs/operators'; import { MatSidenav } from '@angular/material/sidenav'; import { NavigationEnd, Router } from '@angular/router'; import { LogoutComponent } from './logout/logout.component'; import { MatDialog } from '@angular/material/dialog'; import { DashboardService } from './dashboard.service'; import { Title } from '@angular/platform-browser'; import { CommonService } from '../services/common.service'; import { ToastrService } from 'ngx-toastr'; @Component({ selector: 'app-dashboard', templateUrl: './dashboard.component.html', styleUrls: ['./dashboard.component.css'] }) export class DashboardComponent implements OnInit, AfterViewInit, OnChanges, AfterViewChecked { @ViewChild(MatSidenav) sidenav!: MatSidenav; private breakpointObserver = inject(BreakpointObserver); constructor(private titleService: Title, private router: Router, public dialog: MatDialog, public ds: DashboardService, private cdr: ChangeDetectorRef, private comserv: CommonService, private toaster: ToastrService) { router.events.pipe(withLatestFrom(this.isHandset$), filter(([a, b]) => b && a instanceof NavigationEnd) ).subscribe(_ => { // this.sidenav?.close() } ); } theme!: string; ProfileDatails: any; ngOnInit(): void { this.getProfile(); this.titleService.setTitle("Panditji | Dashboard"); let theme = localStorage.getItem('theme'); this.theme = theme || 'theme-light'; this.ds.renderBodywithClass(this.theme); } async getProfile() { try { let res: any = await firstValueFrom(this.ds.getProfile()); if (res.status) { this.ProfileDatails = res.data; } } catch (e: any) { if (e.error.code == 401 || e.error.code == 403) { this.ds.sessionTimeout(); } else { this.toaster.error('', e.error?.message ? e.error.message : 'Something went wrong'); } } } ngAfterViewInit(): void { this.cdr.detectChanges(); } ngOnChanges(changes: SimpleChanges): void { } ngAfterViewChecked() { this.cdr.detectChanges(); } isHandset$: Observable<boolean> = this.breakpointObserver.observe(Breakpoints.Handset) .pipe( map(result => result.matches), shareReplay() ); openDialog(): void { const dialogRef = this.dialog.open(LogoutComponent, { width: '340px', }); } }
CSS file
.sidenav-container { height: 100%; } .sidenav { width: 250px; } .sidenav .mat-toolbar { background: inherit; } .mat-toolbar.mat-primary { position: sticky; top: 0; z-index: 1; } .post-rel-top-5 { position: relative; top: 5px; } .active_list_item { border-left: 5px solid #ff8f00; background-color: #b8b8b8; word-wrap: normal; } .active_list_item>a { word-wrap: normal; background-color: #b8b8b8; border-left: 5px solid #ff8f00; } .z-index { z-index: 1000; } .avatar { vertical-align: middle; width: 100px; height: 100px; border-radius: 50%; }
解决方案
这个问题的核心原因是路由切换后,浏览器焦点自动回落到sidenav内的首个可交互元素(即第一个路由链接),触发了Material组件默认的focus/hover样式。以下是几种可行的修复方案:
方案1:手动重置焦点到sidenav容器
修改TS文件中的路由事件监听逻辑,在路由切换完成后将焦点设置到sidenav本身,避免焦点落在菜单项上:
constructor(/* 保留原有注入 */) { router.events.pipe( withLatestFrom(this.isHandset$), filter(([a, b]) => b && a instanceof NavigationEnd) ).subscribe(async () => { // 移动端路由切换后重置侧边栏状态并转移焦点 if(this.sidenav.opened) { await this.sidenav.close().toPromise(); await this.sidenav.open().toPromise(); } // 将焦点设置到sidenav容器,而非菜单项 this.sidenav.nativeElement.focus(); }); }
方案2:CSS强制覆盖未激活项的焦点/hover样式
通过CSS明确移除未激活菜单项的focus和hover背景效果,只保留激活项的样式:
/* 移除未激活菜单项的hover/focus背景 */ .mat-nav-list a.mat-list-item:not(.active_list_item):hover, .mat-nav-list a.mat-list-item:not(.active_list_item):focus { background-color: transparent !important; } /* 确保激活项样式优先级 */ .mat-nav-list a.mat-list-item.active_list_item { border-left: 5px solid #ff8f00; background-color: #b8b8b8 !important; }
方案3:精确匹配路由激活状态
给routerLinkActive添加精确匹配配置,避免路由前缀匹配导致激活状态异常,间接影响焦点样式:
<a routerLinkActive="active_list_item" [routerLinkActiveOptions]="{ exact: true }" mat-list-item routerLink="appointments"> <mat-icon class="me-2 post-rel-top-5 fw-bold">list_alt</mat-icon> <span>Poojas</span> </a>
将上述配置应用到所有带routerLink的菜单项上。
内容的提问来源于stack exchange,提问作者ErMapsh
相关产品推荐
相关产品推荐

