【问题标题】:How to handle window scroll event in Angular 4?如何在 Angular 4 中处理窗口滚动事件?
【发布时间】:2017-11-14 21:48:15
【问题描述】:

我似乎无法捕捉到窗口滚动事件。 在几个网站上我发现了类似这样的代码:

@HostListener("window:scroll", [])
onWindowScroll() {
  console.log("Scrolling!");
}

sn-ps 通常来自版本 2。这在 Angular 4.2.2 中似乎不起作用(现在?)。例如,如果我将“window:scroll”替换为“window:touchmove”,那么 touchmove 事件就会得到很好的处理。

有人知道我错过了什么吗?非常感谢!

【问题讨论】:

    标签: javascript angular typescript


    【解决方案1】:

    可能您的document 没有滚动,但其中的div 可以。如果从document 调用滚动事件,它只会冒泡到window。此外,如果您从document 捕获事件并调用stopPropagation 之类的东西,您将不会在window 中收到该事件。

    如果您想捕获应用程序内的所有滚动事件,这些事件也来自微小的可滚动容器,您必须使用默认的addEventListener 方法,并将useCapture 设置为true

    这将在事件进入DOM 时触发该事件,而不是气泡阶段。不幸的是,坦率地说,一个很大的失误,angular 不提供传递事件侦听器选项的选项,所以你必须使用addEventListener

    export class WindowScrollDirective {
    
        ngOnInit() {
            window.addEventListener('scroll', this.scroll, true); //third parameter
        }
    
        ngOnDestroy() {
            window.removeEventListener('scroll', this.scroll, true);
        }
    
        scroll = (event): void => {
          //handle your scroll here
          //notice the 'odd' function assignment to a class field
          //this is used to be able to remove the event listener
        };
    
    }
    

    现在这还不是全部,因为所有主要浏览器(显然除了 IE 和 Edge)都实现了新的 addEventListener 规范,这使得将对象作为 third parameter 传递成为可能。

    使用此对象,您可以将事件侦听器标记为passive。建议对触发大量时间的事件执行此操作,这可能会干扰 UI 性能,例如滚动事件。要实现这一点,您应该首先检查当前浏览器是否支持此功能。在 mozilla.org 上,他们发布了一个方法 passiveSupported,您可以使用该方法检查浏览器支持。你只能使用这个,当你确定你不会使用event.preventDefault()

    在我向您展示如何做到这一点之前,您可以想到另一个性能功能。为了防止更改检测运行(每次在区域内发生异步事件时都会调用DoCheck。就像事件触发一样),您应该在区域外运行事件侦听器,并且仅在真正需要时才输入它。 Soo,让我们把所有这些东西结合起来:

    export class WindowScrollDirective {
    
        private eventOptions: boolean|{capture?: boolean, passive?: boolean};
    
        constructor(private ngZone: NgZone) {}
    
        ngOnInit() {            
            if (passiveSupported()) { //use the implementation on mozilla
                this.eventOptions = {
                    capture: true,
                    passive: true
                };
            } else {
                this.eventOptions = true;
            }
            this.ngZone.runOutsideAngular(() => {
                window.addEventListener('scroll', this.scroll, <any>this.eventOptions);
            });
        }
    
        ngOnDestroy() {
            window.removeEventListener('scroll', this.scroll, <any>this.eventOptions);
            //unfortunately the compiler doesn't know yet about this object, so cast to any
        }
    
        scroll = (): void => {
            if (somethingMajorHasHappenedTimeToTellAngular) {
               this.ngZone.run(() => {
                   this.tellAngular();
               });
            }
        };   
    }
    

    【讨论】:

    • 非常感谢,这似乎按预期工作!我认为问题确实是文档本身在我的情况下无法滚动。
    • @Robert 我已经用其他信息更新了我的答案:)
    • 第二个代码块中监听器内部的内容是否与拦截事件相关?此外,如何确定滚动的方向?
    • 这正是我在回答中所说的:)
    • @mkb 它并没有过时。它非常有用。它只是还不支持使用原生事件选项。
    【解决方案2】:

    如果你碰巧使用 Angular Material,你可以这样做:

    import { ScrollDispatchModule } from '@angular/cdk/scrolling';
    

    在 Ts 中:

    import { ScrollDispatcher } from '@angular/cdk/scrolling';
    
      constructor(private scrollDispatcher: ScrollDispatcher) {    
        this.scrollDispatcher.scrolled().subscribe(x => console.log('I am scrolling'));
      }
    

    在模板中:

    <div cdkScrollable>
      <div *ngFor="let one of manyToScrollThru">
        {{one}}
      </div>
    </div>
    

    参考:https://material.angular.io/cdk/scrolling/overview

    【讨论】:

    • 非常感谢,这对我来说似乎可以正常工作!
    • 在这种情况下如何获取滚动方向和偏移量。发出的事件似乎是空白的。
    • @SaurabhTiwari - 见:measureScrollOffset in https://material.angular.io/cdk/scrolling/api。可以与 Rxjs pairWise 结合在一个 observable 中以查看您正在滚动的方向。可能有更直接的方法,但这是一个解决方案。
    • 我明白你想提出什么建议。但是scrollDispatcher 不支持measureScrollOffset 方法。它仅在 cdk-scrollable 指令上受支持。不过,我可能会根据你的建议做点什么。谢谢。
    • @SaurabhTiwari measureScrollOffset 是 lambda 值中可用的: subscribe(x => console.log(x.measureScrolloffset('top')));举个例子。
    【解决方案3】:

    我还不能发表评论。 @PierreDuc 你的答案是正确的,除非@Robert 说文档不会滚动。我稍微修改了您的答案以使用侦听器发送的事件,然后监视源元素。

     ngOnInit() {
        window.addEventListener('scroll', this.scrollEvent, true);
      }
    
      ngOnDestroy() {
        window.removeEventListener('scroll', this.scrollEvent, true);
      }
    
      scrollEvent = (event: any): void => {
        const n = event.srcElement.scrollingElement.scrollTop;
      }
    

    【讨论】:

    • 感谢您包含event 参数。没有它,接受的答案有点没用
    • 你知道我如何从函数内访问组件的范围吗?
    【解决方案4】:

    在 Angular 8 中,实现此代码,在我的情况下,它可以正常工作以使用滚动更改导航栏的颜色... 你的模板:

    <div class="level" (scroll)="scrolling($event)"  [ngClass]="{'level-trans': scroll}">
    <!-- your template -->
    </div>
    

    你的 .ts

    export class HomeNavbarComponent implements OnInit {
    
      scroll:boolean=false;
      constructor() { }
    
      ngOnInit() {
        window.addEventListener('scroll', this.scrolling, true)
      }
      scrolling=(s)=>{
        let sc = s.target.scrollingElement.scrollTop;
        console.log();
        if(sc >=100){this.scroll=true}
        else{this.scroll=false}
      }
    

    你的 CSS

    .level{
        width: 100%;
        height: 57px;
        box-shadow: 0 0 5px 0 rgba(0, 0,0,0.7);
        background: transparent;
        display: flex;
        position: fixed;
        top: 0;
        z-index: 5;
        transition: .8s all ease;
    }
    .level-trans{
        background: whitesmoke;
    }
    

    【讨论】:

      【解决方案5】:

      以防万一我正在寻找一个无法滚动的元素上的滚轮动作,因为它没有滚动条......

      所以,我需要的是这样的:

      @HostListener('mousewheel', ['$event']) 
      onMousewheel(event) {
           console.log(event)
      }
      

      【讨论】:

        猜你喜欢
        • 2023-03-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-01-31
        • 1970-01-01
        • 1970-01-01
        • 2014-08-12
        • 2023-03-30
        相关资源
        最近更新 更多