记录pointer-events的一次使用

pointer-events

文章首发于个人博客

场景

在公司项目中遇到了这样子的一个场景: 有一个商品(有图标、价格等信息), 当悬浮到图标时, 要有一个悬浮的 tooltips 显示它的详细信息, 商品禁用时, 会有一个黑色的遮罩盖在上面, 此时也不能选中, 然后问题就来了, 我又想悬浮到图标上显示 tooltips, 但是上面又有一个黑色的遮罩, 怎么办呢?

代码如下(为了防止代码过长, 删除了一些标签):

<style>
  .container {
    width: 500px;
    height: 300px;
    background: red;
    position: relative;
  }
  .mask {
    width: 100%;
    height: 100%;
    background: green;
    position: absolute;
    z-index: 2;
  }
  .content {
    width: 500px;
    height: 300px;
    background: blue;
  }
</style>
<body>
  <div class="container">
    <div class="mask"></div>
    <p class="content">我是内容</p>
  </div>

  <script>
    const mask = document.querySelector('.mask');
    const container = document.querySelector('.container');
    const content = document.querySelector('.content');

    content.addEventListener('mouseenter', () => {
      console.log('content mouseenter');
    });
    mask.addEventListener('mouseenter', () => {
      console.log('mask mouseenter');
    });
    container.addEventListener('mouseenter', () => {
      console.log('container mouseenter');
    });
  </script>
</body>
复制代码

然而, 它现在的效果就是我悬浮上去时, 打印顺序如下:

// container mouseenter
// mask mouseenter
复制代码

当时, 我思前想后, 一直在想这个怎么解决, 上面又有一个遮罩, 但是mouseenter要在content上触发, 实在搞不明白了, 我就问了一下导师, 她告诉我试一下pointer-events, 我当时自己写了 demo, 然后来了句: 妙啊.

pointer-events 是什么呢?

我们来看看 Mdn 上它的定义

pointer-events CSS 属性指定在什么情况下 (如果有) 某个特定的图形元素可以成为鼠标事件的触发者 .

它的值有很多:

/* Global values */
pointer-events: inherit;
pointer-events: initial;
pointer-events: unset;

/* Keyword values */
pointer-events: auto;
pointer-events: none;
/* 以下的属性仅适用于SVG */
pointer-events: visiblePainted;
pointer-events: visibleFill;
pointer-events: visibleStroke;
pointer-events: visible;
pointer-events: painted;
pointer-events: fill;
pointer-events: stroke;
pointer-events: all;
复制代码

而要解决上面这个问题, 只需要在<div class='mask'>上添加一个pointer-events: none即可, 我们先来看看打印顺序:

// container mouseenter
// content mouseenter
复制代码

为什么呢? 我们再来看看 Mdn 文档上关于pointer-events:none的解释:

元素永远不会成为鼠标事件的触发者。但是,当其后代元素的 pointer-events 属性指定其他值时,鼠标事件可以指向后代元素,在这种情况下,鼠标事件将在捕获或冒泡阶段触发父元素的事件侦听器。

所以我们在.mask上添加了这个属性, 它就不会成为鼠标事件的触发者了, 这样子就鼠标事件的触发者就顺利的成为了.content, 就成功的解决了这个问题.

但是我们看到这个定义, 但是, 当其后代元素也指定了这个属性, 并且不是 none 的话, 鼠标事件可以指向后代元素, 剩下的省略..., 我们再来写个 demo 试试:

<style>
  .mask-child {
    width: 200px;
    height: 300px;
    background: yellow;
    pointer-events: auto;
  }
</style>
<body>
  <div class="container">
    <div class="mask">
      <div class="mask-child"></div>
    </div>
    <p class="content">我是内容</p>
  </div>

  <script>
    const maskChild = document.querySelector('.mask-child');

    maskChild.addEventListener('mouseenter', () => {
      console.log('maskChild mouseenter');
    });
  </script>
</body>
复制代码

在刚刚的代码加上这一部分, 我们再来看看, 需要注意的是我们给.mask加了一个孩子.mask-child, 然后也监听它的mouseenter事件, 我们再来看看效果:

// container mouseenter
// mask mouseenter
// maskChild mouseenter
复制代码

不出意外的, 还是一样会触发maskmouseenter事件, 所以我们就知道了, 如果你想跳过某一个元素的指定事件, 我们可以在它上面设置一个pointer-events:none, 但是需要注意的是, 其后代不要再指定pointer-events属性, 不然还是会失效的.

示例

  1. 不会触发鼠标事件(拖动、悬浮、点击等事件).
<style>
  button {
    pointer-events: none;
  }
</style>
<button onclick="handleClick()">测试</button>
<script>
  function handleClick() {
    console.log('handleClick');
  }
</script>
复制代码

并不会打印出 handleClick.

  1. 还有如果设置了:hover:active等伪类也不会触发.

像之前我们如果想给一个禁用的元素进行取消hover效果, 我们可能会这样子写:

<p class="content disable"></p>

<style>
  .content:not(disable):hover {
  }
</style>
复制代码

而现在我们可以直接这么写了, 是不是简单一点呢?

<p class="content disable"></p>

<style>
  .content.disable {
    pointer-events: none;
  }
  .content:hover {
  }
</style>
复制代码

其实还有更简单一点的方法, 就是把 .disable放在下面, 因为 css 的优先级啦.

<p class="content disable"></p>

<style>
  .content:hover {
    background: black;
  }
  .content.disable {
    background: yellow;
  }
</style>
复制代码

总结

以上就是该文章的全部内容啦, 算是给自己做一个小小的记录, 希望能够帮助到你.

猜你喜欢

转载自juejin.im/post/7079688237035290654
今日推荐