【问题标题】:Angular4 - Scrolling to anchorAngular4 - 滚动到锚点
【发布时间】:2017-11-10 11:39:22
【问题描述】:

我正在尝试对同一页面上的锚元素进行简单的滚动。基本上,该人单击“尝试”按钮,然后滚动到页面下方的区域,ID 为“登录”。现在,它正在使用基本的id="login"<a href="#login"></a>,但它正在跳转到该部分。理想情况下,我希望它在那里滚动。如果我使用 Angular4,是否有一些内置方法可以做到这一点,或者最简单的方法是什么?谢谢!

整个模板...(组件仍为空)

<div id="image-header">
    <div id="intro">
        <h1 class="text-center">Welcome to ThinkPlan</h1>
        <h3 class="text-center">Planning your way</h3>
        <a class="btn btn-outline-primary" href="#login" id="view">Try It</a> <!-- Where the user clicks to scroll -->
    </div>
</div>
<div class="container" id="info">
    <div class="row">
        <div class="col-md-12">
             <div class="space" id="login"></div> <!-- The place to scroll to -->
                <h1 class="text-center">ThinkPlan</h1>
                <form  class="form-horizontal">
                    <fieldset>
                        <legend>Login</legend>
                        <div class="form-group">
                            <label for="inputEmail" class="col-lg-2 control-label">Email</label>
                            <div class="col-lg-10">
                                <input type="text" class="form-control" id="inputEmail" placeholder="Email">
                            </div>
                        </div>
                        <div class="form-group">
                            <label for="inputPassword" class="col-lg-2 control-label">Password</label>
                            <div class="col-lg-10">
                                <input type="password" class="form-control" id="inputPassword" placeholder="Password"> 
                            </div>
                        </div>
                        <div class="form-group" id="log-btn">
                            <div class="col-lg-10 col-lg-offset-2">
                                <button routerLink="/plan" type="submit" class="btn btn-outline-primary">Login</button>
                            </div>
                        </div>
                        <p class="lead text-center">New here? <a routerLink="signup">Sign up.</a></p>
                    </fieldset>
                </form>
        </div>
    </div>
</div>

【问题讨论】:

    标签: html css angular anchor-scroll


    【解决方案1】:

    对于 Angular 6+,您可以使用:

    1. 在你的路由模块中:

       imports: [RouterModule.forRoot(routes, { anchorScrolling: 'enabled'})]
      
    2. 然后在你的组件html中

       <a (click)="onClick('AnchorId')">Click me!</a>
       <a (click)="onClick('OtherAnchorId')">Click me, too!</a>
       ...
       <div id="AnchorId">...</div>
       ...
       <div id="OtherAnchorId">...</div>
      
    3. 然后在你的组件中 ts

      import { ViewportScroller } from '@angular/common';
      
      @Component({
          selector: 'component-selector',
          templateUrl: './mycomponent.component.html'
      })
      export class MyComponent {
        constructor(private viewportScroller: ViewportScroller) {}
      
        public onClick(elementId: string): void { 
            this.viewportScroller.scrollToAnchor(elementId);
        }
      }
      

    【讨论】:

    • 不错,但您不需要导入:[RouterModule.forRoot(routes, { anchorScrolling: 'enabled'})] viewportScroller 不需要路由器模块作为依赖项
    • 即使 Kim Kern 的回答是正确的,这感觉更像是“Angular 框架”。您还可以添加 css: "html {scroll-behavior: smooth;}" 使其更愉快。
    • 完美,但是滚动事件呢?
    • 这对于页面部分滚动很有用。要在新选项卡中打开 URL 并重定向到特定部分,请检查 @Sarah Dubois 答案。
    【解决方案2】:

    我认为这是更简单的解决方案:

    在您的 HTML 中:

    <a [routerLink]="['/']" fragment="login"></a>
    

    在你的打字稿中:

    ngOnInit() {
     this.route.fragment.subscribe(fragment => { this.fragment = fragment; });
    }
    
    ngAfterViewChecked(): void {
      try {
          if(this.fragment) {
              document.querySelector('#' + this.fragment).scrollIntoView();
          }
      } catch (e) { }
    }
    

    【讨论】:

    • 这会重定向到根路由。要在同一页面位置添加哈希,使用&lt;a routerLink="." fragment="login"&gt;&lt;/a&gt; 效果更好。
    • 这种方法比 'npm install ng2-page-scroll' 更好。但它在没有平滑滚动的情况下工作
    • 你可以像这样平滑滚动:scrollIntoView({behavior: "smooth"})
    • 呃,this.route 是什么??
    • @SarahDubois 这是一个非常棒的解决方案,对我使用 Angular 版本 8 非常有用。五颗星因为它的简单性。 @Phil this.route 是ActivatedRoute。您可能需要这些导入 import { ActivatedRoute, Router, NavigationEnd } from '@angular/router'; 我可以发布更多详细信息的答案。
    【解决方案3】:

    自动滚动

    从 Angular v6 开始,RouterModule 有了新的 anchorScrolling 选项。您可以在模块的导入中设置它:

    imports: [
      ...,
      RouterModule.forRoot(routes, {anchorScrolling: 'enabled'})
    

    这样,Angular 将自动滚动到具有给定片段 id 的元素。

    ⚠️ 但是,这在异步加载数据时不起作用,请参阅此Github issue。 ⚠️


    手动滚动

    Smooth scrolling 尚无法通过RouterModule 实现,但您可以手动完成:

    1) 获取目标,例如ViewChild:

    @ViewChild('pageInfo') pageInfo: ElementRef; 
    

    2) 在nativeElement 上致电scrollIntoView

    const targetElement = this.pageInfo.nativeElement
    targetElement.scrollIntoView({behavior: "smooth"})
    

    【讨论】:

    • 这是正确的。谢谢。要为其他人添加此内容,请使用 ViewChild 获取目标参考; @ViewChild('pageInfo') elemPageInfo: ElementRef; this.elemPageInfo.nativeElement.scrollIntoView({behavior: 'smooth'});
    • @Ralphonzo 很高兴听到。 :-) 感谢您的精彩补充,我已经编辑了答案。
    • 请详细说明选项2中的“targetElement”是什么。它是可滚动元素?
    • @ShacharHar-Shuv targetElement 是您要滚动到的元素,以便它在浏览器的视口中可见。
    • 链接到关于anchorScrolling的Angular文档here
    【解决方案4】:

    现代单线解决方案:

    export class Component implements AfterViewInit {
    
      constructor(
        private viewportScroller: ViewportScroller
      ) { }
    
      ngAfterViewInit(): void {
        this.route.fragment.pipe(
          first()
        ).subscribe(fragment => this.viewportScroller.scrollToAnchor(fragment));
      }
    }
    

    【讨论】:

      【解决方案5】:

      所以,我只花了大约一个小时使用最新的 Angular 和其他解决方案进行初始导航,但无论我尝试什么,我都无法让锚滚动与导航历史记录一起使用。

      换句话说,当我在浏览器中“返回”或“前进”时,URL 会更新,但我无法让 Angular 滚动到正确的位置。从记录来看,Angular 在后退/前进时似乎实际上并没有确认片段,因此没有触发滚动。

      我受够了,决定订阅 URL 并在找到片段时手动滚动,代码如下:

      ngOnInit() {
        this.router.events.subscribe(val => {
          if (val instanceof NavigationEnd) {
            let fragmentIdx = val.urlAfterRedirects.lastIndexOf('#');
            if (fragmentIdx >= 0 && fragmentIdx < val.urlAfterRedirects.length - 1) {
              let fragment = val.urlAfterRedirects.substring(fragmentIdx+1);
              console.log('fragment: ' + fragment);
              document.getElementById(fragment).scrollIntoView();
            }
          }
        })
      }
      

      为了清楚起见,您必须为要滚动到的目标元素指定 id

      另外,我的链接如下所示:

      <a routerLink="." fragment="anchor-name">Scroll To Anchor</a>
      

      现在,我可以访问不同的锚点,并在我回溯/前进历史时查看滚动。希望它可以帮助某人!

      【讨论】:

        【解决方案6】:

        在 Angular 4 中,使用锚点到同一页面可能会遇到一些问题。

        我用这种方法解决了

        ng2-page-scroll

        安装

        npm install ng2-page-scroll --save
        

        在你的 app.module.ts 中导入

        import {Ng2PageScrollModule} from 'ng2-page-scroll';
        
        @NgModule({
            imports: [
                /* Other imports here */
                Ng2PageScrollModule
                ]
        })
        export class AppModule {
        }
        

        在你的 html 组件中测试它

        <a pageScroll href="#test">Testing</a>
        <div id="test">
        

        【讨论】:

        • 感谢您的全面回答,我试过了,但 npm 模块的对等依赖项已过时,具有最新的 Angular 版本。
        • 它不起作用。在角度为 6 的列表中。单击后我的页面正在重新加载
        【解决方案7】:

        AfterViewCheck() 会在每次触发ChangeDetection 时被调用,所以这是一个不好的解决方案。

        使用AfterViewInit()点赞

        public ngAfterViewInit(): void {
          this.subscription = this.route.fragment
            .subscribe(fragment => {
                const targetElement = this.document.querySelector('#' + fragment);
                if (fragment && targetElement) {
                    targetElement.scrollIntoView();
                } else {
                    window.scrollTo(0, 0);
                }
            });
        }
        public ngOnDestroy(): void {
            this.subscription.unsubscribe();
        }
        

        【讨论】:

        • 为什么我们需要在 ngOnDestroy 中手动取消订阅?我认为 Angular 会自动执行它,因为它是一个路由订阅。
        【解决方案8】:

        最后,在 Angular 7 中为我工作:

        HTML:

        <h1 id='here'>Here</h1>
        

        组件:

          ngAfterViewInit() {
            this.route.fragment.subscribe(fragment => {
              this.fragment = fragment;
              setTimeout(() => this.scrollToAnchor(), 10);
            });
          }
        
          scrollToAnchor(): void {
            try {
              if (this.fragment) {
                document.querySelector('#' + this.fragment).scrollIntoView();
              }
            } catch (e) { }
          }
          goToHere():void{
            this.router.navigate(['/product'], { fragment: 'here' });
          }
        

        【讨论】:

          【解决方案9】:

          这是一个更详细的示例,可以添加到上述 Sarah Dubois 的答案中。

          导入路由器模块。

          import { ActivatedRoute, Router, NavigationEnd } from '@angular/router';

          constructor(private route:ActivatedRoute, 
                      private router:Router) {
          
            router.events.subscribe(event => {
              if (event instanceof NavigationEnd) {    
                if (event.url) {
                  this.route.fragment.subscribe(fragment => this.fragment = fragment);
                }
              }
            });
          
          }
          
          ngAfterViewChecked(): void {
          
            try {
              if(this.fragment != null) {
               document.querySelector("a[name="+this.fragment+"]").scrollIntoView();
              }
            } catch (e) {
              //console.log(e, 'error');
            }
          
          }
          

          我喜欢使用 querySelector,它会给你很多选项来选择你想要滚动到的元素。 https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector

          这适用于 Angular 8 :)

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-08-06
            • 1970-01-01
            • 1970-01-01
            • 2020-10-17
            • 1970-01-01
            相关资源
            最近更新 更多