TransWikia.com

Unable to remove event listener from document

Stack Overflow Asked by prawn on December 22, 2021

I have a button. When this button is clicked I do two things

  1. Open up a search menu
  2. Attach an event listener to the document body to listen for close events.

However, I cannot seem to be able to remove the eventlistener from the document on the close function. That is, the second time I try to open the menu, it immediately calls the close function

My question is…

How do I remove the document event listener?
And how do I make it so that if the user clicks the search menu, it does not trigger the document click event

openDesktopSearchMenu() {
    this.$desktopSearchMenu.style.height = '330px';
    document.addEventListener('click', this.closeDesktopSearchMenu.bind(this), true);
}

closeDesktopSearchMenu() {
    this.$desktopSearchMenu.style.height = '0px';
    document.removeEventListener('click', this.closeDesktopSearchMenu.bind(this), true);
}

Update July 24
Nick’s answer definitely put me in the right direction. However, the document was always being called first due to the capture parameter. So if the user clicks inside the search menu, it’s automatically closed.

Removing the capture parameter causes the close function to be invoked immediately after it opens.

The way around this that worked for me is to wrap the listener inside a timeout when I add it. And then naturally I had to call stopPropagation() on search menu click

searchMenuClick = (e) => {
    e.stopPropagation();
}

/** open the desktop search menu */
openDesktopSearchMenu = () => {
    this.$desktopSearchMenu.style.height = '330px';
    this.$navBar.classList.add('search');
    setTimeout(() => {
        document.addEventListener('click', this.closeDesktopSearchMenu, { capture: false });
    });
}

closeDesktopSearchMenu = () => {
    this.$desktopSearchMenu.style.height = '0px';
    setTimeout(() => {
        this.$navBar.classList.remove('search');
    }, 300);
    document.removeEventListener('click', this.closeDesktopSearchMenu, { capture: false });
}

One Answer

The .bind() method returns a new function, so the function which you're adding as the callback to addEventListener is a different reference to the one you're trying to remove. As a result, the event listener doesn't get removed.

You could consider binding in your constructor like so:

constructor() {
  ...
  this.closeDesktopSearchMenu = this.closeDesktopSearchMenu.bind(this);
  ...
}

And then use your method like so (without the bind, as that's now done in the constructor):

openDesktopSearchMenu() {
    this.$desktopSearchMenu.style.height = '330px';
    document.addEventListener('click', this.closeDesktopSearchMenu, true);
}

closeDesktopSearchMenu() {
    this.$desktopSearchMenu.style.height = '0px';
    document.removeEventListener('click', this.closeDesktopSearchMen, true);
}

See example snippet below:

class Test {
  constructor() {
    this.prop = "bar";
    this.foo = this.foo.bind(this);
  }
  
  foo() {
    console.log('Foo', this.prop);
  }
  
  a() {
    document.addEventListener('click', this.foo, true);
  }
  
  b() {
    document.removeEventListener('click', this.foo, true);
  }
}

const test = new Test();
console.log("Event listener added");
test.a();

setTimeout(() => {
  console.log("Event listener removed");
  test.b();
}, 3000);

Answered by Nick Parsons on December 22, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP