Remove listener for window events when unbinding Vue directive lifecycle

I recently encountered a problem concerning event listening in Vue directives. Within my component, I have the following code:

function setHeaderWrapperHeight() { ... }
function scrollEventHandler() { ... }

export default {
  ...
  directives: {
    fox: {
      inserted(el, binding, vnode) {
        setHeaderWrapperHeight(el);
        el.classList.add('header__unfixed');
        window.addEventListener(
          'scroll',
          scrollEventListener.bind(null, el, binding.arg)
        );
        window.addEventListener(
          'resize',
          setHeaderWrapperHeight.bind(null, el)
        );
      },
      unbind(el, binding) {
        console.log('Unbound');
        window.removeEventListener('scroll', scrollEventListener);
        window.removeEventListener('resize', setHeaderWrapperHeight);
      }
    }
  }
  ...
}

Every time I change the router path, this component is re-rendered by assigning the current route path to the :key prop. However, the issue lies in the fact that the event listeners are not being removed or destroyed, causing significant performance problems. How can I properly remove these event listeners?

Answer №1

element, it is explained that calling the bind function on a function generates a new function, which leads to listeners not being removed due to passing a different function to removeEventListener than what was initially passed to addEventListener. While communicating between hooks in directives may pose some challenges, the official suggestion is to utilize the element's dataset, although this may appear clumsy in certain scenarios. Instead, storing listeners directly as properties on the element can make them accessible in the unbind hook. An alternative approach presented involves using an array to keep track of all elements currently bound to the directive. By registering the listener on window once, even if the directive is used multiple times, and removing the listener when the directive is not in use. It is also mentioned that using a directive may not always be necessary and handling things at the component level could simplify matters since the component instance can store information. It is crucial to remember that creating a new function upon calling bind necessitates saving a reference to that function for later removal with removeEventListener.

Answer №2

Just wanted to share some insights for those who may stumble upon this post in the future, even though there is already an answer provided. In the context of Vue 3 (not tested on Vue 2), one approach worth considering is utilizing binding.dir as a reference to the directive's object. This allows you to store and retrieve event listener functions associated with the directive.

As a simple illustration (unrelated to the original topic) of binding a focus event:

export default {
  ...
  directives: {
    fox: {
      handleFocus: () => { /* placeholder code */ },
      mounted(el, binding) {
        binding.dir.handleFocus = () => { /* actual action */ }
        el.addEventListener('focus', binding.dir.handleFocus);
      },
      beforeUnmount(el, binding) {
        el.removeEventListener('focus', binding.dir.handleFocus);
      }
    }
  }
  ...
}

An application example of this concept in use would be creating a focus/blur notifier for input or textarea elements. I have shared a Gist here showcasing how I implemented it in a Vue 3 project using TypeScript.

Answer №3

In this code snippet, I have implemented a solution for adding and removing an event listener on the window to handle resizing events. ChatGPT provided some assistance in optimizing the functionality:

  .component('resizeHandler', {
    beforeMount(el, binding, vnode) {
      const resizeCallback = binding.value;
      window.addEventListener('resize', () => {
        const width = document.documentElement.clientWidth;
        const height = document.documentElement.clientHeight;
        resizeCallback({ width, height });
      }, { passive: true });
      // eslint-disable-next-line no-underscore-dangle, no-param-reassign
      el._resizeListener = binding.value;
    },
    unmounted(el) {
      // eslint-disable-next-line no-underscore-dangle
      window.removeEventListener('resize', el._resizeListener);
    },
  })

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Using jQuery to create a click function that determines the length and pattern of an input

I've been grappling with a problem for the past two days. I have a webpage that prompts users to enter their phone number, and I'm attempting to create a "validator" for the phone number input field. However, I'm struggling to determine how ...

Is it possible to eliminate the default placeholder text from Safari browser?

My goal is to design a form that includes date and time input fields. The placeholder text should move up by X pixels when the user clicks on the field. While the form appears fine in Chrome, there seems to be an issue with overlapping form fields in Safa ...

Having trouble scrolling to the top in Flickr Search using AngularJS, the results are restricting my movement

I recently followed a tutorial on creating a Flickr Search App with AngularJS (https://www.youtube.com/watch?v=lvGAgul5QT4). I managed to show the search results on my page, but encountered an issue: I couldn't scroll all the way back up to the search ...

Trouble with launching Semantic UI modal

I have implemented a button using Semantic UI and set up a click event to open a modal when clicked, but nothing is happening. When I click on the link, there is no response. I'm unsure what the issue might be. Below is the code for the button: < ...

Tips for handling the RETURN value in an AJAX call to a server-side script

Seeking guidance on how to properly validate an email address on the server side using PHP and jQuery. Currently, I am utilizing the "$.post()" function in jQuery for this task, but I am aware that it can also be done with '$.ajax()'. The issue ...

Encountered error code 422 Unprocessable_entity upon submitting form to Rails API via Vue.js application

Currently, I am in the process of developing an application using Rails 6 Api as a backend and Vue as a standalone web app. After successfully setting up my sign-up form, I encountered a frustrating issue upon submission where I received a Completed 422 U ...

Template does not display data from Angular Service even though it has been passed through the service

After setting up an API Backend using Django, I attempted to display the data in an Angular Component to create a list of all retrieved datasets. I managed to run a test in service.spec.ts and confirmed that the data is successfully fetched from the backe ...

Preserve user-inputted text from jQuery within a div container

With the help of the knowledgeable individuals here, I successfully created a prototype to address an issue I had encountered. The problem involved using a textbox input with a password requirement to update an HTML element. Although everything is functio ...

Failed to successfully sort several distinct lists using the ng2-dnd sorting library

Recently, I came across a fantastic drag-and-drop library on GitHub that I decided to use. In my current project, I have created a view with three different buttons, each revealing a list when clicked at the same position but not simultaneously. However, ...

Generating directory for application, only to find TypeScript files instead of JavaScript

While following a tutorial on setting up a react.js + tailwindcss app, I used the command npx create-next-app -e with-tailwindcss [app name]. However, instead of getting javascript files like index.js, I ended up with TypeScript files like index.tsx. You c ...

Tips on deleting a nested JSON key?

Here is an example of my JSON structure: var data = [{ "ID" : 3, "discRec" : "Some sample record", "Tasks" : [{ "ID" : 7, ...

Encountered an error when attempting to load resource: net::ERR_CERT_AUTHORITY_INVALID following deployment on Vercel

I recently deployed a chatUI-app on Vercel that fetches chats from an API located at "http://3.111.128.67/assignment/chat?page=0" While the app worked perfectly in development, I encountered an issue after deploying it on Vercel where it ...

Custom date formatting with jQuery table sorting

I have been using a jQuery plugin called Tablesorter to sort a table. I am facing an issue with sorting dates in the yyyy MMM dd format, especially because my date inputs are in French. Here is an example of how the dates are formatted: 2014 janv. 05 20 ...

vue form is returning empty objects rather than the actual input data

I am attempting to transfer data from my component to a view in order for the view to save the data in a JSON file. I have verified that the view is functioning correctly as I attempted to log the data in the component and found it to be empty, and the r ...

How can I use Braintree and Javascript to automatically fill in transaction data (such as card and address information) for customers who have made previous purchases?

I have implemented Braintree's Hosted Fields and Javascript for signup and payment processing. While I can successfully send a payment nonce, I am facing an issue with preloading user information from the Braintree portal for users who already have su ...

Add the word "String" in front of the moment timestamp

In my project, I have used React along with Material UI to show the message Running check... when the clicked state is set to true. If it's not true, then I am displaying a timestamp by utilizing the react-moment library. <Typography gutterBottom ...

Why does the AngularJS ngRepeat filter permanently remove null values?

Check out this JSFiddle demonstration I created to illustrate the problem: http://jsfiddle.net/s6Lj2/2/ Observe that in the dataset $scope.places = [{ name: 'Chicago', status: 'Active', analyst: 'Sam', recor ...

Is there a method on iOS for identifying when a web page is being displayed in an embedded browser and then navigating to the "actual" browser?

I've encountered an issue with my website's magic login links on mobile devices, particularly on iOS. When a user receives the email in the Gmail app and clicks the link, it opens in the embedded browser instead of the system browser. This means ...

Having trouble with the CSS `not` selector?

Below is the code snippet I experimented with XHTML <div> <span>Span1</span> <span>Span12</span> <span>Span13</span> </div> <div> <span>Span1</span> <span> ...

Issues with Twitter-Bootstrap Modal Functionality

After creating a modal dialogue: <a href="#myModal" role="button" class="btn" data-toggle="modal" id="LaunchDemo">Click here to launch the demo modal</a> <!-- Modal --> <div id="myModal" class="modal hide fade" tabindex="-1" role="di ...