You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将context-menu事件转发至同级div元素

问题描述

我开发了一个Web应用,用户可在div内创建可视化元素。其中一个场景是:用户创建了一个可点击且带有hover事件的div(class为me的元素),但它与带有context-menu事件的元素是同级关系,且显示在这些元素上方。

请问是否可以让位于上层的me元素保留自身的点击事件,同时将context-menu事件透明地转发到鼠标光标下方的元素(如class为cousin的子元素),即便它们是同级元素?

我曾尝试在editor div中捕获context-menu事件,找到鼠标光标下方的所有直接子元素并派发新的context-menu事件,但由于目标元素嵌套在同级元素内部,事件无法传递到它们。

原始代码示例

HTML

<header id="header">Header text<br/> x: ?<br/> y: ?</header>
<div id="editor">
  <div class="sibling">
    <div class="nephew">
      <div class="cousin">
        <div class="text" oncontextmenu="alert('context-menu on \'Value 1\'')">Value 1</div>
      </div>
      <div class="cousin">
        <div class="text" oncontextmenu="alert('context-menu on \'Value 2\'')">Value 2</div>
      </div>
    </div>
  </div>
  <div class="me" onclick="alert('clicked \'me\'')" oncontextmenu="alert('context-menu on \'me\'')"></div>
</div>

CSS

#header {
  padding: 25px;
  border: 1px solid black;
}

#editor {
  position: relative;
}

.sibling {
  position: absolute;
  background-color: red;
  border: 5px solid blue;
  opacity: 0.3;
  width: 250px;
  height: 250px;
  top: 50px;
  left: 75px;
}

.me {
  position: absolute;
  opacity: 0.3;
  width: 300px;
  height: 300px;
  top: 25px;
  left: 50px;
  border: 5px solid black;
}

.me:hover {
  border-color: red;
  background-color: green;
}

.nephew {
  height: calc(100% - 30px);
  width: calc(100% - 30px);
  margin: 9px;
  padding: 5px;
  border: 1px solid white;
}

.cousin {
  color: yellow;
  align-content: center;
  text-align: center;
  margin: auto 0;
  height: 50%;
  width: 100%;
  border: 1px solid blue;
}

JavaScript(原始尝试)

let editor = document.getElementById("editor");
editor.oncontextmenu = e => {
  // 阻止浏览器默认右键菜单
  e.preventDefault();

  for (let i = 0; i < editor.children.length; i++) {
    let currentChild = editor.children[i];
    if (currentChild == e.target) {
      // 跳过触发事件的元素本身
      continue;
    }

    let currentRect = currentChild.getBoundingClientRect();
    if (currentRect.left < e.offsetX &&
      currentRect.top < e.offsetY &&
      currentRect.right > e.offsetX &&
      currentRect.bottom > e.offsetY) {

      // 问题:事件只会派发给同级元素,无法传递到内部的text元素
      // 因为text元素才有contextmenu事件,同级元素没有,所以此方法无效
      let ev = new MouseEvent('contextmenu', {
        screenX: e.screenX,
        screenY: e.screenY,
        clientX: e.clientX,
        clientY: e.clientY,
        ctrlKey: e.ctrlKey,
        shiftKey: e.shiftKey,
        altKey: e.altKey,
        metaKey: e.metaKey,
        button: e.button,
      });
      currentChild.dispatchEvent(ev);
    }
  }
}

let header = document.getElementById("header");
document.onmousemove = e => {
  header.innerHTML = 'Header text<br/>x: ' + e.pageX + '<br/>y: ' + e.pageY;
}
解决方案

可以通过临时让上层元素不接收指针事件,再获取鼠标位置下的底层元素,最后转发contextmenu事件来实现,且无需依赖jQuery。

修改后的JavaScript代码如下:

const meElement = document.querySelector('.me');
const header = document.getElementById("header");

// 保留me元素的点击事件,无需修改
// 处理me元素的contextmenu事件
meElement.addEventListener('contextmenu', (e) => {
  e.preventDefault();
  e.stopPropagation();

  // 临时禁用me元素的指针事件,让底层元素能被检测到
  meElement.style.pointerEvents = 'none';
  
  // 获取鼠标坐标对应的最底层元素
  const targetElement = document.elementFromPoint(e.clientX, e.clientY);
  
  // 恢复me元素的指针事件
  meElement.style.pointerEvents = '';

  // 给目标元素派发contextmenu事件
  if (targetElement) {
    const contextMenuEvent = new MouseEvent('contextmenu', {
      bubbles: true,
      cancelable: true,
      clientX: e.clientX,
      clientY: e.clientY,
      screenX: e.screenX,
      screenY: e.screenY,
      ctrlKey: e.ctrlKey,
      shiftKey: e.shiftKey,
      altKey: e.altKey,
      metaKey: e.metaKey,
      button: e.button
    });
    targetElement.dispatchEvent(contextMenuEvent);
  }
});

// 原有的鼠标移动监听不变
document.onmousemove = e => {
  header.innerHTML = `Header text<br/>x: ${e.pageX}<br/>y: ${e.pageY}`;
};

核心逻辑说明

  1. 临时禁用指针事件:通过设置pointerEvents: none,让me元素暂时不拦截鼠标事件,这样document.elementFromPoint()就能准确获取到鼠标下方的嵌套元素(比如Value 1对应的div)。
  2. 事件转发:复制原contextmenu事件的所有属性,派发给找到的底层元素,确保其事件处理逻辑正常触发。
  3. 保留原有交互:仅在右键触发时临时修改指针属性,处理完成后立即恢复,不影响me元素的点击和hover事件。

内容的提问来源于stack exchange,提问作者Jabbl

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 19:59:49