Reputation: 16513
As you can see here, when you open any dialog example and start pressing the tab key, the focus start highlighting the elements that are outside/behind the dialog. I was wondering if element-ui have something to offer which I missed to notice?
Anyhow, for now I am using vue-focus-lock but I wish something native to be offered by element-ui rather pulling a library to fix this issue.
Upvotes: 2
Views: 3000
Reputation: 16513
Here is answer to my own question:
First of all I tried to use vue-focus-lock
plugin it works but also have issues when selecting the components like el-checkbox
or el-radio
. You can see the issue when the outside/behind content of el-dialog
is more (below fold) then the checkbox or radio will not be checked.
Anyhow here is my solution, I have created a component FocusLock.vue
:
<template lang="pug">
div.dialog-focus-lock
slot
</template>
<script>
export default {
name: 'focus-lock',
data() {
return {
focusableEls: [],
firstFocusableEl: [],
lastFocusableEl: [],
focusedElBeforeOpen: document.activeElement,
};
},
methods: {
handleDialogFocusLock() {
const selectors = '' +
'a[href], ' +
'input:not([disabled]), ' +
'select:not([disabled]), ' +
'textarea:not([disabled]), ' +
'button:not([disabled])';
const getEls = this.$el.querySelectorAll(selectors);
this.focusableEls = Array.prototype.slice.call(getEls);
[this.firstFocusableEl] = this.focusableEls; // get first array item
[this.lastFocusableEl] = this.focusableEls.slice(-1); // get last array item
this.$el.addEventListener('keydown', e => this.handleKeyDown(e));
this.firstFocusableEl.focus();
},
handleBackwardTab(e) {
if (document.activeElement === this.firstFocusableEl) {
e.preventDefault();
this.lastFocusableEl.focus();
}
},
handleForwardTab(e) {
if (document.activeElement === this.lastFocusableEl) {
e.preventDefault();
this.firstFocusableEl.focus();
}
},
handleKeyDown(e) {
const KEY_TAB = 9;
switch (e.keyCode) {
case KEY_TAB:
if (this.focusableEls.length === 1) {
e.preventDefault();
break;
}
if (e.shiftKey) {
this.handleBackwardTab(e);
} else {
this.handleForwardTab(e);
}
break;
default:
break;
}
},
},
mounted() {
this.handleDialogFocusLock();
},
};
</script>
I wanted the component to be available globally, so imported it in main.js
or in your case just import it wherever you need.
import FocusLock from '@/components/partials/FocusLock.vue';
Vue.component('FocusLock', FocusLock);
And using them wherever I need, just like this:
<template lang="pug">
focus-lock
el-dialog(:visible.sync="dialogVisible")
el-form(:model="editedItem")
// form elements goes here
</template>
Took some help from HERE.
Also, I think you should not worry about handling this.$el.removeEventListener
, this is taken care by vue.js, you can test by pasting below code in your browser console and run it when the el-dialog
is open and run again when it's closed.
Array.from(document.querySelectorAll('*'))
.reduce(function(pre, dom){
var clks = getEventListeners(dom).click;
pre += clks ? clks.length || 0 : 0;
return pre
}, 0)
Upvotes: 1
Reputation: 1066
That seems like a bug, which element-ui hasn't fixed yet. If you want a sheer native solution, here's an idea: you can listen for a keydown
event on a Tab
key. In that case, you are able to access the last element (document.activeElement
) in the dialog and prevent further tab presses. If you do that, don't forget to remove event listener when the dialog is closed. Hope this helps.
Upvotes: 2