Событие прокрутки все еще работает после вызова removeEventListener

#javascript #typescript #lit

Вопрос:

Я работаю с lit-элементом, и у меня есть некоторые места, где нужно добавить прослушиватель прокрутки и запустить некоторые функции, поэтому я решил создать для этого «службу», которая добавит список событий и может использовать функцию в качестве обратного вызова. И во время вызова unsubscribeDomEvents() он должен удалить список событий.

Таким образом, базовая функциональность работает, но removeEventListener-нет. Я все еще могу видеть эту консоль _onScroll() , даже после unsubscribeDomEvents() того, как она была вызвана. Вот что у меня есть сейчас:

 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
}
 

Буду благодарен за любую помощь!

Ответ №1:

Для будущих Гуглеров

Вы правильно избегаете здесь распространенной ошибки:

 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();
}