记录一次修复上下文菜单,点击事件没反应的过程

370 阅读2分钟

上下文菜单,通过Teleport穿透到body,但是菜单按钮点击事件怎么点都没反应,下面是上下文菜单组件代码(其他小伙伴写的),现在要我来改bug.

<template>
  <div>
    <Teleport to="body">
      <div class="workflow_contextmenu" :style="bStyle">
        <div
          class="workflow_contextmenu_item"
          v-for="item in props.menu"
          :key="item.code"
          @click.stop="handleClickItem(item.code)"
        >
          {{ item.name }}
        </div>
      </div>
    </Teleport>
  </div>
</template>

<script setup>
const props = defineProps({
  menu: {
    type: Array,
    default: () => [{ name: '删除', code: 1 }],
  },
  pos: {
    type: Object,
    default: () => {
      return {
        x: 0,
        y: 0,
      }
    },
  },
})

const emits = defineEmits(['close', 'click'])

const bStyle = reactive({
  top: props.pos.y + 'px',
})

// 菜单位置控制 可以不看
watch(
  () => props.pos,
  (newValue) => {
    const maxWidth = document.body.offsetWidth - 200
    const maxHeight = document.body.offsetHeight - props.menu.length * 30 - 20
    if (newValue.x < maxWidth) {
      bStyle.left = newValue.x + 'px'
    } else {
      bStyle.right = maxWidth - newValue.x + 235 + 'px'
    }
    if (newValue.y < maxHeight) {
      bStyle.top = newValue.y + 'px'
    } else {
      bStyle.top = maxHeight + 'px'
    }
  },
  { deep: true, immediate: true }
)
const closeMenu = () => {
  emits('close')
}
const handleMouse = (event) => {
  if (event.button === 0) {
    setTimeout(() => {
      closeMenu()
    }, 100)
  }
}

// 点击事件怎么都没有反应
const handleClickItem = (code) => {
  emits('click', code)
  emits('close')
}
onMounted(() => {
  window.addEventListener('mousedown', handleMouse, false)
})
onUnmounted(() => {
  window.removeEventListener('mousedown', handleMouse, false)
})
</script>

<style lang="less" scoped>
.workflow_contextmenu {
  position: fixed;
  cursor: pointer;
  width: 200px;
  padding: 5px 0;
  background-color: #ffffff;
  border-radius: 4px;
  border: 1px solid #ebeef5;
  box-shadow: 0px 0px 12px rgba(0, 0, 0, 0.12);
  box-sizing: border-box;
  .workflow_contextmenu_item {
    font-size: 14px;
    color: #333333;
    padding: 3px 10px;
    &:hover {
      color: #409eff;
    }
  }
}
</style>

简单说下代码,template渲染上下文菜单,接受菜单数据,位置作为props,然后就是动态样式,菜单事件。就是上面这个handleClickItem事件怎么点击都没有反应

image.png

尝试方法1 给组件z-index

刚开始以为是z-index属性,是不是页面把上下文菜单给遮住了,导致没有触发,于是在bStyle里加z-index给到9999, 还是不行

尝试方法2 看到页面有mousedown事件,看看能不能执行

页面有window.addEventListener('mousedown', handleMouse, false), 在handleMouse里加了断点,发现可以执行,那就说明这个页面事件机制是正常的,只是按钮的click事件有问题。

然后发现点击菜单按钮,mouseMove事件也执行了,(说的过去,因为点击按钮也要按下鼠标),仔细看了mouseMove事件方法,发现有一句, setTimeout(() => { closeMenu() }, 100) 是不是mousedown鼠标按下后马上就关闭了菜单,导致点击事件没来得及执行。

修复方案 注释mousedown事件
// onMounted(() => {
//   window.addEventListener('mousedown', handleMouse, false)
// })
// onUnmounted(() => {
//   window.removeEventListener('mousedown', handleMouse, false)
// })

注释了mousedown'事件,发现按钮可以正常点击了。果然是mousedown关闭了菜单按钮点击事件没来得及触发。发现按钮自己的点击方法里有emits('close')事件,所以没影响,唯一影响的是点击上下文菜单空白地方,菜单不会关闭。

感觉这是符合用户体验的。只有点击按钮才会关闭,或点击页面出菜单外空白处,到此,这个bug解决了。