前言:
angular页面内跳转,需求是按钮点击后跳转到对应名字标题的位置,标题已设置同名id,且跳转位置不能被顶部固定导航栏遮住。
一开始是用a标签href跳转,标题css加了:target伪类(《锚点定位被顶部固定导航栏遮住的解决方案》文章中的方法4)完美解决,但是后来由于路由前加了服务标识区分(xxx-frontend-web),像这样:
http://localhost:4200/xxx-frontend-web/#/home#我是标题
导致了a标签跳转失效,a标签的只能跳到http://localhost:4200/home#我是标题,其实也可以在所有a标签href里加服务区分标识,但是,这种方法还是太蠢了。。。
然后找到的方法是用js原生的scrollIntoView,配合在标题css上加一个padding-top:140px(那篇文章的方法2),凑合解决。
const element = document.querySelector('#' + id);
if (element) {
element.scrollIntoView({ behavior: 'smooth' });
// 路由跳转(让url也加上#id)
const _path = this.getUrlRelativePath();
this.router.navigate([_path], { fragment: id });
}
没过几天,开始做面包屑,发现面包屑正好被标题的padding挡住按不到。。。看来还是得找替代方法
解决方法1
终于在stackoverflow上大佬的回答里找到了不用加padding的解决方案:
const element = document.querySelector('#' + id);
if (element) {
const yOffset = -140;
const y = element.getBoundingClientRect().top + window.pageYOffset + yOffset;
window.scrollTo({top: y, behavior: 'smooth'});
// 路由跳转(让url也加上#id)
const _path = this.getUrlRelativePath();
this.router.navigate([_path], { fragment: id });
}
解决方法2
在查询过程中发现angular Api中有页面内滚动的方法:ViewportScroller,试了一下发现比原生js的更好用:
//需要先在构造时候引入
constructor(private _vps: ViewportScroller) {}
//然后方法中即可直接调用:
// 设置滚动偏差([x,y], 此处y是header高度)
this._vps.setOffset([0, 140]);
// 滚动到id位置
this._vps.scrollToAnchor(id);
// 路由跳转
const _path = this.getUrlRelativePath();
this.router.navigate([_path], { fragment: id });
2021/8/30 更新 解决方法2改
解决方法2中的setOffset可以在Angular引入Router时的初始化参数里设置:
@NgModule({
imports: [
RouterModule.forRoot(routes, {
scrollPositionRestoration: 'enabled',
anchorScrolling: 'enabled',
scrollOffset: [0, 0],
})
]
})
1、scrollPositionRestoration 可能的选项有:
- disabled:什么也不做(当前默认);
- top:导航后自动回到页面顶部;
- enabled:(后退时)恢复到原始位置或(前进时)基于 anchorScrolling 选项的元素位置(否则置顶);
2、anchorScrolling 可能的选项有:
- disabled:什么也不做(当前默认);
- enabled:跳转到当前 Fragment 对应元素的位置(如果有的话);
3、scrollOffset 用于设定置顶的位置。
参考资料&鸣谢: