JavaScript 练手小技巧:HTML5 的 dialog 标签制作对话框

对话框,在应用中常常用来做信息提示、特定操作(如,登录、删除信息等)。

一、传统对话框做法

以前创建对话框,需要用 标签去模拟,或者使用一些框架、插件,如 artDialog、boostrap 等,去创建对话框。

如:使用 标签去模拟对话框

 

对话框的内容

 特定的功能还需要自己编写 JavaScript 代码,详见博文《JavaScript练手小技巧:页面遮罩层》:JavaScript练手小技巧:页面遮罩层_stones4zd的博客-CSDN博客_js遮罩层覆盖整个页面

二、dialog 标签

是HTML5新增的语义化双标签,用于展示一个交互式的模态对话框。

它既可以创建模态化对话框(带半透明背景遮罩层的对话框,必须完成对话框操作后,如单击【确定】或【取消】按钮等将该对话框关闭,才能进行下一步操作。),也可以创建非模态化对话框。

既可以使用键盘,点击按钮关闭对话框;也可以使用 esc键 关闭对话框。

  • 模态化对话框

    •  非模态化对话框

      三、基础使用

      1. 基础结构和样式

      是双标签,可以在它的头尾标签之间自定义对话框内容。如:

       

      这个是对话框的内容

      这是一个简单的对话框

      默认

      标签是不可见的。

      要显示对话框,需要添加 open 属性。

       

      这个是对话框的内容

      这是一个简单的对话框

       

      标签基础样式:

      dialog {
          display: none;
          position: absolute;
          inset-inline-start: 0px;
          inset-inline-end: 0px;
          width: fit-content;
          height: fit-content;
          background-color: canvas;
          color: canvastext;
          margin: auto;
          border-width: initial;
          border-style: solid;
          border-color: initial;
          border-image: initial;
          padding: 1em;
      }
      dialog[open] {
          display: block;
      }

      注意几个细节:

      1. 标签的显示与隐藏是通过 display:block/none; 进行操作的。意味着, 标签默认不能进行透明度渐隐变化。

      2. 标签默认是绝对定位的。

      3. 标签自带有 1em 的 padding 值。

      4. 标签是通过设置 margin:auto 实现的水平居中。

      2. JS控制对话框的显示隐藏

      标签常用方法:

      (1)show()

      非模态化显示对话框,对其添加open属性。在非模态化下,不能使用 esc 键让对话框消失。

      如:点击按钮,显示对话框。

      let btn = document.getElementById("btn");
      let myDialog = document.querySelector("#myDialog"); // 获取对话框标签
      btn.addEventListener("click",function (e) {
          myDialog.show();
      });

      (2)showModal()

      模态化显示对话框,对其添加open属性并且显示遮罩层,同时按ESC键可以关闭对话框。

      let btn = document.getElementById("btn");
      let myDialog = document.querySelector("#myDialog"); // 获取对话框标签
      btn.addEventListener("click",function (e) {
          myDialog.showModal();
      });

      模态化 的样式:

      dialog:modal {
          position: fixed;
          inset-block-start: 0px;
          inset-block-end: 0px;
          max-width: calc((100% - 6px) - 2em);
          max-height: calc((100% - 6px) - 2em);
          user-select: text;
          visibility: visible;
          overflow: auto;
      }

      遮罩层是 的伪标签::backdrop。其默认样式为:

      dialog::backdrop {
          position: fixed;
          top: 0px;
          right: 0px;
          bottom: 0px;
          left: 0px;
          background: rgba(0, 0, 0, 0.1);
      }

      因此,修改遮罩层就修改::backdrop的样式。

      注意几个细节:

      1. 模态化

        标签的样式是通过 dialog:modal 控制,区别于普通状态的

      2. 模态化

        标签和遮罩层,都是固定定位的。

      3. 模态化

        标签是通过设置 margin:auto 实现的水平居中。

        要垂直居中模态化

        标签,可以使用以下样式。

      .myDialog{
          top:50%;
          transform: translateY(-50%);
      }

      (3)close()

      关闭对话框,删除其open属性,同时将close方法参数保留至dialog.returnValue属性上。

      如,以下代码会在第二次显示对话框的时候,输出“abc”。

      let btn = document.getElementById("btn");
      let btn2 = document.getElementById("btn2");
      let myDialog = document.querySelector("#myDialog"); // 获取对话框标签
      btn.addEventListener("click",function (e) {
          myDialog.show();
          console.info(myDialog.returnValue);
      });
      btn2.addEventListener("click",function (e) {
          myDialog.close("abc");
      });

      利用该方法,可以给对话框添加自定义的“关闭”按钮。

       关闭 

      这个是对话框的内容

      这是一个简单的对话框

      .myDialogClose{
          width: 40px;
          height: 40px;
          background: #f90;
          color: #fff;
          line-height: 40px;
          font-size: 12px;
          text-align: center;
          position: absolute;
          right:-20px;
          top:-20px;
          cursor: pointer;
      }

      3. 对话框事件

      (1)close 事件

      当对话框关闭时触发。

      myDialog.addEventListener("close",function(e){  
         console.info(e.target);  // 输出对话框标签
      });

      (2)cancel 事件

      当按下ESC关闭模态对话框时触发,同时也会触发 close 事件。

      myDialog.addEventListener("close",function(e){
          console.info(e.target);
      });
      myDialog.addEventListener("cancel",function(e){
          console.info("esc");
      });

       注意顺序:先 cancel 事件,后 close 事件。

      四、应用

      1. 点击 dialog 关闭对话框

      有时候,我们需要点击遮罩层(也属于

      标签的一部分)就要关闭对话框。

      但是,点击对话框的内容,也在点击

      标签。

      因此,要判断点击对象的 nodeName 是否是 dialog,是,就说明点击了遮罩层,关闭对话框;不是,就是点击了

      标签里的内容,不需要关闭对话框。

      为了保证最佳点击效果,强烈建议对话框的内容用一个div单独包裹,而不是把内容直接堆在

      标签里。

       关闭  

      这个是对话框的内容

      这是一个简单的对话框

      myDialog.addEventListener("click",function(e){
          if(e.target.nodeName.toLowerCase() == "dialog"){
              myDialog.close();
          }
      });

      可以约定一个属性closeByMask,若标签上存在该属性,则可以进行点击遮罩层进行关闭:

      然后添加以下脚本即可:

      document.querySelectorAll("dialog[closeByMask]").forEach(dialog => {
          dialog.addEventListener("click",function(e){
              if(e.target.nodeName.toLowerCase() == "dialog") {
                  this.close();
              }
          }
      });

      这样页面中所有的模态化对话框,点击遮罩层都可以关闭。

      2. dialog 跟表单结合

      可以在 dialog 标签里添加表单,增加按钮,实现数据传递等。

       关闭  

      这个是对话框的内容

      这是一个简单的对话框

      注意:

      1. 标签添加属性 method = "dialog",当点击submit 按钮的时候就会关闭对话框。

      2. 如果,有多个 submit 按钮,可以给按钮添加 value 属性。在 close 事件里,利用returnValue 属性判断点击了哪个 submit按钮。从而可以选择执行不同的代码。

      3. 但是在执行完代码后,记得清空 returnValue 的值,否则会一直保留在 myDialog上。

       关闭  

      这个是对话框的内容

      这是一个简单的对话框

      myDialog.addEventListener("close",function(e){
          console.info("close");
          let rV = myDialog.returnValue;
          console.info( rV );
          if(rV =="ok"){
              console.info("你点了确定");
          }else if(rV =="no"){
              console.info("你点了取消");
          }else{
              console.info("其它操作");
          }
          myDialog.returnValue = ""; // 清空该值,否则会一直保留到对话框上。
      });

      3. 隐藏显示添加动画

      默认,

      标签的显示与隐藏是通过 display:block/none; 进行操作的。意味着, 标签默认不能进行透明度渐隐变化。

      因此,要修改为 opacity 属性来实现渐隐变化。

      但是,隐藏的时候 opacity:0 ,用户仍然可以点击

      标签。所以,还要结合 visibility:hidden; 属性来让用户不能点击透明度为 0 的对话框标签。

      dialog {
          display: block;
          opacity: 0;
          visibility: hidden;
          transition: all 0.2s;
      }
      dialog[open] {
          display: block;
          opacity: 1;
          visibility: visible;
      }

      兼容性

      目前主流浏览器都支持 标签,可以放心使用。