调用 removeEventListener 后滚动事件仍然有效

Scroll event still working after removeEventListener was called

我正在使用 lit-element,有些地方需要添加滚动侦听器和 运行 一些函数,因此决定为此创建一个 'service',它将添加 EventListener 和可以将函数作为回调。在调用 unsubscribeDomEvents() 时,它应该 removeEventListener。

所以基本功能正常,但 removeEventListener 无效。即使在 unsubscribeDomEvents() 被调用之后,我仍然可以从 _onScroll() 看到这个控制台。这是我现在拥有的:

const SCROLL_DOWN = 'scrollDown';

export class ScrollManager {
  private container: HTMLElement;
  private _callbacks = [];
  private readonly _scrollBind;

  constructor({ container }) {
    this._scrollBind = this._onScroll.bind(this);
    this.container = container;
    this.container.addEventListener('scroll', this._scrollBind);
  }

  onScrollDown(callback): ScrollManager {
    return this._registerCallback(SCROLL_DOWN, callback);
  }

  unsubscribeDomEvents(): void {
    this.container.removeEventListener('scroll', this._scrollBind);
  }

  private _registerCallback(callbackType: string, callback): ScrollManager {
    this._callbacks[callbackType] = callback;
    return this;
  }

  private _isScrollToBottom(container: HTMLElement): boolean {
    return container.scrollHeight - container.scrollTop - container.clientHeight < 1;
  }

  private _onScroll(): void {
    console.log('onscroll');
    if (this._isScrollToBottom(this.container)) {
      this._callbacks[SCROLL_DOWN]();
    }
  }
}

这是我在组件中使用它的方式

stateChanged(state: IState): void {
    ...
    this.isEditMode = state.modes.isEditMode;
    
    if (this.isEditMode) {
      this._scrollManager = new ScrollManager({ container: this.container })
              .onScrollDown(this.onScrollDown.bind(this));
    } else {
      this._scrollManager?.unsubscribeDomEvents();
    }

    this.requestUpdate();
  }

private onScrollDown(): void {
      // some function
}

如有任何帮助,将不胜感激!

致未来的 Google 员工

您在这里正确地避免了一个常见错误:

this._scrollBind = this._onScroll.bind(this);

在保留对原始绑定函数的引用并取消订阅的地方:

this.container.removeEventListener('scroll', this._scrollBind);

否则您将尝试取消订阅另一个功能

你的情况

观察你的 stateChanged 函数,如果它触发不止一次而 isEditMode 是真实的,那么你将实例化 ScrollManager 两次,但取消订阅一次。

我认为这是正确的形式:

stateChanged(state: IState): void {
  ...
  this.isEditMode = state.modes.isEditMode;
  this._scrollManager?.unsubscribeDomEvents();
  if (this.isEditMode) {
    this._scrollManager = new ScrollManager({ container: this._scrolableContainer })
      .onScrollDown(this.onScrollDown.bind(this));
  }
  this.requestUpdate();
}