防止滚动子元素在 Angular 2 中传播

新手上路,请多包涵

这是一个经典。您有一个父元素和一个子元素。子元素是绝对定位的,您希望用户滚动浏览其内容。但是,当您到达子元素的底部时,父元素(也允许有滚动条)开始滚动。这是不可取的。我想重现的基本行为是纽约时报的评论部分。 例如

在此处输入图像描述

允许正文向下滚动,但是当您位于评论部分的底部时,向下滚动不会执行任何操作。我认为这种情况下的主要区别在于,当光标位于 body 元素上时,我想让用户向下滚动。其他方法需要向主体添加一个类以防止主体中出现任何滚动事件。我以为我可以在 Angular 2 中使用一些 Javascript 来做到这一点,但这是我迄今为止失败的尝试:

在此处输入图像描述

我的子组件中有一个自定义指令:

<child-element scroller class="child"></child-element>

并且该指令应该停止将滚动事件传播到 body 元素:

 import {Component} from 'angular2/core'
import {Directive, ElementRef, Renderer} from 'angular2/core';

@Directive({
    selector: '[scroller]',
})
export class ScrollerDirective {
    constructor(private elRef: ElementRef, private renderer: Renderer) {
        renderer.listen(elRef.nativeElement, 'scroll', (event) => {
            console.log('event!');
            event.stopPropagation();
            event.preventDefault();
        })
    }

}

它实际上侦听事件,但不会停止传播。

演示:向下滚动数字列表,当您到达底部时,其父元素开始向下滚动。那就是问题所在。

如果您有其他方法可以实现此目的,请告诉我。

更新:根据 Günter Zöchbauer 提供的答案,我试图防止用户到达底部时发生滚轮事件。这基本上是我到目前为止在这个 更新的演示 中所拥有的:

 renderer.listen(elRef.nativeElement, 'wheel', (e) => {
    console.log('event', e);
    console.log('scrollTop', elRef.nativeElement.scrollTop);
    console.log('lastScrollTop', lastScrollTop);

    if (elRef.nativeElement.scrollTop == lastScrollTop && e.deltaY > 0) {
      e = e || window.event;
      if (e.preventDefault)
          e.preventDefault();
      e.returnValue = false;
    }
    else if (elRef.nativeElement.scrollTop == 0) {
      lastScrollTop = -1;
    }
    else {
      lastScrollTop = elRef.nativeElement.scrollTop;
    }

}, false)

但是,逻辑很丑陋并且效果不佳。例如,当用户到达底部,向上滚动一点然后再次向下滚动时,父组件会轻微移动。有谁知道如何处理这个?一个(好得多)更好的实施?

更新 2

这样 好多了,不过现在晚了,明天再检查一下。

原文由 r_31415 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 332
1 个回答

在我看来,我会提出更直接的建议:将任意类添加到任意父级,并防止通过 CSS overflow: hidden 滚动。

在此示例中,我编写了指令以防止父元素在元素存在时滚动,因为这是我想要的行为。而不是 OnDestroy 和 AfterViewInit,对于您的用例,您应该绑定到 mouseentermouseleave

HTML:

 <div add-class="noscroll" to="body">Some Content Here</div>

CSS:

 .noscroll { overflow: hidden; }

TS:

 import {Directive, AfterViewInit, OnDestroy, Input} from "@angular/core";

@Directive({
  selector: '[add-class]'
})
export class AddClassDirective implements AfterViewInit, OnDestroy {
  @Input('add-class')
  className: string;

  @Input('to')
  selector: string;

  ngOnDestroy(): void {
    document.querySelector(this.selector).classList.remove(this.className);
  }

  ngAfterViewInit(): void {
    document.querySelector(this.selector).classList.add(this.className);
  }
}

原文由 William B 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题