【问题标题】:vuejs: @keyup.esc on div element is not workingvuejs:div 元素上的@keyup.esc 不起作用
【发布时间】:2018-08-09 02:33:30
【问题描述】:

我希望在我离开时会触发“关闭”事件 单击“shadow-modal”div上的ESC按钮,但没有发生

vue 2.5.13,有什么想法吗?

    <template>
      <div class="shadow-modal"
         @keyup.esc="$emit('close')">
        <transition name="modal">
          <div class="modal-mask">
            <div class="modal-wrapper">
              <div class="modal-container">
                <div class="modal-header">
                  <slot name="header">
                    default header
                  </slot>
                </div>
                <div class="modal-body">
                  <slot name="body">
                    default body
                  </slot>
                </div>
                <div class="modal-footer">
                  <slot name="footer">
                    <a href="#"
                       class="btn btn--diagonal btn--blue"
                       @click="$emit('close')">Cancel</a>
                  </slot>
                </div>
              </div>
            </div>
          </div>
        </transition>
      </div>
    </template>

【问题讨论】:

    标签: vue.js vuejs2 vue-events


    【解决方案1】:

    虽然您尝试绑定键盘事件的不是输入元素,但除非您定义 tabindex,否则它们将不起作用:

    <div class="shadow-modal" @keyup.esc="$emit('close')" tabindex="0">
    

    这是一个参考:https://www.w3.org/WAI/GL/WCAG20/WD-WCAG20-TECHS/SCR29.html

    【讨论】:

    • 试过了,只有先点击“DIV”,然后ESC关闭面板才有效。如果模式打开,然后立即单击 ESC - 没有任何反应。
    • @DmitrySemenov 在根 div 上设置事件侦听器 @keyup.esc 会起作用。另一种方法是在模式打开时在“DIV”上trigger focus
    • 添加mounted关闭而不点击DIV:this.$refs.modal.focus()
    【解决方案2】:

    虽然 DmitrySemenov 解决方案对我有用,但当页面上有多个模式时,它并不是最佳解决方案。我试过了,我发现它会为每个模式触发关闭事件。

    我认为最好的方法是在显示模式时注册“keyup”事件并在隐藏后取消注册。它为您提供了一个优势,因为该事件仅在需要时才注册。为此,您需要为“show”属性添加一个观察者:

      props: {
        show: {
          type: Boolean,
          default: false
        }
      },
      watch: {
        show() {   
          if (this.show === false) {
            window.removeEventListener("keyup", this.onEscapeKeyUp);
          } else {
            window.addEventListener("keyup", this.onEscapeKeyUp);
          }
        }
      },
      methods: {
        onEscapeKeyUp(event) {
          if (event.which === 27) {
            this.$emit("close");
          }
        }
      }
    

    模态框应该有v-if="show" 来控制模态框的可见性:

    <div class="modal-mask" v-if="show" @click="$emit('close');">
    

    完整的解决方案代码(可以在codesandbox.io看到):

    Modal.vue

    <template>
      <transition name="modal">
        <div class="modal-mask" v-if="show" @click="$emit('close');">
          <div class="modal-wrapper" @click.stop>
            <div class="modal-container">
              <div class="modal-header"><slot name="header"></slot></div>
              <div class="modal-body"><slot name="body"></slot></div>
              <div class="modal-footer">
                <slot name="footer">
                  <div class="buttons">
                    <a class="button" href="#" @click.prevent="$emit('close');">
                      OK
                    </a>
                  </div>
                </slot>
              </div>
            </div>
          </div>
        </div>
      </transition>
    </template>
    
    <script>
    export default {
      name: "Modal",
      props: {
        show: {
          type: Boolean,
          default: false
        }
      },
      watch: {
        show() {
          const body = document.querySelector("body");
    
          if (this.show === false && body.style.overflow === "hidden") {
            body.style.overflow = "";
            window.removeEventListener("keyup", this.onEscapeKeyUp);
          } else {
            body.style.overflow = "hidden";
            window.addEventListener("keyup", this.onEscapeKeyUp);
          }
        }
      },
      methods: {
        onEscapeKeyUp(event) {
          if (event.which === 27) {
            console.log("close event");
            this.$emit("close");
          }
        }
      }
    };
    </script>
    
    <style lang="scss" scoped>
    .modal-mask {
      position: fixed;
      z-index: 1100;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      background-color: rgba(0, 0, 0, 0.5);
      overflow: auto;
      transition: opacity 0.3s ease;
      display: flex;
      justify-content: center;
      align-items: center;
    }
    
    .modal-wrapper {
      max-width: 980px;
      width: 100%;
    }
    
    .modal-container {
      padding: 1.5em 2em;
      background-color: white;
      border-radius: 2px;
      box-shadow: 0 2px 8px rgba(0, 0, 0, 0.33);
      transition: all 0.3s ease;
    }
    
    .wrapper {
      max-width: 980px;
    }
    
    .modal-body {
      margin: 1em 0;
    }
    
    .modal-enter {
      opacity: 0;
    }
    
    .modal-leave-active {
      opacity: 0;
    }
    
    .modal-enter .modal-container,
    .modal-leave-active .modal-container {
      -webkit-transform: scale(1.1);
      transform: scale(1.1);
    }
    </style>
    

    App.vue

    <template>
      <div id="app">
        <button @click="show1 = true;">Show first modal</button>
        <button @click="show2 = true;">Show second modal</button>
    
        <Modal :show="show1" @close="show1 = false;">
          <div slot="header"><h1>First Modal</h1></div>
          <div slot="body">
            Lorem ipsum dolor sit amet consectetur, adipisicing elit. Rem beatae
            repellat dolores deleniti illum voluptatem facilis neque ut placeat,
            eius iusto tempore! Totam omnis non tempore perferendis expedita numquam
            neque!
          </div>
        </Modal>
    
        <Modal :show="show2" @close="show2 = false;">
          <div slot="header"><h1>Second Modal</h1></div>
          <div slot="body">
            Lorem ipsum dolor sit amet consectetur, adipisicing elit. Rem beatae
            repellat dolores deleniti illum voluptatem facilis neque ut placeat,
            eius iusto tempore! Totam omnis non tempore perferendis expedita numquam
            neque!
          </div>
        </Modal>
      </div>
    </template>
    
    <script>
    import Modal from "./components/Modal";
    
    export default {
      name: "App",
      components: {
        Modal
      },
      data() {
        return {
          show1: false,
          show2: false
        };
      }
    };
    </script>
    

    【讨论】:

    • 大卫,这太棒了!对于制作干净且可重用的 Modal 组件也很有帮助,将在项目中使用它,谢谢!
    【解决方案3】:

    如果你需要关闭一个模态窗口,你必须遵守几个条件。

    1. 在模态窗口的标签中:
    <div class="modal-content" ref="modal" tabindex="0" @keyup.esc="$emit('close')">
    
    1. 在生命周期钩子mounted()中:
    mounted() {
       this.$refs.modal.focus()
    }
    
    

    【讨论】:

      【解决方案4】:

      (除了前面的 2 个答案。)

      你没有用 Vue 处理所有事件。

      这里是另一种方式

      export default {
        created() {
          document.onkeydown = evt => {
            evt = evt || window.event;
            if (evt.keyCode == 27) {
              this.$emit("close");
            }
          };
        }
      };
      

      【讨论】:

      • 这不是必需的,它将获取所有按键事件。
      • codesandbox.io/s/038lv1n1vl 在另一个答案中提到,您可以使用 tabindex
      【解决方案5】:

      我的替代实现

      <template>
        <div class="shadow-modal"
           @keyup.esc="$emit('close')">
          <transition name="modal">
            <div class="modal-mask">
              <div class="modal-wrapper">
                <div class="modal-container">
                  <div class="modal-header">
                    <slot name="header">
                      default header
                    </slot>
                  </div>
                  <div class="modal-body">
                    <slot name="body">
                      default body
                    </slot>
                  </div>
                  <div class="modal-footer">
                    <slot name="footer">
                      <a href="#"
                         class="btn btn--diagonal btn--blue"
                         @click="$emit('close')">Cancel</a>
                    </slot>
                  </div>
                </div>
              </div>
            </div>
          </transition>
        </div>
      </template>
      
      <script>
      export default {
        beforeMount() {
          window.addEventListener('keyup', this.onEscapeKeyUp);
        },
        beforeDestroy () {
          window.removeEventListener('keyup', this.onEscapeKeyUp)
        },
        methods: {
          onEscapeKeyUp (event) {
            if (event.which === 27) {
              this.$emit('close');
            }
          },
        },
      };
      </script>
      

      【讨论】:

        【解决方案6】:

        不能从 div 或其他元素生成 Key 事件。为了从 div 中获取关键事件,您需要在 div 元素中使用&lt;Input type="text"&gt;&lt;/Input&gt;

        【讨论】:

          猜你喜欢
          • 2018-10-01
          • 1970-01-01
          • 2021-05-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-02-13
          相关资源
          最近更新 更多