25

我希望当我单击“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>

4

6 回答 6

40

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

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

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

于 2018-03-01T04:39:44.233 回答
8

虽然 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上看到它):

模态的.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>

应用程序.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>
于 2018-12-20T19:29:56.640 回答
6

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

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

于 2019-12-08T18:44:57.107 回答
5

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

您没有使用 Vue 处理所有事件。

这里是另一种方式

export default {
  created() {
    document.onkeydown = evt => {
      evt = evt || window.event;
      if (evt.keyCode == 27) {
        this.$emit("close");
      }
    };
  }
};
于 2018-03-01T07:11:43.363 回答
1

我的替代实现

<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>
于 2018-03-01T20:39:44.700 回答
-3

不能从 div 或其他元素生成 Key 事件。为了从 div 获取关键事件,您需要<Input type="text"></Input>在 div 元素内部使用。

于 2018-03-01T06:56:27.780 回答