通过重叠的网格项使指针事件冒泡

Make pointer events bubble through overlapping grid items

在我的应用程序中,我有两个重叠的网格项。因此,点击或悬停等指针事件仅在上层元素上触发(根据 z-index 或出现顺序)。

我想知道是否有办法(react/js 或 css 明智地)让两个项目都检测到指针事件或至少模拟该行为:

这是我的例子:

export default function App() {
  function onClick() {
    console.log("click");
  }

  return (
    <div className="grid">
      <div className="item-1" />
      <div className="item-2" onClick={onClick}>
        Click
      </div>
    </div>
  );
}
.grid {
  display: grid;
  grid-template-columns: 100px 100px 100px;
}

.item-1 {
  grid-row: 1;
  grid-column: 1/-1;
  background-color: deeppink;
}

.item-1:hover {
  background-color: lime;
}

.item-2 {
  grid-row: 1;
  grid-column: 2;
  border: 1px solid black;
  cursor: pointer;
}

function App() {
  function onClick() {
    console.log("click");
  }

  return (
    <div className="grid">
      <div className="item-1" />
      <div className="item-2" onClick={onClick}>
        Click
      </div>
    </div>
  );
}

ReactDOM.render(
  <App />,
  document.getElementById("root")
);
.grid {
  display: grid;
  grid-template-columns: 100px 100px 100px;
}

.item-1 {
  grid-row: 1;
  grid-column: 1/-1;
  background-color: deeppink;
}

.item-1:hover {
  background-color: lime;
}

.item-2 {
  grid-row: 1;
  grid-column: 2;
  border: 1px solid black;
  cursor: pointer;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>

<div id="root"></div>

看到这个codesandbox

item-1 在悬停时改变颜色,item-2 在点击时触发一个函数。显然,当悬停在 item-2 上时,它会捕获悬停事件并且 item-1 不会改变它的颜色。 我正在寻找的是一种将悬停(并单击)item-2 也重定向到 item-1 或以某种方式“冒泡”通过这两个元素的方法。

不想做的是改变html-结构,即两个元素必须是网格容器的平行子代。

点击:

有一种方法可以模拟这个。最好看在这个code sandbox,不过原理很简单

在上层元素上注册一个点击事件处理器。在处理程序中,执行您需要执行的任何操作,并决定是否要将点击传递到下方元素。如果是,则将pointer-events: none应用于上层元素并调用document.elementFromPoint(event.pageX, event.pageY).click()。这将在原始点击的位置搜索元素,并且由于上部元素不在路上,将 return 下部元素或该位置下部元素内的任何内容,然后模拟点击。然后只需应用 pointer-events: initial 即可恢复所有内容。

由于 DOM 元素自然冒泡,您可以在“上层结构”的顶部附加上层事件处理程序并取消内部事件处理程序。据我所知,它保留了正常的事件语义。

document.getElementById("upper").addEventListener("click", (event) => {
  document.getElementById("upper").style.pointerEvents = "none";
  document.elementFromPoint(event.pageX, event.pageY).click();
  document.getElementById("upper").style.pointerEvents = "initial";
});

唯一需要注意的是,下层元素只会接收假点击事件,因此修改键和位置等属性将不可用。如果需要的话,最好单独传这个数据。

悬停:

编辑:我考虑过悬停部分并想出了这个解决方案:

mousemovemouseout 处理程序添加到上部元素并检查 mousemove 是否 pageX / pageY 坐标在边界内使用 getBoundingClientRect 的其他元素的框。如果是,请将 class 添加到其他元素以指示它们正在悬停。然后在有 :hover 选择器的地方添加匹配的悬停 class 选择器。清除 mouseout 上的悬停 classes(并卸载等)。

modified your sandbox显示悬停方法。

不过,此方法也有一个警告。如果您在上一个元素之上创建一个新元素,则不会调用 mouseout。所以记住这一点。