IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    原生popover终于来了!

    XboxYan发表于 2023-05-29 09:16:06
    love 0
    欢迎关注我的公众号:前端侦探

    提到 popover,相信大家都很熟悉,没错,就是组件库里经常见到的悬浮层(或者叫“气泡卡片”),比如 Ant Design

    image.png

    现在,这个好用的特性终于在Chrome 114上正式支持了~下面花几分钟快速了解一下吧

    一、popover 属性

    其实这个名称以前叫做popup,后来才更改成了popover😂。

    popover是一个全局属性。给任意元素添加popover以后,它就变成了一个悬浮层。

    <div popover>我是悬浮层</div>

    popover有两个值,分别是

    • auto:自动(默认值)。也就是浏览器默认行为,比如点击悬浮层外面会关闭悬浮层,按键盘Esc键也会关闭悬浮层
    • manual:手动。也就是没有前面的默认行为,所有操作必须由开发者手动控制。
    <div popover>我是悬浮层</div>
    <div popover="auto">我是悬浮层</div>
    <div popover="manual">我是悬浮层</div>

    悬浮层默认是隐藏的,也不能通过属性设置默认显示。那么,如何打开一个悬浮层呢?

    二、控制悬浮层的方式

    控制悬浮层有两种方式,分别是 声明式 和 命令式

    首先来看声明式,经常写原生HTML的应该会很喜欢这种方式,无需 JS 介入就可以实现悬浮层的打开和关闭,如下

    <button popovertarget="pop1">打开 auto 悬浮层</button>
    <div id="pop1" popover>我是 auto 悬浮层</div>

    只需要通过popovertarget属性将目标悬浮层的id属性和按钮相关联就行了(注意,只能是ID),效果如下

    Kapture 2023-05-04 at 19.52.20.gif

    还可以通过popovertargetaction属性来设置点击行为,有三个值,分别是

    • show:打开。
    • hide:关闭。
    • toggle:切换(默认值)。如果悬浮层是关闭的就打开,反正亦然
    <button popovertarget="pop1" popovertargetaction="show">打开 auto 悬浮层</button>
    <button popovertarget="pop1" popovertargetaction="hide">关闭 auto 悬浮层</button>
    <button popovertarget="pop1" popovertargetaction="toggle">切换 auto 悬浮层</button>
    <div id="pop1" popover>我是 auto 悬浮层</div>

    效果如下

    Kapture 2023-05-04 at 19.59.38.gif

    现在回过头来看看两种popover的区别

    <button popovertarget="pop1">切换 auto 悬浮层</button>
    <button popovertarget="pop2">切换 manual 悬浮层</button>
    <div id="pop1" popover>我是 auto 悬浮层</div>
    <div id="pop2" popover="manual">我是 manual 悬浮层</div>

    效果如下

    Kapture 2023-05-06 at 11.00.49.gif

    可以看到,auto悬浮层点击空白会自动关闭(还可以通过Esc键关闭),而manual悬浮层只能手动去关闭。当然大部分情况下auto可以满足需求。

    三、命令式方式

    所谓“命令式”,其实就是一套JS API,需要在 JS中主动去调用。

    那么,有了声明式为啥还要命令式呢?答案是,更灵活。

    比如,前面的声明式,只适用于click场景,如果需要hover也能打开悬浮层,这种方式就不行了。像这种情况,就可以采用命令式方式了。

    先看语法,很简单,就是 3 个方法

    popoverEl.showPopover(); // 打开
    popoverEl.hidePopover(); // 关闭
    popoverEl.togglePopover(force) // 切换,可传参数,强制设置为 true 或者 false

    需要注意的是,这 3 个方式仅适用于悬浮层,也就是必须有popover属性,如果是普通元素,会直接报错,如下

    image.png

    还有一种情况,如果一个本来已经打开的悬浮层,再次调用showPopover(),也会报错,反之亦然

    image.png

    因此,在使用 JS 控制时,推荐使用manual悬浮层,便于精准控制。

    下面来看一个hover控制的例子

    <button id="button">hover 打开悬浮层</button>
    <div id="pop" popover="manual">我是 hover 悬浮层</div>

    然后是相关JS

    button.addEventListener('mouseenter', () => {
      pop.showPopover()
    })
    button.addEventListener('mouseleave', () => {
      pop.hidePopover()
    })

    效果如下

    Kapture 2023-05-06 at 11.26.08.gif

    四、判断悬浮层的打开状态

    首先,从HTML结构上来看,打开和关闭没有任何属性变化,这个和details不一样(details会添加open属性)。为此,CSS 还专门出了一个伪类:open用于标识悬浮层的打开状态

    目前还不稳定,后续可能会更变为:popover-open
    div[popover]:open{
      /* 打开样式 */
    }

    通过这个伪类,我们可以很轻松的给悬浮层添加过渡动画

    [popover]{
      display: block; /*默认是display:none,不会有动画*/
      visibility: hidden;
      opacity: 0;
      transform: scale(.6);
      transition: .3s;
    }
    [popover]:open{
      visibility: visible;
      transform: scale(1);
      opacity: 1;
    }

    效果如下

    Kapture 2023-05-06 at 14.31.03.gif

    除了 CSS 方式,JS 也能判断悬浮层的状态,但是并不是自己想象的那样。

    起初,我以为有一个属性可以直接获取到悬浮层的状态,发现并不行,如下

    popoverEl.open // undefined

    那如何获取呢?

    其实可以借助前面 CSS 的方式,只要匹配的:open伪类不就可以了吗,需要用到matches方法

    https://developer.mozilla.org/en-US/docs/Web/API/Element/matches

    这样就能随时获取到悬浮层的打开状态了

    popoverEl.matches(':open')

    另外,还可以通过事件监听的方式来获取,需要用到新的事件toggle,这是一个专门针对popover新推出的事件,使用方式如下

    popoverEl.addEventListener("toggle", (event) => {
      if (event.newState === "open") {
        console.log("打开状态");
      } else {
        console.log("关闭状态");
      }
    });

    五、popover 的顶层特性

    前面说了那么多,好像并没有什么很厉害的地方,随便一个 div 都可以模拟,而且现在的组件库不是也实现的好好的吗?到底有什么优势呢?

    打开控制台可以看到,popover上有一个很特殊的标识

    image.png

    这个就是顶层 top-layer !也就是层级是最高的,高于页面上的一切。

    这也是悬浮层的意义所在,本身就应该是悬浮在最上面。下面是示意图

    image.png

    这样的好处就是无论在 HTML中的任何位置,都无需担心悬浮层被遮挡的情况,也无需将悬浮层移动的最外层body上。

    这个特性和dialog是一样的,有兴趣的可以参考之前这篇文章:你可能不知道的dialog弹窗

    以上完整代码可以查看以下任意链接:

    • popover (codepen.io)
    • popover (runjs.work)

    如果浏览器不支持,会有下面提示

    image-20230506163742550

    这个是用@supports实现的

    @supports selector([popover]:open) {
        .no-support{
            display: none !important;
        }
    }

    六、兼容性和总结

    看一下兼容性,目前只有Chrome 114+支持,内部项目可以尝鲜一下,如果是Electron应用,那就大胆使用吧

    image.png

    下面来总结一下本文要点:

    1. popover是一个全局属性。给任意元素添加popover以后,它就变成了一个悬浮层。
    2. popover属性有两个值,默认是auto自动模式,支持默认行为,比如点击空白关闭,键盘Esc关闭
    3. popover属性还支持manual手动模式,也就是没有以上默认行为
    4. 控制popover有两种方式,分别是声明式和命令式
    5. 声明式是指通过HTML属性来实现点击交互
    6. 可以通过popovertarget属性将悬浮层的id和按钮相关联,这样就能通过按钮打开悬浮层了
    7. 还可以通过popovertargetaction属性来设置点击行为,有show、hide、toggle3种方式
    8. 命令式是指通过 JS API来实现对悬浮层的控制,相比声明式而言更加灵活
    9. 控制悬浮层的方法有showPopover、hidePopover、togglePopover
    10. CSS伪类:open可以区分悬浮层的打开状态
    11. JS 可以通过matches(':open')来获取悬浮层的打开状态
    12. JS 还可以通过监听toggle事件来获取悬浮层的打开状态,方式是event.newState
    13. 相比传统实现,原生popover最大的优势是支持顶层特性

    最近正在对xy-ui进行升级改造,里面的 popover 组件就使用到了这一特性,不过也对不支持的浏览器做了兼容,有兴趣的可以去体验一下。

    https://xy-ui.codelabo.cn/components/popover

    image.png

    最后,如果觉得还不错,对你有帮助的话,欢迎点赞、收藏、转发❤❤❤

    欢迎关注我的公众号:前端侦探


沪ICP备19023445号-2号
友情链接