聊聊Top Layer顶层特性的隐患与实践

这篇文章发布于 2024年06月11日,星期二,00:00,归类于 Web综合。 阅读 4913 次, 今日 17 次 3 条评论

 

封面图,海边,沙滩,救生圈

一、好不好用过才知道

HTML5 <dialog>元素使用showModal()显示的时候,自动层级最高,自动居中,自动有黑色蒙层,明显就比使用show()方法或者设置open属性显示弹框要好。

于是,就在前段时间,对于LuLu UI Edge主题的弹框组件,我就进行了大刀阔斧的修改,让弹框的显示均采用showModal()方法(通过劫持open属性实现)。

修改完了自我感觉良好,但是真的实践之后,痛苦的事情来了,由于dialog元素顶层了,因此,自定义的验证提示,浮动定位,toast提示等,全都无法显示,因为这些组件的元素创建都是在body元素下的,由于弹框层级顶级,因此,这些组件样式被覆盖地死死的,用户根本就看不到。

唯一的方法就是让toast元素,浮层元素全都append到<dialog>元素中,这实在是太麻烦了。

于是,没办法,妥协,仅alert和confirm类型的弹框使用showModal()显示,其他类型的弹框还是传统的show()方法显示,使用JavaScript进行层级控制。

可见,看起来很棒的特性真正使用,可能并没有想的那么好。

就像找对象一样,看起来长得漂亮身材又好,真正相处并不一定自在。

二、顶层特性说的是什么?

撤了这么久的“顶层特性”究竟是什么呢?

MDN上对这个术语有专门的文档,参见:https://developer.mozilla.org/en-US/docs/Glossary/Top_layer

简而言之,就是浏览器会在文档外创建一个独立的层,这个层正好覆盖整个浏览器视口的宽度和高度,位于页面所有内容之上。

打开控制台,查看元素,会看到#top-layer的标识,例如:

#top-layer示意

一旦看到这个标识,就说明页面有顶层元素了。

如何创建顶层元素?

目前来说,创建顶层元素有下面这几种方法:

  • 元素全屏显示,可以通过调用Element.requestFullscreen()方法实现;
  • 上面提到的使用showModal()方法显示的<dialog>元素
  • 使用HTMLElement.showPopover()方法显示的弹出层元素。

其中,requestFullscreen()可以让任意元素进入全屏显示状态,showPopover()则可以让任意的HTML元素以弹出层的形式显示,我之前写过一篇文章专门介绍,详见“时代变了,该使用原生popover属性模拟下拉了”。

至于CSS锚点定位(CSS Anchor Positioning API)虽然也是浏览器原生的定位效果,但是,并不会让元素顶层显示,因此,目前,让元素顶层的方法就上面3种。

三、隐患规避的思考

要想让自定义的提示效果能在具有顶层特性的Dialog对话框上显示,唯一的办法就是以毒攻毒,也就是自定义提示元素我也弄成顶层元素,顶层元素遵循后显示层级更高的元素,因此,这种方法一定可以让提示元素在对话框之上。

所以,归根结底,最好的解决方法,那就是把Tips提示或Toast提示效果使用showPopover()方法显示。

因此,传统组件中的HTMLElement.style.display=”block”这种显示方法就不能使用了,改为给对应的提示元素设置popover属性,然后再调用showPopover()方法。

这里有个演示案例,交互过程是这样的,点击按钮,显示一个模态对话框,模态对话框中还有个显示toast提示的按钮,点击之后,再显示提示框,注意,这里的提示框是使用showPopover()方法实现的。

直接HTML就可以完整运行:

<button onclick="dialog.showModal();">点击我显示模态对话框</button>

<dialog id="dialog">
    <blockquote>
        <button onclick="toast.showPopover();">显示toast提示</button>
        <button onclick="dialog.close();">关闭</button>
    </blockquote>
</dialog>

<div id="toast" class="toast" popover>我是提示内容</div>

按照预定的路径连续点击后,就可以看到toast出现在对话框上面啦!

眼见为实,您可以狠狠地点击这里:popover实现toast提示覆盖顶层对话框demo

此时,页面中其实存在两个顶层元素,截图示意如下:

两层顶层元素示意

改造成本极低,技术上完全可行。

不过,考虑到兼容性,Safari 23年9月26日才从版本17开始支持,仍有大量用户尚未升级到此版本之上,因此,组件的改造工作还需要再等1年左右,到时候LuLu UI所有这种需要顶层显示的元素全部都采用popover方式实现。

就目前而言,如果遇到顶层元素覆盖的情况,只能让提示元素当作顶层元素的子元素处理了。

四、端午节快乐

这个端午节,连续钓了三条鱼,哈哈哈,收获都还凑合,能明显看出比去年这个时候的自己进步了不少。

第一天存古垂钓园,140元6小时,钓了31斤。第二天宝沪休闲垂钓,150元6小时,钓了34斤,大热天钓这么多不容易,第三天,兄弟垂钓,100元4小时,钓了18斤。

具体过程可以参见我的抖音“最会钓鱼的程序员”,欢迎关注哈。

就是CSS世界精讲视频要delay一天,因为今早带小朋友出去玩,早上赶着出门,没什么时间。

不知道大家端午过得怎么样呢?

嘿嘿,就这样吧,一点思考与实践记录。

嘿嘿

(本篇完)

分享到:


发表评论(目前3 条评论)

  1. Woody说道:

    在这个demo中,为什么toast显示在顶层之后,内部无法交互呢,内容无法选择,双击等操作选择的是dialog里的文本。
    用dev tool查看也定位不到toast元素,定位的反而是dialog元素。

  2. codehz说道:

    我觉得dialog最麻烦的地方可能还是在于动画了,要实现展示**和**消失的动画,目前有我想到的有三种方案,一个是手动用js来控制animate,兼容性好,但是消失动画不好做,得劫持esc按键,以及不能使用原生的button target=dialog来关闭dialog了,而且这种用法相当于是把dialog滥用成一个top layer portal了,那和传统方案相比优势并不明显
    第二种是使用view-transition,这个兼容性差(只有新版chrome),效果选择有限
    第三种理论上是最优解,但兼容性更爆炸,就是使用transition-behavior: allow-discrete来允许none到block的transition,还需要使用@starting-style来设定从none转变过来时的初始样式,唯一的问题就是:只有最新的chrome能用

  3. re说道:

    `撤了` -> `扯了`