web-dev-qa-db-ja.com

vuejs:div要素の@ keyup.escが機能しない

「シャドウモーダル」div上にあるESCボタンをクリックすると、「close」イベントが発生することを期待していますが、発生していません

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>
10
DmitrySemenov

Keyboradイベントをバインドしようとしているのはinput要素ではありませんが、tabindexを定義しない限り機能しません。

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

リファレンスは次のとおりです。 https://www.w3.org/WAI/GL/WCAG20/WD-WCAG20-TECHS/SCR29.html

23

(以前の2つの回答とは別に。)

Vueですべてのイベントを処理する必要はありません。

ここで別の方法

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

DmitrySemenovソリューションは私には有効ですが、ページに複数のモーダルがある場合、それは最良のソリューションではありません。私はそれを試してみましたが、すべてのモーダルに対してcloseイベントが発生することを発見しました。

それを行う最善の方法は、モーダルが表示されたときに「キーアップ」イベントを登録し、非表示にしてから登録を解除することだと思います。イベントは必要なときにのみ登録されるため、利点があります。それを行うには、「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>
1
Dawid Loranc

私の代替実装

<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>
0
DmitrySemenov