If you want to maintain the @click
handler on the parent element, one solution is to utilize the .self
modifier:
<template>
<div class="hello" @click.self="openAlert">
<div class="hello_title">{{ title }}</div>
<!-- There is another @click handler within the dropdown component -->
<dropdown class="hello_dropdown" :dropdownId="id" />
</div>
</template>
However, keep in mind that with this approach, the event will not trigger on clicks of any child elements. For instance, clicks on the div with the class hello_title
will not trigger the openAlert
handler.
In scenarios where the DOM tree is simple, you can easily navigate this limitation by adding separate handlers to each descendant. In more complex component hierarchies, this may not be an ideal solution.
If a more robust solution is necessary, you could create an independent "Event Bus" Javascript module that can be imported and used by multiple components. This module could serve as a wrapper for a map of strings to handler functions:
const map = {};
export function addEventListener(name, fn) {
if (!map[name]) map[name] = [];
map[name].push(fn);
}
export function emit(name, ...args) {
if (map[name]?.length) {
for (const fn of map[name]) {
fn(...args);
}
}
}
Dropdown components can register their listeners on the event bus and emit appropriate events from a handler with stopped propagation. This eliminates the need to let the event bubble up to the document body. Consequently, the parent div can utilize the standard @click
syntax without modifications, as clicks on the dropdown will be ignored.
<template>
<div class="hello" @click="openAlert">
<div class="hello_title">{{ title }}</div>
<!-- The dropdown component uses a @click.stop handler, delegating to the event bus -->
<dropdown class="hello_dropdown" :dropdownId="id" />
</div>
</template>