将元素变成弹出框 -- Popover API详解

1,338 阅读6分钟

pexels-jorge-urosa-9131059.jpg


Popover的中文意思是弹出框或者弹出窗口,是指在用户与一个控件元素(通常是一个按钮或链接)交互时,出现在屏幕上的临时信息、选项或其他内容。主要是为了是向用户提供额外的信息、选项或功能,而不必离开当前页面或上下文。

在日常开发中,我们就能用到很多弹出框内容,比如Element-Plus等组件库中的反馈组件中的大部分组件都属于弹出框内容: image.png

MDN对Popover API(以下简称)的定义描述是:

Popover API为开发人员提供了一种标准、一致、灵活的机制,用于在其他页面内容之上显示弹出框内容。弹出框内容可以使用HTML属性进行声明性控制,也可以通过JavaScript进行控制。

是不是与HTML <dialog>元素很像,它们都是用户与某个元素进行交互之后在屏幕上弹出内容。但是Popover API创建的弹出框始终是非模态(弹出框显示时仍然可以与其它页面内容交互)的,而<dialog>可以通过JavaScript的方式创建出模态(弹出框显示时不能与其它页面内容交互)对话框。

另外,两者创建的弹出内容(<dialog>需要模态对话框)都会放到顶层中,但是Popover API创建的弹出框层级在<dialog>之上。

image.png

Popover API比较简单,只有一个相关的接口,主要还是对HTML属性、CSS伪类和伪元素还有其它接口的扩展,通过Popover API来创建弹出框有声明式和编程式两种方式。需要注意:

  • 弹出框元素可以是块级元素、行内元素或者替换元素,但不能是行内块元素,这会始终显示弹出框(非顶层)。
  • 注意浏览器兼容性: image.png

声明式创建弹出框

声明式是通过HTML popoverpopovertargetpopovertargetaction属性创建和控制弹出框。

<button popovertarget="mypopover">打开/关闭弹出框</button>
<div id="mypopover" popover>一个弹出框</div>

其中:

属性简介
popover1. auto(默认值):弹出框显示时,可以通过点击弹出框区外的区域(对应的按钮控件除外)来隐藏弹出框,因此页面上只会同时显示一个弹出框内容。

2. manual:弹出框显示时,只能通过再次点击对应的控件按钮才能隐藏弹出框。这甚至连ESC键都不能隐藏弹出框。

     这就导致如果没有点击对应的控件按钮隐藏弹出框,页面上可能会同时出现多个弹出框(后来者居上)。
用于将元素指定为弹出框元素,元素的display默认为none
popovertarget应用了popover属性的元素的id应用在<button>或按钮类<input>上,将元素转换为弹出/隐藏控件按钮。
popovertargetactionhideshowtoggle:分别表示对元素进行交互会隐藏、显示和在显示/隐藏之间切换。用于控制指定了popovertarget属性的<button>或按钮类<input>应该执行什么操作。

编程式

编程式是通过Javascript使用相应API属性、方法或事件来创建和控制弹出框。接下来,我们先来学习这些内容。

对其它接口的扩展

对其它接口的扩展包括:

属性/方法值/参数简介
HTMLElement.popoverauto/manual获取/设置元素的popover属性。
HTMLButtonElement.popoverTargetElement

HTMLInputElement.popoverTargetAction
弹出框元素的Javascript引用(不是字符串ID)获取/设置控件对应的目标弹出框元素。相当于获取/设置HTML popovertarget属性。
HTMLButtonElement.popoverTargetAction

HTMLInputElement.popoverTargetAction
hideshowtoggle获取/设置控件元素应该执行什么操作。相当于获取/设置HTML popovertargetaction属性
HTMLElement.hidePopover()-将弹出框元素从顶层移除,并隐藏。如果弹出框元素已被隐藏则会报错。
HTMLElement.showPopover()-将弹出框元素放到顶层显示。如果弹出框元素已经显示则会报错。
HTMLElement.togglePopover(force)force:使该方法的行为类似showPopover()/hidePopover()

1. 如果值为true,则显示最初隐藏的弹出框。

2. 如果值为false,则隐藏最初显示的弹出框。
将弹出框元素在显示/隐藏状态中切换。

接口

Popover API只有一个ToggleEvent事件接口,用于对在弹出框元素在显示与隐藏状态切换之间做出响应。

构造函数:

new ToggleEvent(type, init)

其中:

  • type是一个事件类型的字符串,值始终是toggleevent
  • init是包含以下属性的对象:
    {
      // 两个只读属性分别表示“正在从什么状态转换”和“正在转换到”的状态,可选值有open和closed
      oldState: String
      newState: String,
    }
    

我们不需要对这个接口进行操作,实际上它是beforetoggletoggle事件的事件对象。我们只需要在弹出框元素上监听这两个事件即可,oldStatenewState可以通过事件对象(e)中获取:

popoverElement.addEventListener('beforetoggle', (e) => {
  if (e.newState === 'open') {
    console.log("正在显示弹出窗口");
  } else {
    console.log("正在隐藏弹出窗口");
  }
})

下面是一个完整的编程式🌰:

相关CSS伪类和伪元素

:popover-open

:popover-open伪类是Popover API的内容,匹配已经显示的通过Popover API创建的的弹出框,但是不匹配<dialog>

该伪类不限制属性,通常用于设置弹出框的进/退场动画、弹出框的显示位置等等。

::backdrop

::backdrop伪元素表示一个视口大小的框,紧挨着顶层中显示的任何元素的下方单独显示。通常用于在某些情况下覆盖整个视口的背景。比如对话框(<dialog>)、弹出框处于全屏模式的元素等等。

它并不是Popover API的内容,但是也能将样式应用到弹出框下方的遮罩层,不过这一点浏览器兼容性很差。

🌰:

写在最后

现在我们已经学习完了Popover API,但是你会发现,这怎么跟那些组件库的Popover组件不一样啊?而且怎么Popover API创建的弹出框默认跟<dialog>模态对话框看着没区别啊?

确实,从样式上看它们不能说毫不相干,简直是一模一样。这点可以从MDN对Popover API的描述来理解: image.png 意思是:

网络上一种非常常见的模式是将内容显示在其他内容的顶部,将用户的注意力吸引到需要采取的特定重要信息或操作上。这些内容可以有几个不同的名称-覆盖、弹出窗口、弹出框、对话框等。我们将在整个文档中将它们称为popover...

所以在本文开头我会用Element-Plus的反馈组件来举🌰。目前如果需要做到像组件库中效果,还需要配合其它API或者是自己手动来实现。

参考资料

Popover API