A smart Vue autofocus directive
This directive, v-autofocus
, tries to be smart in the following ways:
- When placed on a non-focusable element (such as a
<div>
) or a Vue component, it will focus on the first focusable descendant. Descendants are scanned in document order. - It can focus on
contenteditable
elements, too. - Focusable candidates can be further restricted by a CSS selector.
- This allows
v-autofocus
to work with opaque Vue input components such as the Vue Material Datepicker, Vue Material Chips and Vue Material Autocomplete. -
v-autofocus
can act with some delay in order to be compatible with container components that manipulate the focus after their children have been inserted (e.g. the Vue Material Dialog), - The focus can also be set in response to child events, e.g. when a dialog is (re-)opened.
- The directive can be disabled.
A simple example is available here (example source code).
Please note: in this context, an element is considered "focusable" if it can become the
document.activeElement
.
This includes contenteditable
elements.
Focusable elements become non-focusable only if hidden or having attribute disabled
.
Elements with any integer tabindex
are at least click focusable.
Installation
As a module:
$ npm install @undecaf/vue-autofocus
or
$ yarn add @undecaf/vue-autofocus
Included as <script>
:
<script src="https://cdn.jsdelivr.net/npm/@undecaf/vue-autofocus/dist/directives.min.js"></script>
Usage
Registering the directive
import autofocus from 'vue-autofocus'
Vue.use(autofocus)
Configuration
v-autofocus
expects a configuration object, a primitive value as a single option (see below), or no
value at all. Unspecified options get default values.
The configuration object supports the following properties:
Name | Type | Description | Default |
---|---|---|---|
enabled |
Boolean |
Enables the directive if truthy. | true |
selector |
String |
Only an element matching this selector can receive the focus, starting with the element on which this directive is placed. | '*' |
on |
String or Array<String>
|
Child event(s) that re-trigger auto-focusing. | [] |
delay |
Number |
Delay (in ms) until the focus is set. A value of 0 sets the focus synchronously with the trigger event. |
50 |
If a value is specified that is not an object then its type determines which option it applies to:
Boolean
→ enabled
, String
→ selector
, Array
→ on
, Number
→ delay
.
The configuration can be modified after binding; changes to on
take effect immediately, all other changes become noticeable only after a child event
(e.g. 'hook:updated'
or
'md-opened'
).
Examples
A simple use case:
<input type="text" v-autofocus>
Conditional autofocus:
<input type="text" v-autofocus="{ enabled: active }"> <!-- or: autofocus="Boolean(active)" -->
Focusing on the first focusable descendant:
<div v-autofocus>
<!-- These are not focusable -->
<div><span>Not focusable</span></div>
<img src="#">
<a></a>
<input type="hidden">
<input type="text" disabled>
<div>
<!-- First focusable descendant -->
<textarea v-model="comment"></textarea>
</div>
</div>
Focusing on the first focusable descendant that matches a selector:
<div autofocus="{ selector: '.focus-me' }"> <!-- or: v-autofocus="'.focus-me'" -->
<!-- Focusable but will not receive focus -->
<textarea v-model="comment"></textarea>
<!-- Will receive focus -->
<input type="text" class="focus-me" v-model="text">
</div>
Auto-focusing on the input inside a Vue Material Datepicker:
<md-datepicker v-autofocus v-model="birthdate" :md-open-on-focus="false" />
Setting the focus on the first input of a Vue Material Dialog whenever the dialog is (re-)opened (a selector is required since the dialog container is focusable):
<md-dialog v-autofocus="{ selector: 'input', on: 'md-opened' }" :md-active="showDialog">
...
</md-dialog>
This will have no effect whatsoever:
<div v-autofocus>
<input type="hidden">
</div>
License
Software: MIT
Documentation: CC-BY-SA 4.0