简体   繁体   English

Vue.js元素Ui - 当el-dialog打开时陷阱/锁定焦点

[英]Vue.js Element Ui - trap / lock focus when el-dialog is open

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. 正如您在此处所看到的,当您打开任何对话框示例并开始按Tab键时,焦点开始突出显示对话框外部/后面的元素。 I was wondering if element-ui have something to offer which I missed to notice? 我想知道element-ui是否有提供的东西,我没注意到?

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. 无论如何,现在我正在使用vue-focus-lock但我希望element-ui提供原生内容,而不是拉一个库来解决这个问题。

That seems like a bug, which element-ui hasn't fixed yet. 这似乎是一个错误,哪个元素-ui还没有修复。 If you want a sheer native solution, here's an idea: you can listen for a keydown event on a Tab key. 如果你想要一个纯粹的原生解决方案,这里有一个想法:你可以在Tab键上监听keydown事件。 In that case, you are able to access the last element ( document.activeElement ) in the dialog and prevent further tab presses. 在这种情况下,您可以访问对话框中的最后一个元素( document.activeElement )并阻止进一步按Tab键。 If you do that, don't forget to remove event listener when the dialog is closed. 如果这样做,请不要忘记在关闭对话框时删除事件侦听器。 Hope this helps. 希望这可以帮助。

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 . 首先,我尝试使用vue-focus-lock插件,但是在选择el-checkboxel-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. el-dialog的外部/后面内容更多(低于折叠)时,您可以看到问题,然后不会选中复选框或收音机。

Anyhow here is my solution, I have created a component FocusLock.vue : 无论如何这是我的解决方案,我创建了一个组件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. 我希望该组件可以全局使用,因此在main.js导入它,或者在您的情况下将其导入到您需要的任何位置。

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. 另外,我认为你不应该担心处理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)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM