Skip to content

Instantly share code, notes, and snippets.

@loilo
Last active April 27, 2023 00:21
Show Gist options
  • Star 17 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save loilo/597fa84f4d7e6b7552373f2df9517b45 to your computer and use it in GitHub Desktop.
Save loilo/597fa84f4d7e6b7552373f2df9517b45 to your computer and use it in GitHub Desktop.
Make Vue events bubble

Make Vue events bubble

Vue events don't bubble the component tree on their own. However when writing wrapper components this can be the desired behaviour.

This code registers a global bubble directive which allows to re-emit all given events:

Let's say we want to bubble events start, accelerate and brake of our component Car.

Without any help, we'd roughly have to do this:

<Car
  @start="(...args) => $emit('start', ...args)"
  @accelerate="(...args) => $emit('accelerate', ...args)"
  @brake="(...args) => $emit('brake', ...args)"
  />

However with this directive, we can just do the following:

<Car v-bubble.start.accelerate.brake />

That's it! :)

Vue.directive('bubble', (el, binding, vnode) => {
Object.keys(binding.modifiers).forEach(event => {
// Bubble events of Vue components
if (vnode.componentInstance) {
vnode.componentInstance.$on(event, (...args) => {
vnode.context.$emit(event, ...args)
})
// Bubble events of native DOM elements
} else {
el.addEventListener(event, payload => {
vnode.context.$emit(event, payload)
})
}
})
})
@turigeza
Copy link

turigeza commented Dec 9, 2019

For some reason or an other I can not figure out why I had to remove the events as well ... otherwise events would get added infinite amount of times.
vnode.componentInstance.$off(event);

export default ({ Vue }) => {
    Vue.directive('bubble', (el, binding, vnode) => {
        Object.keys(binding.modifiers).forEach(event => {
            // Bubble events of Vue components
            if (vnode.componentInstance) {
                vnode.componentInstance.$off(event);
                vnode.componentInstance.$on(event, (...args) => {
                    vnode.context.$emit(event, ...args);
                });

            // Bubble events of native DOM elements
            } else {
                el.addEventListener(event, payload => {
                    vnode.context.$emit(event, payload);
                });
            }
        });
    });
};

@loilo
Copy link
Author

loilo commented Dec 9, 2019

Mh. I'm not sure. I'd love to help you with that but for that I'd need a little more context about your component. 🙂

@turigeza
Copy link

turigeza commented Dec 9, 2019

I know there is not enough info here to troubleshoot : ) but I can not reproduce it in a pen ... So I wasn't really expecting any solutions. I just thought I mention it here in case anyone comes across similar issue. Thank you !

@loilo
Copy link
Author

loilo commented Dec 9, 2019

Alright, good luck then and thanks for the heads-up. 😁

@emahuni
Copy link

emahuni commented Feb 23, 2020

The reason there's an infinite insertion of events is because the directive is not using directive hooks. Bind hook makes sure this is done once, only at insertion. https://vuejs.org/v2/guide/custom-directive.html

Vue.directive('bubble', {
 bind: (el, binding, vnode) => {
  Object.keys(binding.modifiers).forEach(event => {
    // Bubble events of Vue components
    if (vnode.componentInstance) {
      vnode.componentInstance.$on(event, (...args) => {
        vnode.context.$emit(event, ...args)
      })

    // Bubble events of native DOM elements
    } else {
      el.addEventListener(event, payload => {
        vnode.context.$emit(event, payload)
      })
    }
  })
 }
})

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment