0

I have this context menu which you can find online. I want to access the click event of the button in order to open a Bootstrap Modal. The problem is that when I set the @blur attribute to the close function I cannot handle the click event anymore. It seems like the click event gets ingored because the div gets closed beforehand. Also the context menu closes itself even if I click on the button which is incorrect. It should only close when I click outside the div. Is there a workaround for it?

<template>
    <div class="context-menu" v-show="show" :style="style" ref="context" tabindex="0" @blur="close">
        <button @click="handle" class="btn btn-outline-secondary">Create Object</button>
    </div>
</template>

<script>
import { nextTick } from '@vue/runtime-core';

export default {
    name: 'ContextMenu',
    data(){
        return {
            left: 0,
            top: 0,
            show: false,
        }
    },
    computed: {
        style() {
            return {
                top: this.top + 'px',
                left: this.left + 'px',
            };
        },
    },
    methods: {
        close() {
            console.log("closed")
            this.show = false;
            this.left = 0;
            this.top = 0;
        },
        open(e) {
            this.left = e.pageX || e.clientX;
            this.top = e.pageY || e.clientY;
            nextTick(() => this.$el.focus())
            this.show = true;
        },
        handle(){          
            console.log("clicked")
            //Do something
            this.close()
        }
    }
}
</script>

<style scoped>
.context-menu {
    position: fixed;
    background: white;
    z-index: 999;
    outline: none;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);
    cursor: pointer;
}
</style>
Florent
  • 111
  • 10

1 Answers1

0

This is because your div is losing focus to your button. blur event fires on the parent div before the click event can be fired on your button. The moment you click on the button, these events fire in sequence:

mousedown (button) -> blur (div) -> focus (button) -> mouseup (button) -> click (button)

const parent = document.getElementById("parent");
const button = document.getElementById("button");
const log = document.getElementById("log");

parent.focus();

parent.onblur = (e) => {
  const msg = document.createElement("div");
  msg.innerText = `parent blur: losing focus to #${e.relatedTarget?.id}`;
  log.appendChild(msg);
};

button.onmousedown = (e) => {
  const msg = document.createElement("div");
  msg.innerText = "button mousedown";
  log.appendChild(msg);
};

button.onmouseup = (e) => {
  const msg = document.createElement("div");
  msg.innerText = "button mouseup";
  log.appendChild(msg);
};

button.onclick = (e) => {
  const msg = document.createElement("div");
  msg.innerText = "button click";
  log.appendChild(msg);
};

button.onfocus = (e) => {
  const msg = document.createElement("div");
  msg.innerText = "button focus";
  log.appendChild(msg);
};

button.onblur = () => {
  const msg = document.createElement("div");
  msg.innerText = "button blur";
  log.appendChild(msg);
};
#parent {
  background: pink;
  padding: 20px;
}

#parent:focus {
  border: 1px solid blue;
}
<div id="parent" tabindex="0">
  <button id="button"> click me </button>
</div>

<div id="log">

</div>

In your blur handler, you will need to check if the element you are losing focus to is still within your parent div. The blur event has a relatedTarget property which will be the element receiving the focus. This answer has a solution for the task.

Xinchao
  • 2,929
  • 1
  • 24
  • 39