The useEventListener() composable attaches an event listener to an element, to window, to document or to a component, and takes care of everything around it: it waits for the target to exist, follows a target that changes, removes the listener when your component gets destroyed and re-attaches it when the event name or the listener options change.
Use it wherever a template listener cannot reach: window and document events, an element outside of your template, or an event that must be listened to with capture, passive or once options driven by your own state.
On the server-side, the composable never listens to anything, but the arguments you pass are still evaluated there. Reference window or document through a getter Function (() => window), which only ever runs on the client, instead of passing them directly.
The composable can also be called outside of setup(): in a boot file, a store or a plain module. There is no mount to wait for there, so it starts listening right away (give it an element, window or document rather than a template ref) and nothing stops it by itself: call stopEventListener() when you are done.
Syntax
import { useTemplateRef } from 'vue'
import { useEventListener } from 'quasar'
setup () {
const target = useTemplateRef('target') // an Element or a component
const { stopEventListener } = useEventListener(
target, // or () => window, () => document, any EventTarget
'keydown', // or an Array of event names
(evt) => { // called with the Event
// ...
},
{ // all optional:
capture: true, // addEventListener() option (default: false)
passive: true, // addEventListener() option (default: left to the browser)
once: true, // addEventListener() option (default: false)
disabled: true // pause listening (default: false)
}
)
// ...
}function useEventListener<E extends Event = Event>(
target: MaybeRefOrGetter<
EventTarget | ComponentPublicInstance | null | undefined
>,
event: MaybeRefOrGetter<string | string[]>,
handler: (evt: E) => void,
options?: MaybeRefOrGetter<{
capture?: boolean
passive?: boolean
once?: boolean
disabled?: boolean
}>
): {
stopEventListener: () => void
}The target can be any EventTarget (window, document, an element) or a component instance (standing for its root element), given directly or through a Ref or a getter Function. A template ref is the usual case: the composable starts listening as soon as your component gets mounted and the ref is populated, and a null or undefined target simply means there is nothing to listen to yet. A component rendering a fragment (multiple root nodes) has no root element to listen on.
The event is a single event name or an Array of names (the same handler serves all of them). It can be a Ref or a getter too, in which case the listener follows it.
Unless you set passive, the browser’s own default applies, so a touchstart or wheel listener on window or document stays passive as the browser intends it to be.
stopEventListener() removes the listener for good. You will rarely need it, as the composable stops by itself when the component gets destroyed.
Changing the options while running
The target, the event and the options can be plain values, Refs or getter Functions. Plain values are read once. With Refs or getters, the composable tracks whatever reactive state they read and re-applies them whenever that state changes, so you never call anything to “update” it:
- toggling
disabledremoves and re-attaches the listener - pointing the target to another element (or letting a template ref change through
v-if) moves the listener along - changing the event name or the
capture,passiveoronceoptions re-attaches the listener with the new ones
import { ref } from 'vue'
import { useEventListener } from 'quasar'
setup () {
const listening = ref(true)
useEventListener(() => window, 'keydown', onKeydown, () => ({
disabled: !listening.value
}))
function onKeydown (evt) { /* ... */ }
// ...
}Example
Listening to keydown on the whole document, with the listener paused through a reactive option:
Listening to window events from setup code, without any node in the template (a beforeunload guard for unsaved changes here):
import { ref } from 'vue'
import { useEventListener } from 'quasar'
setup () {
const dirty = ref(false)
useEventListener(() => window, 'beforeunload', evt => {
if (dirty.value) evt.preventDefault()
})
return { dirty }
}For the state that Quasar already tracks for you, reach for the plugins instead: AppVisibility for the page visibility, AppNetwork for the online state, Screen for the viewport size and breakpoints.