InfoMenu2-TabMoving

by Chige12

HTML

<div id="app">
  <div class="containter">
  <p>Tab移動をした時に勝手に閉じる</p>
    <msg-comp
      v-for="msg in msgList"
      :key="msg"
      :msg="msg"
    ></msg-comp>
  </div>
</div>

<script type="text/x-template" id="message">
  <div class="msg">
    <button 
      ref="msgBtn"
      v-on:click="showInfo = !showInfo"
    >
      Info {{msg}}
    </button>
    <info-menu 
      v-if="showInfo"
      v-on:close="showInfo = false"
      :msgBtn="$refs.msgBtn"
    >
    </info-menu>
  </div>
</script>

<script type="text/x-template" id="info">
  <div class="info">
    <button>AAA</button>
    <button>BBB</button>
    <button>CCC</button>
  </div>
</script>

CSS

.msg {
  position: relative;
  border: 1px solid blue;
  width: 60px;
  margin: 4px;
  padding: 4px;
  background: #fff;
}

.info {
  position: absolute;
  top: 0;
  left: 72px;
  background: #fff;
  border: 1px solid red;
  box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
  padding: 5px;
}

Vue

var listener = {
    methods:{
    listen :function(target, eventType, callback) {
        if (!this._eventRemovers){
        this._eventRemovers = [];
      }
      target.addEventListener(eventType, callback);
      this._eventRemovers.push( {
        remove :function() {
          target.removeEventListener(eventType, callback)
        }
      })
    }
  },
  destroyed:function(){
    if (this._eventRemovers){
      this._eventRemovers.forEach(function(eventRemover){
        eventRemover.remove();
      });
    }
 }
}

Vue.component('info-menu', {
  mixins:[listener],
  template: '#info',
  props: {
  	msgBtn: {
      type: HTMLButtonElement
    }
  },
  created:function(){
    this.listen(window, 'click', function(e){
      if (!this.$el.contains(e.target) && !this.msgBtn.contains(e.target)){
        this.$emit('close');
      }
    }.bind(this));
    
    this.listen(window, 'keyup', function(e){
      if (!this.$el.contains(e.target) && e.key === 'Tab'){
        this.$emit('close');
      }
    }.bind(this));
  }
});

Vue.component('msg-comp', {
  template: '#message',
  props: ['msg'],
  data() {
    return {
      showInfo: false
    }
  }
});

new Vue({
  el: '#app',
  data: {
		msgList: [
      1, 2, 3
    ]
	}
})