-
Notifications
You must be signed in to change notification settings - Fork 4.7k
Move event handling to Vue 3 syntax #34
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
d74b8be
Moved events handling up to modifiers
efaa6d3
Moved the rest of event handling
0474c5f
Merge branch 'master' into move-event-handling
NataliaTepluhina fa62ff2
Merge branch 'master' into move-event-handling
NataliaTepluhina 6404403
fix: added missing comma
e188015
fix: fixed order
4a46dc8
fix: uncommented computed
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
<template> | ||
<div id="example-1" class="demo"> | ||
<button v-on:click="counter += 1">Add 1</button> | ||
<p>The button above has been clicked {{ counter }} times.</p> | ||
</div> | ||
</template> | ||
|
||
<script> | ||
export default { | ||
data() { | ||
return { | ||
counter: 1 | ||
} | ||
} | ||
} | ||
</script> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
<template> | ||
<div id="example-2" class="demo"> | ||
<button v-on:click="greet">Greet</button> | ||
</div> | ||
</template> | ||
|
||
<script> | ||
export default { | ||
data() { | ||
return { | ||
name: 'Vue.js' | ||
} | ||
}, | ||
methods: { | ||
greet(event) { | ||
alert('Hello ' + this.name + '!') | ||
if (event) { | ||
alert(event.target.tagName) | ||
} | ||
} | ||
} | ||
} | ||
</script> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
<template> | ||
<div id="example-3" class="demo"> | ||
<button v-on:click="say('hi')">Say hi</button> | ||
<button v-on:click="say('what')">Say what</button> | ||
</div> | ||
</template> | ||
|
||
<script> | ||
export default { | ||
methods: { | ||
say(message) { | ||
alert(message) | ||
} | ||
} | ||
} | ||
</script> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,293 @@ | ||
# Event Handling | ||
|
||
<div class="vueschool"><a href="https://vueschool.io/lessons/vuejs-user-events?friend=vuejs" target="_blank" rel="sponsored noopener" title="Learn how to handle events on Vue School">Learn how to handle events in a free Vue School lesson</a></div> | ||
|
||
## Listening to Events | ||
|
||
We can use the `v-on` directive to listen to DOM events and run some JavaScript when they're triggered. | ||
|
||
For example: | ||
|
||
```html | ||
<div id="example-1"> | ||
<button v-on:click="counter += 1">Add 1</button> | ||
<p>The button above has been clicked {{ counter }} times.</p> | ||
</div> | ||
``` | ||
|
||
```js | ||
Vue.createApp().mount( | ||
{ | ||
data() { | ||
return { | ||
counter: 1 | ||
} | ||
} | ||
}, | ||
'#example-1' | ||
) | ||
``` | ||
|
||
Result: | ||
|
||
<events-1/> | ||
|
||
## Method Event Handlers | ||
|
||
The logic for many event handlers will be more complex though, so keeping your JavaScript in the value of the `v-on` attribute isn't feasible. That's why `v-on` can also accept the name of a method you'd like to call. | ||
|
||
For example: | ||
|
||
```html | ||
<div id="example-2"> | ||
<!-- `greet` is the name of a method defined below --> | ||
<button v-on:click="greet">Greet</button> | ||
</div> | ||
``` | ||
|
||
```js | ||
Vue.createApp().mount( | ||
{ | ||
data() { | ||
return { | ||
name: 'Vue.js' | ||
} | ||
}, | ||
methods: { | ||
greet(event) { | ||
// `this` inside methods points to the Vue instance | ||
alert('Hello ' + this.name + '!') | ||
// `event` is the native DOM event | ||
if (event) { | ||
alert(event.target.tagName) | ||
} | ||
} | ||
} | ||
}, | ||
'#example-2' | ||
) | ||
``` | ||
|
||
Result: | ||
|
||
<events-2/> | ||
|
||
## Methods in Inline Handlers | ||
|
||
Instead of binding directly to a method name, we can also use methods in an inline JavaScript statement: | ||
|
||
```html | ||
<div id="example-3"> | ||
<button v-on:click="say('hi')">Say hi</button> | ||
<button v-on:click="say('what')">Say what</button> | ||
</div> | ||
``` | ||
|
||
```js | ||
Vue.createApp().mount( | ||
{ | ||
methods: { | ||
say(message) { | ||
alert(message) | ||
} | ||
} | ||
}, | ||
'#example-3' | ||
) | ||
``` | ||
|
||
Result: | ||
|
||
<events-3/> | ||
|
||
Sometimes we also need to access the original DOM event in an inline statement handler. You can pass it into a method using the special `$event` variable: | ||
|
||
```html | ||
<button v-on:click="warn('Form cannot be submitted yet.', $event)"> | ||
Submit | ||
</button> | ||
``` | ||
|
||
```js | ||
// ... | ||
methods: { | ||
warn(message, event) { | ||
// now we have access to the native event | ||
if (event) { | ||
event.preventDefault() | ||
} | ||
alert(message) | ||
} | ||
} | ||
``` | ||
|
||
## Event Modifiers | ||
|
||
It is a very common need to call `event.preventDefault()` or `event.stopPropagation()` inside event handlers. Although we can do this easily inside methods, it would be better if the methods can be purely about data logic rather than having to deal with DOM event details. | ||
|
||
To address this problem, Vue provides **event modifiers** for `v-on`. Recall that modifiers are directive postfixes denoted by a dot. | ||
|
||
- `.stop` | ||
- `.prevent` | ||
- `.capture` | ||
- `.self` | ||
- `.once` | ||
- `.passive` | ||
|
||
```html | ||
<!-- the click event's propagation will be stopped --> | ||
<a v-on:click.stop="doThis"></a> | ||
|
||
<!-- the submit event will no longer reload the page --> | ||
<form v-on:submit.prevent="onSubmit"></form> | ||
|
||
<!-- modifiers can be chained --> | ||
<a v-on:click.stop.prevent="doThat"></a> | ||
|
||
<!-- just the modifier --> | ||
<form v-on:submit.prevent></form> | ||
|
||
<!-- use capture mode when adding the event listener --> | ||
<!-- i.e. an event targeting an inner element is handled here before being handled by that element --> | ||
<div v-on:click.capture="doThis">...</div> | ||
|
||
<!-- only trigger handler if event.target is the element itself --> | ||
<!-- i.e. not from a child element --> | ||
<div v-on:click.self="doThat">...</div> | ||
``` | ||
|
||
::: tip | ||
Order matters when using modifiers because the relevant code is generated in the same order. Therefore using `v-on:click.prevent.self` will prevent **all clicks** while `v-on:click.self.prevent` will only prevent clicks on the element itself. | ||
::: | ||
|
||
```html | ||
<!-- the click event will be triggered at most once --> | ||
<a v-on:click.once="doThis"></a> | ||
``` | ||
|
||
Unlike the other modifiers, which are exclusive to native DOM events, the `.once` modifier can also be used on [component events](TODO:components-custom-events.html). If you haven't read about components yet, don't worry about this for now. | ||
|
||
Vue also offers the `.passive` modifier, corresponding to [`addEventListener`'s `passive` option](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Parameters). | ||
|
||
```html | ||
<!-- the scroll event's default behavior (scrolling) will happen --> | ||
<!-- immediately, instead of waiting for `onScroll` to complete --> | ||
<!-- in case it contains `event.preventDefault()` --> | ||
<div v-on:scroll.passive="onScroll">...</div> | ||
``` | ||
|
||
The `.passive` modifier is especially useful for improving performance on mobile devices. | ||
|
||
::: tip | ||
Don't use `.passive` and `.prevent` together, because `.prevent` will be ignored and your browser will probably show you a warning. Remember, `.passive` communicates to the browser that you _don't_ want to prevent the event's default behavior. | ||
::: | ||
|
||
## Key Modifiers | ||
|
||
When listening for keyboard events, we often need to check for specific keys. Vue allows adding key modifiers for `v-on` when listening for key events: | ||
|
||
```html | ||
<!-- only call `vm.submit()` when the `key` is `Enter` --> | ||
<input v-on:keyup.enter="submit" /> | ||
``` | ||
|
||
You can directly use any valid key names exposed via [`KeyboardEvent.key`](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values) as modifiers by converting them to kebab-case. | ||
|
||
```html | ||
<input v-on:keyup.page-down="onPageDown" /> | ||
``` | ||
|
||
In the above example, the handler will only be called if `$event.key` is equal to `'PageDown'`. | ||
|
||
### Key Codes | ||
|
||
::: tip | ||
The use of `keyCode` events [is deprecated](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode) and may not be supported in new browsers. | ||
::: | ||
|
||
Using `keyCode` attributes is also permitted: | ||
|
||
```html | ||
<input v-on:keyup.13="submit" /> | ||
``` | ||
|
||
Vue provides aliases for the most commonly used key codes when necessary for legacy browser support: | ||
|
||
- `.enter` | ||
- `.tab` | ||
- `.delete` (captures both "Delete" and "Backspace" keys) | ||
- `.esc` | ||
- `.space` | ||
- `.up` | ||
- `.down` | ||
- `.left` | ||
- `.right` | ||
|
||
A few keys (`.esc` and all arrow keys) have inconsistent `key` values in IE9, so these built-in aliases should be preferred if you need to support IE9. | ||
|
||
You can also [define custom key modifier aliases](TODO:../api/#keyCodes) via the global `config.keyCodes` object: | ||
|
||
```js | ||
// enable `v-on:keyup.f1` | ||
Vue.config.keyCodes.f1 = 112 | ||
``` | ||
|
||
## System Modifier Keys | ||
|
||
You can use the following modifiers to trigger mouse or keyboard event listeners only when the corresponding modifier key is pressed: | ||
|
||
- `.ctrl` | ||
- `.alt` | ||
- `.shift` | ||
- `.meta` | ||
|
||
::: tip Note | ||
On Macintosh keyboards, meta is the command key (⌘). On Windows keyboards, meta is the Windows key (⊞). On Sun Microsystems keyboards, meta is marked as a solid diamond (◆). On certain keyboards, specifically MIT and Lisp machine keyboards and successors, such as the Knight keyboard, space-cadet keyboard, meta is labeled “META”. On Symbolics keyboards, meta is labeled “META” or “Meta”. | ||
::: | ||
|
||
For example: | ||
|
||
```html | ||
<!-- Alt + C --> | ||
<input v-on:keyup.alt.67="clear" /> | ||
|
||
<!-- Ctrl + Click --> | ||
<div v-on:click.ctrl="doSomething">Do something</div> | ||
``` | ||
|
||
::: tip Tip | ||
Note that modifier keys are different from regular keys and when used with `keyup` events, they have to be pressed when the event is emitted. In other words, `keyup.ctrl` will only trigger if you release a key while holding down `ctrl`. It won't trigger if you release the `ctrl` key alone. If you do want such behaviour, use the `keyCode` for `ctrl` instead: `keyup.17`. | ||
::: | ||
|
||
### `.exact` Modifier | ||
|
||
The `.exact` modifier allows control of the exact combination of system modifiers needed to trigger an event. | ||
|
||
```html | ||
<!-- this will fire even if Alt or Shift is also pressed --> | ||
<button v-on:click.ctrl="onClick">A</button> | ||
|
||
<!-- this will only fire when Ctrl and no other keys are pressed --> | ||
<button v-on:click.ctrl.exact="onCtrlClick">A</button> | ||
|
||
<!-- this will only fire when no system modifiers are pressed --> | ||
<button v-on:click.exact="onClick">A</button> | ||
``` | ||
|
||
### Mouse Button Modifiers | ||
|
||
- `.left` | ||
- `.right` | ||
- `.middle` | ||
|
||
These modifiers restrict the handler to events triggered by a specific mouse button. | ||
|
||
## Why Listeners in HTML? | ||
|
||
You might be concerned that this whole event listening approach violates the good old rules about "separation of concerns". Rest assured - since all Vue handler functions and expressions are strictly bound to the ViewModel that's handling the current view, it won't cause any maintenance difficulty. In fact, there are several benefits in using `v-on`: | ||
|
||
1. It's easier to locate the handler function implementations within your JS code by skimming the HTML template. | ||
|
||
2. Since you don't have to manually attach event listeners in JS, your ViewModel code can be pure logic and DOM-free. This makes it easier to test. | ||
|
||
3. When a ViewModel is destroyed, all event listeners are automatically removed. You don't need to worry about cleaning it up yourself. |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we plan on having a section that defines
ViewModel
? This might be new terminology to some so we may want to link to since I imagine some will be curious what this is.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would Wikipedia link work?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That would work!