Event Handler Tips for JavaScript and VueJS

Dan Pastori avatar
Dan Pastori September 13th, 2022

One of the basic features of JavaScript is working with Event Handlers. Event handlers allow you to listen to interactions (click, touchstart), changes (input, change), etc. within your application and respond accordingly. There are hundreds of events you can listen to, check them out here. Here’s a few ways I’ve used event handlers working with JavaScript and VueJS.

Adding Event Handlers in JavaScript

At the most basic level, you add an event handler to an element. This allows you to listen to certain events on that element. Let’s say you have a <button> element with an ID of #click-me and you want to listen when someone clicks it. You’d add the following code:

document.getElementById('click-me').addEventListener( 'click', function(){
    alert("I've been clicked!")
} );

The first parameter is the event you want to listen to. In our case it’s click . The second parameter is the callback function. In this example, we passed just an anonymous function. This is great, but say you wanted to do some more complex processing when the element has been clicked. You might not want the element to be a callback method or your code will get extremely hairy. Instead, you can call a named method like this:

document.getElementById('click-me').addEventListener( 'click', complexClickHandler);
  
function complexClickHandler(){
    alert("I've been clicked");
    alert("Performing complex functions");
}

I definitely prefer this approach since it keeps your code nice and clean. You can expand at will and your code will stay nice and organized.

Adding Event Handlers in VueJS

Within VueJS, you’d add these events using the v-on: syntax, or the shorthand @ notation. To update the above example for VueJS, make the following changes to your code:

<template>
    <button id="click-me" @click="complexClickHandler()">Please click me!</button>
</template>

<script>
    export default {
        methods: {
            complexClickHandler(){
                alert("I've Been Clicked!")
            }
        }
    }
</script>

And with the Composition API:

<template>
    <button id="click-me" @click="complexClickHandler()">Please click me!</button>
</template>

<script setup>
const complexClickHandler = () => {
    alert("I've Been Clicked!")
}
</script>

Awesome. Those are pretty simple examples, but what if you need access to the actual event itself? Check out the next section.

Accessing the Event from a JavaScript Event

Adding events is only half the battle. Working with the event and getting the necessary data from the event is where you can really add some power to your application. Let’s say you want to get the location of a click event or work with the element that’s been targeted.

We do this a lot with AmplitudeJS. There are multiple elements on the page that have different attributes that define the functionality. Say we have 3 play/pause buttons. The first one does not have any attributes, the second one has an attribute that defines the audio we want to play (data-amplitude-audio-index), and the third one defines the collection (data-amplitude-collection-key) and audio (data-amplitude-audio-index). We need to access these to determine how the player should react.

Luckily, the event itself contains a ton of useful information. To access the event, whether it’s a click, input, etc. within your handler, you can name the first parameter event and it will be injected from the handler itself:

<button id="play-pause" data-audio-index="0">Play/Pause</button>
<script>
    document.getElementById('play-pause').addEventListener( 'click', handlePlayPause);
    
    function handlePlayPause(event){
        console.log( event );
    }
</script>

The event parameter is massive! It contains useful information such as the element that the event occurred on [event.target](<http://event.target>) . The location of the event (event.clientX and event.clientY). The type of the event, event.type. Along with a ton of other useful information. Now you can really add some complex functionality to your app!

Accessing the Event within VueJS

Luckily, it’s super easy to access the Event object within VueJS as well! Vue will send the event along as the first parameter to your handler method as well:

<template>
    <button id="click-me" @click="complexClickHandler()">Please click me!</button>
</template>

<script>
    export default {
        methods: {
            complexClickHandler(event){
                console.log( event );
            }
        }
    }
</script>

And with the Composition API:

<template>
    <button id="click-me" @click="complexClickHandler()">Please click me!</button>
</template>

<script setup>
const complexClickHandler = ( event ) => {
    console.log( event );
}
</script>

Let’s touch on a quick gotcha with some more advanced events within VueJS.

Window Events with VueJS

Within VueJS, you can add event handlers to any element scoped within the component using the shorthand. But what about window events and document level events? Well, you can handle these as well, through Vanilla JavaScript. However there are some gotchas.

Let’s say we want to add an event to a page that occurs when the user scrolls. There are 2 steps to accomplish this. I’ll touch on the first part here, and then in the next section we will go over removing the event and discuss why this is important.

First, we need to bind the event in the mounted() or onMounted() lifecycle hook:

<script>
    export default {
        mounted(){
            window.addEventListener('scroll', this.handleScroll);
        },

        methods: {
            handleScroll(){
                console.log('scrolling!');
            }
        }
    }
</script>

or

<script setup>
import { onMounted } from 'vue';

onMounted( () => {
    window.addEventListener('scroll', handleScroll);
})
const handleScroll = ( event ) => {
    console.log('Scrollin!');
}
</script>

Notice that within Vue 2 or if you aren’t using the composition API, you have to reference this. I use these event handlers in situations for pages that need to scroll and load more content, or handle clicks within the entire document. You’d think we are done, but there’s one more step. And that’s to remove an event handler.

Removing an Event Handler from an Element with JavaScript

Sometimes you need to remove an event handler from an element. Maybe you only need to listen for a certain period of time. Or you need to run a re-initialization and don’t want multiple event handlers bound. Either way, you need to be using a named method to remove an event and not a callback method.

Within JavaScript, that looks like:

<script>
    document.getElementById('play-pause').addEventListener( 'click', handlePlayPause);
    
    function handlePlayPause(event){
        console.log( event );
    }

    function removeEventHandlers(){
        document.getElementById('play-pause').removeEventListener('click', handlePlayPause)
    }

</script>

Whenever our removeEventHandlers() method is called, we will remove the event from the element with ID of play-pause. It’s important to pass the name of the event (click) as the first parameter and the named method handlePlayPause as the second. This will remove the event handler from the element. This can be super useful so you don’t end up binding the same event handler multiple times and making an overly complex issue in your codebase.

Removing Event Handlers with VueJS

Now with VueJS, you really won’t have to remove events often. However, when you are looking at the example above, where you add an event handler such as scroll to the window object, situations can get hairy quick. How? Well every time you navigate to the page, the scroll event handler gets re-added to the window object. If you visit 3 times without a page refresh, you will have 3 different event handlers.

In order to remove the event handler, you will have to bind the following code to the appropriate lifecycle hook. For example, in Vue 2, this would be appropriate in the beforeDestroy() lifecycle hook:

<script>
    export default {
        beforeDestroy(){
            window.removeEventListener('scroll', this.handleScroll);
        },

        mounted(){
            window.addEventListener('scroll', this.handleScroll);
        },

        methods: {
            handleScroll(){
                console.log('scrolling!');
            }
        }
    }
</script>

Or in the Composition API, onUnmounted:

<script setup>
import { onMounted, onUnmounted } from 'vue';

onMounted( () => {
    window.addEventListener('scroll', handleScroll);
})

onUnmounted( () => {
    window.removeEventListener('scroll', handleScroll)
} );

const handleScroll = ( event ) => {
    console.log('Scrollin!');
}
</script>

Now we won’t have multiple event handlers bound to an element or object if we navigate without re-loading! Before we wrap up, let’s take a look at JavaScript event handlers in classes.

Working with JavaScript Event Handlers in Classes

In newer versions of JavaScript you can actually write Object Oriented JavaScript code. To me, this still blows my mind! However, you can really make some powerful implementations using these structures. I’ll touch more on OOP JavaScript a little later on, but for now, know you can do public and private methods. If you want a large example, we re-factored AmplitudeJS 6.0 to be 100% Object Oriented and we bind a ton of events within classes!

Let’s say you have JavaScript TableRows class that handles clicks on elements with the class .table-row. You want to handle that click with a private method. Your TableRows class should look like:

export class TableRows{
    static query = '.table-row';
    
    initialize(){
        this.#bindEvents();
    }

    #bindEvents(){
        document.querySelectorAll( TableRows.query ).forEach( (element) => {
            element.addEventListener("click", this.#handleClick );
        } );
    }

    #handleClick(){
        console.log('Table Row Clicked!')
    }
}

And you can initialize the TableRows like this:

const Rows = new TableRows();
Rows.initialize()

Now you will have a click handler bound on each element that has a class .table-row. Super handy and nice and clean with OOP! Now one quick gotcha, see how we use this.#handleClick to call the local, private #handleClick() method? Quick note, methods prefixed with # are private in JavaScript. Well, what if you needed to access the element that it the event occurred on? You could send the event, or you will need to bind the element to the click handler like this:

export class TableRows{
    static query = '.table-row';
    
    initialize(){
        this.#bindEvents();
    }

    #bindEvents(){
        document.querySelectorAll( TableRows.query ).forEach( (element) => {
            element.addEventListener("click", this.#handleClick.bind(this, element) );
        } );
    }

    #handleClick( element ){
        console.log('Table Row Clicked!')
    }
}

Now we can pass the element to our private method handler directly to get more information! Pretty slick!

Conclusion

Event handlers are at the very core of JavaScript and can be very simple, or extremely complex. Hopefully these tips help shine a little light on the possibilities and make them a little more tangible for your use-case.

If you have any questions, feel free to reach out on Twitter (@danpastori). If you like these tips, feel free to sign up for our mailing list to get more tips and tricks to your inbox each month!

Support future content

The Ultimate Guide to Building APIs and Single-Page Applications with Laravel + VueJS + Capacitor book cover.

Psst... any earnings that we make off of our book is being reinvested to bringing you more content. If you like what you read, consider getting our book or get sweet perks by becoming a sponsor.

Gold Sponsor SnapShooter Backups

Written By Dan

Dan Pastori avatar Dan Pastori

Builder, creator, and maker. Dan Pastori is a Laravel certified developer with over 10 years experience in full stack development. When you aren't finding Dan exploring new techniques in programming, catch him at the beach or hiking in the National Parks.

Like this? Subscribe

We’re privacy advocates. We will never spam you and we only want to send you emails that you actually want to receive. One-click unsubscribes are instantly honored.

What topics are you interested in?
Are you interested in our upcoming book?