You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
In addition to the default set of directives shipped in core (like `v-model` or `v-show`), Vue also allows you to register your own custom directives. Note that in Vue, the primary form of code reuse and abstraction is components - however, there may be cases where you need some low-level DOM access on plain elements, and this is where custom directives would still be useful. An example would be focusing on an input element, like this one:
11
+
In addition to the default set of directives shipped in core (like `v-model` or `v-show`), Vue also allows you to register your own custom directives.
We have introduced two forms of code reuse in Vue: [components](/guide/essentials/component-basics.html) and [composables](./composables). Components are the main building blocks, while composables are focused on reusing stateful logic. Custom directives, on the other hand, are mainly intended for reusing logic that involves low-level DOM access on plain elements.
8
14
9
-
When the page loads, that element gains focus (note: `autofocus` doesn't work on mobile Safari). In fact, if you haven't clicked on anything else since visiting this page, the input above should be focused now. Also, you can click on the `Rerun` button and input will be focused.
15
+
A custom directive is defined as an object containing lifecycle hooks similar to those of a component. The hooks receive the element the directive is bound to. Here is an example of a directive that focuses an input when the element is inserted into the DOM by Vue:
10
16
11
-
Now let's build the directive that accomplishes this:
17
+
<divclass="composition-api">
12
18
13
-
```js
14
-
constapp=Vue.createApp({})
15
-
// Register a global custom directive called `v-focus`
16
-
app.directive('focus', {
17
-
// When the bound element is mounted into the DOM...
18
-
mounted(el) {
19
-
// Focus the element
20
-
el.focus()
21
-
}
22
-
})
19
+
```vue
20
+
<script setup>
21
+
// enables v-focus in templates
22
+
const vFocus = {
23
+
mounted: (el) => el.focus()
24
+
}
25
+
</script>
26
+
27
+
<template>
28
+
<input v-focus />
29
+
</template>
23
30
```
24
31
25
-
If you want to register a directive locally instead, components also accept a `directives` option:
32
+
</div>
33
+
34
+
<divclass="options-api">
26
35
27
36
```js
28
-
directives: {
29
-
focus: {
30
-
// directive definition
31
-
mounted(el) {
32
-
el.focus()
33
-
}
37
+
constfocus= {
38
+
mounted: (el) =>el.focus()
39
+
}
40
+
41
+
exportdefault {
42
+
directives: {
43
+
// enables v-focus in template
44
+
focus
34
45
}
35
46
}
36
47
```
37
48
38
-
Then in a template, you can use the new `v-focus` attribute on any element, like this:
39
-
40
49
```vue-html
41
50
<input v-focus />
42
51
```
43
52
44
-
## Directive Hooks
45
-
46
-
A directive definition object can provide several hook functions (all optional):
47
-
48
-
-`created`: called before the bound element's attributes or event listeners are applied. This is useful in cases where the directive needs to attach event listeners that must be called before normal `v-on` event listeners.
49
-
50
-
-`beforeMount`: called when the directive is first bound to the element and before parent component is mounted.
51
-
52
-
-`mounted`: called when the bound element's parent component is mounted.
53
+
</div>
53
54
54
-
-`beforeUpdate`: called before the containing component's VNode is updated
55
+
<divclass="demo">
56
+
<inputv-focusplaceholder="This should be focused" />
57
+
</div>
55
58
56
-
:::tip Note
57
-
We'll cover VNodes in more detail [later](/guide/advanced/render-function.html#the-virtual-dom-tree), when we discuss render functions.
58
-
:::
59
+
Assuming you haven't clicked elsewhere on the page, the input above should be auto-focused. This directive is more useful than the `autofocus` attribute because it works not just on page load - it also works when the element is dynamically inserted by Vue.
59
60
60
-
-`updated`: called after the containing component's VNode **and the VNodes of its children** have updated.
61
+
<divclass="composition-api">
61
62
62
-
-`beforeUnmount`: called before the bound element's parent component is unmounted
63
+
In `<script setup>`, any camelCase variable that starts with the `v` prefix can be used as a custom directive. In the example above, `vFocus` can be used in the template as `v-focus`.
63
64
64
-
-`unmounted`: called only once, when the directive is unbound from the element and the parent component is unmounted.
65
+
If not using `<script setup>`, custom directives can be registered using the `directives` option:
65
66
66
-
You can check the arguments passed into these hooks (i.e. `el`, `binding`, `vnode`, and `prevVnode`) in [Custom Directive API](/api/application.html#app-directive)
67
+
```js
68
+
exportdefault {
69
+
setup() {
70
+
/*...*/
71
+
},
72
+
directives: {
73
+
// enables v-focus in template
74
+
focus: {
75
+
/* ... */
76
+
}
77
+
}
78
+
}
79
+
```
67
80
68
-
### Dynamic Directive Arguments
81
+
</div>
69
82
70
-
Directive arguments can be dynamic. For example, in `v-mydirective:[argument]="value"`, the `argument` can be updated based on data properties in our component instance! This makes our custom directives flexible for use throughout our application.
83
+
<divclass="options-api">
71
84
72
-
Let's say you want to make a custom directive that allows you to pin elements to your page using fixed positioning. We could create a custom directive where the value updates the vertical positioning in pixels, like this:
85
+
Similar to components, custom directives must be registered so that they can be used in templates. In the example above, we are using local registration via the `directives` option.
73
86
74
-
```vue-html
75
-
<div id="dynamic-arguments-example" class="demo">
76
-
<p>Scroll down the page</p>
77
-
<p v-pin="200">Stick me 200px from the top of the page</p>
78
87
</div>
79
-
```
88
+
89
+
It is also common to globally register custom directives at the app level:
80
90
81
91
```js
82
-
constapp=Vue.createApp({})
92
+
constapp=createApp({})
83
93
84
-
app.directive('pin', {
85
-
mounted(el, binding) {
86
-
el.style.position='fixed'
87
-
// binding.value is the value we pass to directive - in this case, it's 200
88
-
el.style.top=binding.value+'px'
89
-
}
94
+
// make v-focus usable in all components
95
+
app.directive('focus', {
96
+
/* ... */
90
97
})
91
-
92
-
app.mount('#dynamic-arguments-example')
93
98
```
94
99
95
-
This would pin the element 200px from the top of the page. But what happens if we run into a scenario when we need to pin the element from the left, instead of the top? Here's where a dynamic argument that can be updated per component instance comes in very handy:
100
+
:::tip
101
+
Custom directives should only be used when the desired functionality can only be achieved via direct DOM manipulation. Prefer declarative templating using built-in directives such as `v-bind` when possible because they are more efficient and server-rendering-friendly.
102
+
:::
103
+
104
+
## Directive Hooks
96
105
97
-
```vue-html
98
-
<div id="dynamicexample">
99
-
<h3>Scroll down inside this section ↓</h3>
100
-
<p v-pin:[direction]="200">I am pinned onto the page at 200px to the left.</p>
101
-
</div>
102
-
```
106
+
A directive definition object can provide several hook functions (all optional):
103
107
104
108
```js
105
-
constapp=Vue.createApp({
106
-
data() {
107
-
return {
108
-
direction:'right'
109
-
}
109
+
constmyDir= {
110
+
// called before bound element's attributes
111
+
// or event listeners are applied
112
+
created(elbinding, vnode, prevVnode) {
113
+
// see below for details on arguments
114
+
},
115
+
// called right before the element is inserted into the DOM.
116
+
beforeMount() {},
117
+
// called when the bound element's parent component
118
+
// and all its children are mounted.
119
+
mounted() {},
120
+
// called before the parent component is updated
121
+
beforeUpdate() {},
122
+
// called after the parent component and
123
+
// all of its children have updated
124
+
updated() {},
125
+
// called before the parent component is unmounted
126
+
beforeUnmount() {},
127
+
// called when the parent component is unmounted
128
+
unmounted() {}
110
129
}
111
-
})
130
+
}
131
+
```
112
132
113
-
app.directive('pin', {
114
-
mounted(el, binding) {
115
-
el.style.position='fixed'
116
-
// binding.arg is an argument we pass to directive
117
-
consts=binding.arg||'top'
118
-
el.style[s] =binding.value+'px'
119
-
}
120
-
})
133
+
### Hook Arguments
121
134
122
-
app.mount('#dynamic-arguments-example')
123
-
```
135
+
Directive hooks are passed these arguments:
124
136
125
-
Result:
137
+
-`el`: the element the directive is bound to. This can be used to directly manipulate the DOM.
-`binding`: an object containing the following properties.
140
+
-`value`: The value passed to the directive. For example in `v-my-directive="1 + 1"`, the value would be `2`.
141
+
-`oldValue`: The previous value, only available in `beforeUpdate` and `updated`. It is available whether or not the value has changed.
142
+
-`arg`: The argument passed to the directive, if any. For example in `v-my-directive:foo`, the arg would be `"foo"`.
143
+
-`modifiers`: An object containing modifiers, if any. For example in `v-my-directive.foo.bar`, the modifiers object would be `{ foo: true, bar: true }`.
144
+
-`instance`: The instance of the component where directive is used.
145
+
-`dir`: the directive definition object.
128
146
129
-
Our custom directive is now flexible enough to support a few different use cases. To make it even more dynamic, we can also allow to modify a bound value. Let's create an additional property `pinPadding` and bind it to the `<input type="range">`
147
+
-`vnode`: the underlying VNode representing the bound element.
148
+
-`prevNode`: the VNode representing the bound element from the previous render. Only available in the `beforeUpdate` and `updated` hooks.
Apart from `el`, you should treat these arguments as read-only and never modify them. If you need to share information across hooks, it is recommended to do so through element's [dataset](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dataset).
177
+
:::
169
178
170
179
## Function Shorthand
171
180
172
-
In previous example, you may want the same behavior on `mounted` and `updated`, but don't care about the other hooks. You can do it by passing the callback to directive:
181
+
It's common for a custom directive to need the same behavior for `mounted` and `updated`, and don't care about the other hooks. In such cases we can define the directive as a function:
182
+
183
+
```vue-html
184
+
<div v-color="color"></div>
185
+
```
173
186
174
187
```js
175
-
app.directive('pin', (el, binding) => {
176
-
el.style.position='fixed'
177
-
consts=binding.arg||'top'
178
-
el.style[s] =binding.value+'px'
188
+
app.directive('color', (el, binding) => {
189
+
// this will be called for both `mounted` and `updated`
When used on components, custom directive will always apply to component's root node, similarly to [non-prop attributes](/guide/components/attrs.html).
211
+
When used on components, custom directive will always apply to component's root node, similarly to [Fallthrough Attributes](/guide/components/attrs.html).
200
212
201
213
```vue-html
202
-
<my-component v-demo="test"></my-component>
214
+
<MyComponent v-demo="test" />
203
215
```
204
216
205
-
```js
206
-
app.component('my-component', {
207
-
template:`
208
-
<div> // v-demo directive will be applied here
209
-
<span>My component content</span>
210
-
</div>
211
-
`
212
-
})
213
-
```
217
+
```vue-html
218
+
<!-- template of MyComponent -->
214
219
215
-
Unlike attributes, directives can't be passed to a different element with `v-bind="$attrs"`.
220
+
<div> <!-- v-demo directive will be applied here -->
221
+
<span>My component content</span>
222
+
</div>
223
+
```
216
224
217
-
Note that components can potentially have more than one root nodes. When applied to a multi-root component, directive will be ignored and the warning will be thrown.
225
+
Note that components can potentially have more than one root nodes. When applied to a multi-root component, directive will be ignored and the warning will be thrown. Unlike attributes, directives can't be passed to a different element with `v-bind="$attrs"`. In general, it is **not** recommended to use custom directives on components.
0 commit comments