Vue.js 元素 Ui - 当 el-dialog 打开时陷阱/锁定焦点
Vue.js Element Ui - trap / lock focus when el-dialog is open
如您所见 here,当您打开任何对话框示例并开始按 Tab 键时,焦点开始突出显示属于 outside/behind 对话框的元素。我想知道 element-ui 是否提供了我没有注意到的东西?
无论如何,现在我正在使用 vue-focus-lock,但我希望 element-ui 提供原生的东西,而不是拉一个库来解决这个问题。
这似乎是一个错误,element-ui 尚未修复。如果你想要一个纯粹的本地解决方案,这里有一个想法:你可以在 Tab
键上监听 keydown
事件。在这种情况下,您可以访问对话框中的最后一个元素 (document.activeElement
) 并防止进一步按 Tab 键。如果这样做,请不要忘记在对话框关闭时删除事件侦听器。希望这有帮助。
这是我自己的问题的答案:
首先,我尝试使用 vue-focus-lock
插件,它可以工作,但在选择 el-checkbox
或 el-radio
等组件时也有问题。当 el-dialog
的 outside/behind 内容更多(低于折叠)时,您会看到该问题,然后将不会选中复选框或单选按钮。
无论如何这是我的解决方案,我创建了一个组件 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>
我希望该组件在全球范围内可用,因此将其导入 main.js
或者在您的情况下将其导入到您需要的任何地方。
import FocusLock from '@/components/partials/FocusLock.vue';
Vue.component('FocusLock', FocusLock);
并在我需要的任何地方使用它们,就像这样:
<template lang="pug">
focus-lock
el-dialog(:visible.sync="dialogVisible")
el-form(:model="editedItem")
// form elements goes here
</template>
从 HERE 那里得到了一些帮助。
此外,我认为您不必担心处理 this.$el.removeEventListener
,这是由 vue.js 处理的,您可以通过在浏览器控制台中粘贴以下代码进行测试,然后 运行当 el-dialog
打开时,运行 再次关闭时。
Array.from(document.querySelectorAll('*'))
.reduce(function(pre, dom){
var clks = getEventListeners(dom).click;
pre += clks ? clks.length || 0 : 0;
return pre
}, 0)
如您所见 here,当您打开任何对话框示例并开始按 Tab 键时,焦点开始突出显示属于 outside/behind 对话框的元素。我想知道 element-ui 是否提供了我没有注意到的东西?
无论如何,现在我正在使用 vue-focus-lock,但我希望 element-ui 提供原生的东西,而不是拉一个库来解决这个问题。
这似乎是一个错误,element-ui 尚未修复。如果你想要一个纯粹的本地解决方案,这里有一个想法:你可以在 Tab
键上监听 keydown
事件。在这种情况下,您可以访问对话框中的最后一个元素 (document.activeElement
) 并防止进一步按 Tab 键。如果这样做,请不要忘记在对话框关闭时删除事件侦听器。希望这有帮助。
这是我自己的问题的答案:
首先,我尝试使用 vue-focus-lock
插件,它可以工作,但在选择 el-checkbox
或 el-radio
等组件时也有问题。当 el-dialog
的 outside/behind 内容更多(低于折叠)时,您会看到该问题,然后将不会选中复选框或单选按钮。
无论如何这是我的解决方案,我创建了一个组件 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>
我希望该组件在全球范围内可用,因此将其导入 main.js
或者在您的情况下将其导入到您需要的任何地方。
import FocusLock from '@/components/partials/FocusLock.vue';
Vue.component('FocusLock', FocusLock);
并在我需要的任何地方使用它们,就像这样:
<template lang="pug">
focus-lock
el-dialog(:visible.sync="dialogVisible")
el-form(:model="editedItem")
// form elements goes here
</template>
从 HERE 那里得到了一些帮助。
此外,我认为您不必担心处理 this.$el.removeEventListener
,这是由 vue.js 处理的,您可以通过在浏览器控制台中粘贴以下代码进行测试,然后 运行当 el-dialog
打开时,运行 再次关闭时。
Array.from(document.querySelectorAll('*'))
.reduce(function(pre, dom){
var clks = getEventListeners(dom).click;
pre += clks ? clks.length || 0 : 0;
return pre
}, 0)