Photo by Anna Kolosyuk on Unsplash

Creating Your First VueJS Custom Directive

Matt Maribojoc
Jan 10 · 6 min read

In VueJS, directives are one of the best ways to directly edit the DOM.

Some examples of directives in VueJS are v-if, v-show, v-bind, and so on. If you’ve worked in VueJS, you are definitely familiar with them.

A VueJS custom directive is, as you may guess, Vue’s way of letting us build additional directives for our projects. They’re a great way to add unique functionality across your project. They allow you to manipulate elements as well as handle reactivity in the DOM.

By the end of this intermediate tutorial, you will know:

  • What a custom directive is
  • Vue directive’s various event hooks
  • How to create your own custom directives

Let’s gooo!

What is a Custom Directive

Before we go into how to implement custom directives, let’s discuss why they’re useful.

Essentially, custom directives allow you to make your project fit your needs. If you use a Vue plugin, you’ll notice that they use custom directives pretty frequently.

For example, in the v-lazy plugin, they use v-lazy to add custom functionality that makes image loading more effective. Using a directive is the best case here because we want to directly edit the DOM.

You may be asking, “ Can’t I just register component options like computed and watchers?

Yes. You can. But while component options are useful for abstraction and code reuse, custom directives are still one of the best ways to directly manipulate DOM elements.

A directive has five hooks

A VueJS directive has 5 hooks that trigger events. Like lifecycle hooks, they observe and then run the method.

  • bind — called once when the directive is bound to an element
  • inserted — when the bound element is inserted into its parent node
  • update — when the element updates (but any children haven’t yet)
  • componentUpdated — after the children have also updated
  • unbind — called once when the directive is unbound from an element

When implementing these hooks, they each have a few arguments that they accept.

  1. el — the directive is bound to this element; gives you access to modify it
  2. binding — an object contain a lot of properties; we’ll go into depth in a second
  3. vnode — the virtual node
  4. oldNode — the previous virtual node (only available in update hooks)

An important note in the VueJS docs for directives is that you should treat these arguments (besides el) as read-only and never modify them.

The Binding Object

The binding object contains several properties that help you actually add functionality to your hooks.

  • name — the name of the directive (no v- prefix)
  • value — the value passed to the directive
  • oldvalue — the previous value of the directive (only available in update hooks)
  • expression — the expression bound to the string (ex. v-direc=”3 * 3″, expression = “3*3”)
  • arg — any arguments passed to the string (ex. v-direc:blue, arg = blue)
  • modifiers — all modifiers passed as object (ex. v-direc.blue.link, modifiers = {blue: true, link: true}

Defining your directive

There are two ways to define your directive.

  1. Globally — which I think is best most of the time
  2. Locally — using the components option called directives

Let’s look into adding them globally.

Inside our main.js file — or wherever your Vue instance is defined — we just have to use the Vue.directive method.

For right now, let’s create a directive that allows us to manipulate the font size of our component. It’ll be called v-size.

Inside main.js, we’ll add some code like this…

Vue.directive("font-size", {
bind: (el, binding, vnode) => {
el.style.fontSize = 24 + 'px';
},
updated: (el, binding, vnode) => {
el.style.fontSize = 24 + 'px';
}
})

Then, inside any component file, let’s just add the following two lines so we can see our component in action! Whenever we declare a directive, we can access it using the prefix, “v-”.

<p>Default Size Text</p>
<p v-font-size>Uses font-size directive</p>

There is another way to define your Vue directive. For shorthand, you could also use this syntax inside main.js.

Vue.directive("font-size", (el, binding, vnode) => {
el.style.fontSize = 24 + 'px';
})

If a function is passed instead of an object, then it will run during the bind and update hooks.

No matter which method you used, the result should look something like this.

We have our first custom directive working! Now, let’s make this a little more advanced.

Passing arguments to your directive

There are a few ways of adding more control over your directive. This can be done through passing additional values, arguments, or modifiers

For our example, let’s say we want better control of the font size in our elements.

Passed Values — for reactive data

The most intuitive way to pass data is to simply give it a value. This allows your directive to responsively update whenever the value changes. This also gives the most flexible control because you can accept any value (any font size).

In your component, the declaration would look like this…

<p v-font-size='64'>Uses font-size directive</p> 
<!-- OR USE A VARIABLE -->
<p v-font-size='fontSize'>Uses font-size directive</p>

And inside your directive, you would need to change your method to use the value from the binding object.

Vue.directive("font-size", (el, binding, vnode) => {
el.style.fontSize = binding.value + 'px';
})

Sending arguments to directives

If you don’t really need any reactivity and just want a way to provide multiple options to your directive. Arguments are a great way to do that.

The code inside our directive goes like this.

Vue.directive("font-size", (el, binding, vnode) => {
console.log(binding + " " + vnode);
var defaultSize = 16;
switch (binding.arg) {
case "small":
defaultSize = 12;
break;
case "large":
defaultSize = 32;
break;
default:
defaultSize = 16;
break;
}
el.style.fontSize = defaultSize + "px";
})

And then in our template.

<p v-font-size:small>SMALL font-size directive</p>
<p v-font-size:medium>MEDIUM font-size directive</p>
<p v-font-size:large>LARGE font-size directive</p>

Using modifiers

Modifiers are similar to arguments in that they’re not really geared for reactivity. But when use in combination with args, you can really create an extremely customized system.

Let’s just implement them in our directive first.

Vue.directive("font-size", (el, binding, vnode) => {
console.log(binding + " " + vnode);
var defaultSize;
if (binding.modifiers.small) {
defaultSize = 12;
} else if (binding.modifiers.large) {
defaultSize = 32;
} else {
defaultSize = 16;
}
el.style.fontSize = defaultSize + "px";
})

And then we can use them in our template

<p v-font-size.small>SMALL font-size directive</p> 
<p v-font-size.medium>MEDIUM font-size directive</p>
<p v-font-size.large>LARGE font-size directive</p>

The output should all three should look something like this.

Conclusion

Congrats!

You’ve made it here and should now have some working knowledge of registering your own VueJS custom directives.

Thanks for following along and I hope that this article gave you the tools you need to build some amazing tools.

Happy Coding!


If you’re looking to power up your Vue development, I’ve created a 3 page PDF cheatsheet with all of the essential references that every Vue developer needs. It’s saved me so much time and thousands of the same Google searches.


Originally published at https://learnvue.co on January 10, 2020.

JavaScript in Plain English

Learn the web's most important programming language.

Matt Maribojoc

Written by

I run a VueJS community over at https://learnvue.co, develop web sites, and post whatever I find cool on the Internet.

JavaScript in Plain English

Learn the web's most important programming language.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade