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

Rob*_*ith 10 javascript scroll dom-events angular

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

在此输入图像描述

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

在此输入图像描述

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

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

并且该指令应该停止将scroll事件传播到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();
        })
    }

}
Run Code Online (Sandbox Code Playgroud)

它实际上会监听事件但不会阻止传播.

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

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

谢谢

更新:基于GünterZö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)
Run Code Online (Sandbox Code Playgroud)

然而,逻辑是丑陋的,并不是很好.例如,当用户到达底部时,向上滚动一点并再次向下滚动,父组件稍微移动.有谁知道如何处理这个?(更好)实施?

更新2:

这好多了,但现在已经很晚了,所以我明天再来看看.

Wil*_*m B 6

在我看来,我会建议一些更简单的方法:将任意类添加到任意父级,并防止通过CSS滚动overflow: hidden.

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

HTML:

<div add-class="noscroll" to="body">Some Content Here</div>
Run Code Online (Sandbox Code Playgroud)

CSS:

.noscroll { overflow: hidden; }
Run Code Online (Sandbox Code Playgroud)

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);
  }
}
Run Code Online (Sandbox Code Playgroud)


Rob*_*ith 5

这是我迄今为止最好的尝试。

renderer.listen(elRef.nativeElement, 'wheel', (e) => {
    let el = elRef.nativeElement;
    let conditions = ((el.scrollTop +  el.offsetHeight >  el.scrollHeight)) && e.deltaY > 0 || el.scrollTop === 0 & e.deltaY < 0;
    if (conditions) {
        e = e || window.event;
        if (e.preventDefault)
            e.preventDefault();
        e.returnValue = false; 
    } 
}, false)
Run Code Online (Sandbox Code Playgroud)

演示

它在 Firefox、Chrome stable 和 beta 中运行良好,但出于某种原因,Chrome dev 的行为略有不同。如果用户在底部附近非常用力地向下滚动(或等效地,在顶部附近向上滚动),则父元素会移动一点。不幸的是,我注意到纽约时报的评论部分也有同样的烦恼。如果您有建议,请告诉我。

我报告了 Chrome dev 中发生的问题,这是迄今为止的回应。


Mih*_*ilo 5

我有类似的问题。我想在打开模态组件时禁用滚动。

这是我解决它的方法:

import { Component, HostListener } from '@angular/core';

@Component({
  selector   : 'app-modal',
  templateUrl: './modal.component.html',
  styleUrls  : ['./modal.component.scss'],
})
export class ModalComponent {
  @HostListener('wheel', ['$event'])
  handleWheelEvent(event) {
    event.preventDefault();
  }

...
Run Code Online (Sandbox Code Playgroud)

希望你觉得它有用。