We know that Vue can listen to events on your elements and trigger specific functions to run when such events occur. Vue.js also allows you to listen for custom events, an ability which has its most important use case in allowing child components to fire off events that parent components can listen for.
We created a simple photo gallery component in the Vue Template Syntax article. You could click any of the photos in a row of thumbnails and the photo you clicked would be displayed in a large size below. Now, what if we wanted the background of the entire page to be set to the average color of the photo being displayed? We could call this something like theater mode.
The power of custom events is that we can fairly easily do that. The parent component of the photo gallery, App.vue
, simply needs to receive the average RGB value of the photo from its child component, PhotoGallery.vue
, when the photo is clicked.
Let’s get started. This tutorial picks up where the Template Syntax tutorial left off.
Let’s use the same setup from this previous post.
We’re going to use an npm
library called fast-average-color
to get the average color value for a particular photo. Import it at the top of the <script>
section of PhotoGallery.vue
.
import from 'fast-average-color';
Our PhotoGallery.vue
component, currently, has a method, highlight()
, which is triggered when you click on one of the photos.
highlight() {
event.target.id = "theater";
this.theatrical = event.target.src;
let eventIterator = event.target.parentNode;
while (eventIterator.previousElementSibling != null) {
eventIterator.previousElementSibling.getElementsByTagName('img')[0].id = "";
eventIterator = eventIterator.previousElementSibling;
}
eventIterator = event.target.parentNode;
while (eventIterator.nextElementSibling != null) {
eventIterator.nextElementSibling.getElementsByTagName('img')[0].id = "";
eventIterator = eventIterator.nextElementSibling;
}
}
This method displays the clicked photo in a larger size below the thumbnails. Notice that we used event.target
all over the method. event.target
is the element that was clicked on. This is the image that we want to get the average color of.
By perusing the fast-average-color
docs, we can find the getColorAsync
function, which returns a color object where color.rgba
is the average RGB value. Let’s go ahead and make that function call.
const fac = new FastAverageColor();
fac.getColorAsync(event.target)
.then(function(color) {
})
.catch(function(e) {
console.log(e);
});
We’re not yet doing anything with color
. Ultimately, we need to set the background color to color.rgba
in App.vue
. App.vue
is going to have a method that takes color.rgba
as an argument. Why don’t we just go ahead and write that method?
methods: {
setBackground(rgba) {
document.querySelector('body').style.backgroundColor = rgba;
}
}
That should look good to you!
Take a look at the template section of App.vue
. Here’s how it looks right now:
<template>
<div id="app">
<PhotoGallery />
</div>
</template>
The App
component is going to have to pick up an event from the PhotoGallery
component, a custom event. Say the event was called theater-mode
; when we want to listen for such an event in our component, the syntax is just like for regular events. That is, it would be: v-on:theater-mode
. When theater-mode
occurs we’ll call our setBackground
method.
Now we need to send the value color.rgba
to App.vue
somehow. Go back to PhotoGallery.vue
.
Every Vue component has a method $emit
. This method allows you to trigger an event, in our case one called theater-mode
. We’re going to call this.$emit
inside the then
function from the async call we made to the color library. Let’s jog your memory.
highlight() {
event.target.id = "theater";
this.theatrical = event.target.src;
let eventIterator = event.target.parentNode;
while (eventIterator.previousElementSibling != null) {
eventIterator.previousElementSibling.getElementsByTagName('img')[0].id = "";
eventIterator = eventIterator.previousElementSibling;
}
eventIterator = event.target.parentNode;
while (eventIterator.nextElementSibling != null) {
eventIterator.nextElementSibling.getElementsByTagName('img')[0].id = "";
eventIterator = eventIterator.nextElementSibling;
}
const fac = new FastAverageColor();
fac.getColorAsync(event.target)
.then(function(color) {
})
.catch(function(e) {
console.log(e);
});
}
this.$emit
takes the event name as its first argument and has optional further arguments in which you can pass data. We’ll pass color.rgba
. So our function call is going to look like this.$emit('theater-mode', color.rgba)
. Here’s our new function:
highlight() {
event.target.id = "theater";
this.theatrical = event.target.src;
let eventIterator = event.target.parentNode;
while (eventIterator.previousElementSibling != null) {
eventIterator.previousElementSibling.getElementsByTagName('img')[0].id = "";
eventIterator = eventIterator.previousElementSibling;
}
eventIterator = event.target.parentNode;
while (eventIterator.nextElementSibling != null) {
eventIterator.nextElementSibling.getElementsByTagName('img')[0].id = "";
eventIterator = eventIterator.nextElementSibling;
}
const fac = new FastAverageColor();
fac.getColorAsync(event.target)
.then((color) => this.$emit('theater-mode', color.rgba))
.catch(function(e) {
console.log(e);
});
}
That should look good to you! Let’s look back at App.vue
.
<template>
<div id="app">
<PhotoGallery/>
</div>
</template>
<script>
import PhotoGallery from './components/PhotoGallery.vue'
export default {
name: 'App',
components: {
PhotoGallery
},
methods: {
setBackground(rgba) {
document.querySelector('body').style.backgroundColor = rgba;
}
}
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
We already discussed that listening to the theater-mode
event looks like v-on:theater-mode
. When we listen for a custom event, we can access any data that is passed with it via $event
.
So, we write the following:
<template>
<div id="app">
<PhotoGallery v-on:theater-mode="setBackground($event)"/>
</div>
</template>
You just successfully emitted a custom event, listened to it from a parent component and accessed the value emitted from the event. Check your browser. Your app should be working just as we intended it to. Good job and bon voyage! 🚢
If you’re interested in furthering your knowledge around component communication in Vue, I recommend you give this article a read.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
While we believe that this content benefits our community, we have not yet thoroughly reviewed it. If you have any suggestions for improvements, please let us know by clicking the “report an issue“ button at the bottom of the tutorial.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!
Sign up for Infrastructure as a Newsletter.
Working on improving health and education, reducing inequality, and spurring economic growth? We'd like to help.
Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.