LuLu UI Edge版中文文档 » Dialog模态层弹框

教程

Dialog模态层弹框

安装与调用

如果是基本弹框功能,直接引用下面CSS和JS即可:

<link rel="stylesheet" href="https://unpkg.com/lu2/theme/edge/css/common/ui/Button.css">
<link rel="stylesheet" href="https://unpkg.com/lu2/theme/edge/css/common/ui/Dialog.css">
<script type="module" src="https://unpkg.com/lu2/theme/edge/js/common/ui/Dialog.js"></script>

或者:

<script type="module">
    import Dialog from "https://unpkg.com/lu2/theme/edge/js/common/ui/Dialog.js";
</script>

如果需要用到loading弹框,则还需要引入Loading.css,如下:

<link rel="stylesheet" href="https://unpkg.com/lu2/theme/edge/css/common/ui/Loading.css">

如果是npm安装调用,则:

import 'lu2/theme/edge/css/common/ui/Button.css';
import 'lu2/theme/edge/css/common/ui/Dialog.css';
// 可选
import 'lu2/theme/edge/css/common/ui/Loading.css';

import 'lu2/theme/edge/js/common/ui/Dialog.js';

如果 JS import语法报错,试试在业务代码中动态引入。

import('lu2/theme/edge/js/common/ui/Dialog.js');

概要

Edge主题中的Dialog组件性质上已经变成DOM组件,有别于peak、pure主题中原型组件。大家可以理解为对HTML5原生的<dialog>元素进行的扩展。

目前Firefox浏览器和Safari浏览器并不支持<dialog>元素,本组件进行了简易的Ployfill,可以满足常规的弹框需求。

本Dialog组件的所有功能都是围绕<dialog>元素的属性和方法展开的,使用的时候需要给<dialog>元素添加is="ui-dialog",否则还是原始UI的<dialog>元素。

例如:

<dialog id="dialog" is="ui-dialog"></dialog>

此时,设置该<dialog>元素的open属性值为true,就可以显示弹框。

测试:

button.addEventListener('click', function () {
    dialog.open = true;
});

支持使用data-params传递各类通用参数,例如:

<dialog is="ui-dialog" data-params="{
    title: '我是标题',
    closable: false,
    onHide: function () {
        console.log('隐藏了~');
    },
    buttons: [{}, {}]
}">我是内容</dialog>

测试:

我是内容

之前<dialog>内置内容会自动放在div.ui-dialog-body元素中,不过这会影响Vue/React等框架的渲染,因此,再2024之后的版本,不会对<dialog>中的元素进行任何DOM层级这块的调整,大家需要需要合理的间距,可以自行在外面包裹div.ui-dialog-body元素。

弹框的显示、隐藏

弹框显示有3种方法,分别是:

  1. dialog.open = true;
  2. dialog.setAttribute('open', '');
  3. dialog.show();
  4. dialog.showModal(); // 推荐

其中showModal()方法是模态显示,层级顶级,覆盖元素无法被点击。如果希望其他的显示方法也采用模态显示,可以添加modal属性。

<dialog modal is="ui-dialog"></dialog>

  模态对话框吗?

例如,点击下面按钮,设置某<dialog>元素的open为true,看看是不是模态对话框?

弹框隐藏有4种方法,分别是:

  1. dialog.open = false;
  2. dialog.removeAttribute('open');
  3. dialog.close();
  4. dialog.hide();   // 非原生方法

显示和隐藏测试

弹框的创建

实际开发,弹框往往并不是在页面中的,需要JS创建,此时按照普通的DOM元素创建使用就可以了。

这里列举3个不同的弹框创建案例:

1. DOM创建

测试:

点击按钮执行的代码如下:

let dialog = document.createElement('dialog');
dialog.setAttribute('is', 'ui-dialog');
dialog.addEventListener('DOMContentLoaded', function () {
    this.alert('Hello, LuLu UI!');
});
document.body.appendChild(dialog);

2. HTML字符串

测试:

点击按钮执行的代码如下:

document.body.insertAdjacentHTML('beforeend', '<dialog id="dialogAlert" is="ui-dialog"></dialog>');
dialogAlert.addEventListener('DOMContentLoaded', function () {
    this.alert('Hello, LuLu UI!');
});

3. Dialog类创建

测试:

点击按钮执行的代码如下:

new Dialog().alert('Hello, LuLu UI!');

Dialog对象隐藏了创建的细节,是更推荐的元素创建方法,详见“Dialog类使用指南”。

弹框的删除

弹框删除可以使用下面的方法:

  • dialog.remove();

测试:

比方说下面这个例子:

new Dialog({
    content: '<button onclick="this.closest(\'dialog\').remove()">删除弹框</button>'
});

如果弹框内容以字符串形式设置,则弹框关闭默认执行的就是删除操作,而不是隐藏。

弹框内容设置

如果需要让弹框显示对应的内容,则有下面这些方法:

1. 直接显示

需要显示的内容内容直接放在<dialog>元素中,例如:

<dialog id="dialog2" is="ui-dialog" title="我是标题">
    下拉:<select is="ui-select" style="width: 150px">
        <option value="1">选项1</option>
        <option value="2">选项2</option>
        <option value="3">选项3</option>
    </select>            
</dialog>

此时弹框直接显示已有内容,例如:

dialog2.show();
下拉:

测试:

其中,<dialog>元素上的title属性值会作为弹框的标题显示。此title属性是一次性的,也就是后期再使用dialog.title修改title属性是无效的,请使用dialog.params.title进行标题的修改。

2. params参数传递

本弹框组件的参数传递底层使用的是dialog.params对象,因此,对于弹框内容,就可以通过dialog.params.content。例如:

<dialog id="dialog3" is="ui-dialog"></dialog>
dialog3.params.content = 'Hello, LuLu UI!';
dialog3.show();

如果希望弹框关闭后还能继续显示当前弹框,需要使用节点对象作为弹框内容。例如本例中需要把文字转为文本节点使用:

button3.addEventListener('click', function () {
    dialog3.params.content = document.createTextNode('Hello, LuLu UI!');
    dialog3.show();
});

测试:

3. 直接在<dialog>元素上设置

<dialog>元素扩展了一个可读写的content属性,可以用来直接改变弹框内容。

<dialog id="dialog3" is="ui-dialog"></dialog>
button4.addEventListener('click', function () {
    dialog4.show().content = document.createTextNode('Hello, LuLu UI!');
});

测试:

这里的dialog4.show()返回的是弹框元素自身,因此可以级联使用。原生弹框的show()方法返回的是undefined,不支持dialog4.show().content这种用法。

4. Dialog类设置内容

如果使用Dialog类创建弹框,则弹框的内容推荐使用content参数进行传递,例如:

new Dialog({
    content: '#someId'
});

会自动把页面上id'someId'的元素作为内容显示在弹框中。

实际开发,Dialog类的使用频率是最高的,因此接下来会专门对Dialog类进行介绍。

Dialog类使用指南

Dialog类本质是对常用的原生<dialog>元素的功能进行了封装,这样大家不必关心具体的细节,专注于API本身。

1. 起步

打开控制台,输入并执行下面这行JS:

new Dialog()

页面就会显示一个空弹框,并且返回当前<dialog>弹框元素。

因此下面代码中的eleDialog本质上就是<dialog>元素,<dialog>元素所有的属性和方法都可以直接使用。

let eleDialog = new Dialog()

例如eleDialog元素扩展的alert()方法就可以使用:

let eleDialog = new Dialog();
eleDialog.alert('我是提示内容');

当然,也可以级联使用:

new Dialog().alert('我是提示内容');

下面通过多个案例演示Dialog类的不同使用场景。

2. alert信息提示框

new Dialog().alert('操作成功!', {
    type: 'success'
}); 

我们可以type参数指定提示的类型,其他内置类型还有:'remind'提示,'danger'警示。

也可以使用自定义的类型,例如:

代码如下:

button.addEventListener('click', function () {
    new Dialog().alert('自定义提示,有标题,无图标。', {
        title: '系统提醒',
        type: 'custom'
    });
});

3. confirm确认提示框

button.addEventListener('click', function () {
    new Dialog().confirm('<h6>您确定要停用所选的成员吗?</h6><p>成员停用后,可以在停用成员列表中重新启用。</p>', {
        buttons: [{
            value: '停用',
            events: function (event) {
                // 按钮禁用
                button.disabled = true;
                // event.dialog是当前实例对象
                event.dialog.remove();
            }
        }, {}]
    });
});

我们可以使用buttons参数指定按钮的文案、类型以及事件。

4. 普通的内容弹框

new Dialog({
    title: '普通文本框',
    content: '我是一段HTML,啦啦啦啦啦',
    buttons: [{
        value: '知道啦!'
    }]
}); 

5. 高度拉伸弹框

new Dialog({
    title: '中间高度自适应的容器',
    height: 'stretch',
    content: `<div class="bg-white p20">${Array(60).fill().map((_, index) => {
        return `<img src="${index}.gif" class="db mt10">`;
    }).join('')}</div>`,
    buttons: [{}]
});

通过设置参数height的值为'stretch'实现。最大支持高度为1000px,此值通过CSS进行修改。如果浏览器高度小于1000px,弹框高度会自动上下拉伸适配。

6. loading弹框

new Dialog().loading();

有时候,弹框内容是Ajax动态请求的,此时在Ajax成功之前是需要一个加载状态的。体验更好的做法是根据最终呈现布局,局部动态loading,但是有时候我们想要偷懒,不想再写一个loading布局,则就可以使用这里的loading弹框。例如下面这个示意:

代码如下:

button.addEventListener('click', function () {
    let eleDialog = new Dialog({
        width: 600
    }).loading();
    // ajax请求模拟
    fetch(location.href).then(res => res.text()).then(text => {
        eleDialog.setParams({
            title: '请求内容',
            buttons: [{}],
            content: text.replace(/[\w\W]+<pre>([\w\W]+?)<\/pre>[\w\W]+/, '$1')
        });
    });
});

这里使用params参数让loading弹框替换成内容弹框。

7. 弹框打开DOM元素

下拉:

var target = document.getElementById('xxTarget');
button.addEventListener('click', function () {
    // 如果弹框已创建,就直接显示
    if (this.dialog) {
        this.dialog.show();
    } else {
        target.classList.remove('dn');
        this.dialog = new Dialog({
            title: 'DOM载入测试',
            content: target,
            buttons: [{}, {}]
        });
    }
});

本例中,也可以直接使用ID选择器作为content参数值,例如:

new Dialog({
    title: 'DOM载入测试',
    content: '#xxTarget',
    buttons: [{}, {}]
});

语法和参数

Dialog类的基础语法

基本的弹框使用方法为:

let eleDialog = new Dialog(options);

其中:

options
options是可选参数,支持的参数见下表:
参数名称 支持类型 默认值 释义
title String '' 表示弹框的标题。
content String
Object
Function
'' 表示弹框显示的主体内容。可以是HTML字符串,或者是DOM对象,也可以是返回HTML字符串或DOM对象的函数。参数类型不同,弹框关闭的方法也不一样,DOM对象关闭执行hide()方法,HTML内容执行remove()方法。
closable* Boolean true 是否显示右上角的关闭按钮。
buttons Array [] 弹框的按钮元素。默认是空数组,表示没有按钮。每个按钮为一个{}对象,含一些可选参数,具体释义参见这里
width String
Number
'auto'

标题弹框的主体宽度。可以是纯数值,会按照像素单位处理;也支持其他任意单位,例如'60vw''60%';也可以是任意的合法的width属性值,例如calc(100% - 2rem)var(--some-size)

默认值是'auto',表示宽度自适应。

height String
Number
'auto'

标题弹框的主体高度。可以是纯数值,会按照像素单位处理;也支持其他任意单位,例如'60vh''60%';也可以是任意的合法的height属性值,例如calc(100vh - 2rem)var(--some-size)

支持关键字'stretch',表示高度拉伸。

onShow* Function function () {} 弹框显示时候的回调。this为当前弹框元素,支持一个参数,为'show'事件对象。
onHide* Function function () {} 弹框隐藏时候的回调。this为当前弹框元素,支持一个参数,为'hide'事件对象。
onRemove* Function function () {} 弹框移除时候的回调。this为当前弹框元素,支持一个参数,为'remove'事件对象。

后面跟随*的参数,为Dialog()方法独有的可选参数。其他扩展方法,无法重置。


buttons参数

buttons参数的数组项支持的参数如下:
{
    type: '',
    value: '',
    form: '',
    for: '',
    className: '',
    events: {}
}

其中:

参数名称 支持类型 默认值 释义
type String 'primary'或'' 表示按钮的类型。和Button.css中按钮类型关键字对应。默认值与当前对象在buttons参数数组中的位置有关。第一个按钮对象,默认值是'primary',也就是蓝色按钮,也就是如果你的弹框只有一个按钮,默认是蓝色。之后的按钮,默认则是白色按钮,如白色的“取消”按钮。
value String '确定'或'取消' 表示按钮的文字内容。默认值与当前对象在buttons参数数组中的位置有关。如果在数组的第一个选项中,默认值是'确定',如果在后面的选项,则按钮默认值是'取消'
form String '' 表示按钮所属的表单元素的id,如果form属性值有设置,则按钮会自动变成'submit'类型,点击该按钮会触发对应表单元素的'submit'行为。
for String '' 如果设置此参数,弹框中的按钮会使用lable标签。此时,点击弹框按钮,会触发id值和此按钮for属性一致的控件元素的点击行为。此属性在一些特殊场景下,非常有用。例如,点击弹框按钮触发文件选择,又例如,点击弹框按钮触发页面中某个React按钮的执行。
disabled Boolean false 表示按钮是否处于禁用态。
className String '' 表示按钮上额外添加的类名。
events Object|Function {} 表示按钮绑定的事件。可选,如果不设置,会调用弹框关闭事件。

如果只有click事件,可以直接使用Function类型参数,例如:

events: function (event) {
  event.dialog.remove();
}

如果包含多个事件,则只能使用Object类型参数,例如:

events: {
  focus() {},
  blur() {}
}

因此:

1. 如果你的弹框默认就是一个文字为“确定”的确认按钮,则buttons参数内容就是:

{
    buttons: [{}]
}

2. 如果你的弹框默认就是一个蓝色确认按钮,但是文字内容是“我知道了”,则buttons参数内容就是:

{
    buttons: [{
        value: '我知道了'
    }]
}

3. 如果你的弹框默认就是一个文字为“确定”的确认按钮,但是是红色的警示按钮,则buttons参数内容就是:

{
    buttons: [{
        type: 'danger'
    }]
}

4. 如果你的弹框默认就有一个蓝色“确定”按钮,和一个白色“取消”按钮,则buttons参数内容就是:

{
    buttons: [{}, {}]
}

我们只需要空对象占位就好了。你也可以使用null占位。

5. form参数使用示意,点击下面的编辑小图标测试效果。

昵称:最帅最俊朗

相关代码如下:

昵称:<output>最帅最俊朗</output> <css-icon id="btnForm"></css-icon>
btnForm.addEventListener('click', function () {
    let eleOutput = this.previousElementSibling;
    new Dialog({
        title: '修改昵称',
        content: `<form id="editForm">
            姓名:<input value="${eleOutput.textContent}" name="nickname" required>
        </form>`,
        buttons: [{
            value: '修改',
            form: 'editForm'
        }],
        onShow: function () {
            this.querySelector('form').addEventListener('submit', event => {
                event.preventDefault();
                // 输入框内容赋值
                eleOutput.textContent = this.querySelector('[name="nickname"]').value;
                // 弹框关闭
                this.remove();
            });
        }
    });
});

参数的底层设置

Dialog类中options参数的设置在底层是使用dialog.setParams()这个方法实现的。

dialog.setParams(options)

dialog.setParams()这个方法底层是使用Object.assign()这个方法实现的。

Object.assign(dialog.params, options || {})

因此,如果想要同时设置多个参数,下面这两种用法是等同的:

<dialog id="dialog5" is="ui-dialog">Hello, LuLu UI!</dialog>
button5.addEventListener('click', function () {
    Object.assign(dialog5.show().params, {
        title: '我是标题',
        height: 400,
        buttons: [{
            value: '我是按钮'
        }]
    });
});

等同于:

button5.addEventListener('click', function () {
    dialog5.show().setParams({
        title: '我是标题',
        height: 400,
        buttons: [{
            value: '我是按钮'
        }]
    });
});
Hello, LuLu UI!

测试:

如果需要设置的参数就1个或者2个,则可以使用更底层的方法,直接改变<dialog>元素的params属性值。

例如使用dialog.params.title设置弹框的标题。

<dialog id="dialog6" is="ui-dialog">Hello, LuLu UI!</dialog>
button6.addEventListener('click', function () {
    dialog5.show().params.title = '我是标题-哇咔咔';
});
Hello, LuLu UI!

测试:

事件的底层处理

设置is="ui-dialog"<dialog>元素除了支持原生的'close'事件,还支持'show''hide''remove''DOMContentLoaded'事件。

这些事件的用法很简单,直接对<dialog>元素添加对应的监听即可,例如:

dialog.addEventListener('DOMContentLoaded', event => {
    console.log('弹框自定义属性和方法注册完毕了');                    
});
dialog.addEventListener('show', event => {
    console.log('弹框显示了');                    
});
dialog.addEventListener('hide', event => {
    console.log('弹框隐藏了');                    
});
dialog.addEventListener('remove', event => {
    console.log('弹框移除了');                    
});

Dialog类中options的可选参数包括onShowonHideonRemove,其底层逻辑就是使用的上面的事件监听。

实际上,onShow参数并不是必须的,例如下面两段JS代码作用其实是一样的:

new Dialog({
    content: `<form id="editForm"></form>`,
    buttons: [{ form: 'editForm' }]
    onShow: function () {
        this.querySelector('form').addEventListener('submit', event => {
            event.preventDefault();
            // 弹框关闭
            this.remove();
        });
    }
});

等同于(不推荐这么使用):

let eleDialog = new Dialog({
    content: `<form id="editForm"></form>`,
    buttons: [{ form: 'editForm' }]
});
eleDialog.querySelector('form').addEventListener('submit', event => {
    event.preventDefault();
    // 弹框关闭
    this.remove();
});

因为new Dialog()执行的时候,弹框元素已经在页面中实现了,通过onShow设置显示的回调其实有些多余了。

Alert弹框语法

模拟浏览器window.alert()弹框。用法如下:

dialog.alert(content, alertOptions);

其中,dialog可以通过new Dialog()方法构造,也可以使用原生JS语句在页面中创建<dialog>元素。例如下面这种常用的用法:

let eleDialog = new Dialog(options).alert(content, alertOptions);

其中:

options
可选。其中onShow, onHide, onRemove这3个参数只能在这里设置。
content
必须。提示的内容。支持HTML标签。
alertOptions
可选。具体参数值见下表:
参数名称 支持类型 默认值 释义
title String '' alert弹框默认无标题文字,本项目,此参数不需要关心。
type String 'remind' alert弹框的类型, 参数包括'remind', 'success', 'warning', 'danger', 或者任意自定义'custom'。对应效果图缩略图如下:

buttons Array [{}] 表示按钮。默认为1个蓝色“确定”按钮。

实际开发用到可选参数场景并不多,多类似这样:

new Dialog().alert('弹弹弹,弹走鱼尾纹……');

Confirm弹框语法

模拟浏览器window.confirm()弹框。语法如下:

dialog.confirm(content, alertOptions);

其中,dialog可以通过new Dialog()方法构造,也可以使用原生JS语句在页面中创建<dialog>元素。例如下面这种常用的用法:

let eleDialog = new Dialog(options).confirm(content, confirmOptions);

其中:

options
可选。其中onShow, onHide, onRemove这3个参数只能在这里设置。
content
必须。提示的内容。支持HTML标签。
confirmOptions
可选。具体参数值见下表:
参数名称 支持类型 默认值 释义
title String '' 弹框的标题文字,默认无标题。
type String 'danger' 弹框的类型, 参数包括'remind', 'success', 'danger', 或者任意自定义'custom'。同Alert弹框,差别在于图标。
buttons Array [{
  type: 'danger'
}, {}]
表示按钮。默认为1个红色警示“确定”按钮和1个普通浅色按钮。

一般而言,我们需要对第1个警示按钮写事件,也就是确认删除之后干嘛干嘛,如:

new Dialog().confirm('确定弹弹弹,弹走鱼尾纹?', {
    buttons: [{
        events: function(event) {
            /*
             * 这里回调,巴拉巴拉小魔仙……
             * ......
            */
            // event.dialog为弹框实例对象
            event.dialog.remove();
        }
    }, {}]
});

Loading弹框语法

语法如下:

let eleDialog = new Dialog(options).loading();

我们可以设定弹框的宽度以及各个回调。loading模式下,标题、关闭按钮、底部按钮都不可见,只有菊花转转转。此方法需要Loading.css,如果希望替换loading弹框内容,下面这些方法都是可以的:

dialog.content = newContent;
dialog.params.content = newContent;
dialog.setParams({
    content: newContent;
});

newContent可以是任意的字符串、DOM节点或者返回字符串或DOM节点的Function函数。

暴露的属性和方法

eleDialog就是<dialog>弹框元素,暴露了下面这些属性和方法:

{
    // 只读
    element: {
        // 含半透明遮罩的容器
        container: null,
        // 弹框主体元素
        dialog: null,
        // 弹框标题元素
        title: null,
        // 弹框关闭按钮
        close: null,
        // 弹框主内容元素
        body: null,
        // 弹框底部元素
        footer: null,
        // 弹框按钮1(如果有)
        button0: null,
        // 弹框按钮2(如果有)
        button1: null
    },
    // 可读可写
    params: {
        title: '',
        width: 'auto',
        height: 'auto',
        buttons: [],
        content: ''
    },
    // 参数设置
    setParams: function (options) {},
    // 一些格式化的方法
    alert: function () {},
    confirm: function () {},
    loading: function () {},

    // 弹框显示
    show: function () {},
    // 弹框隐藏
    hide: function () {},
    // 弹框移除
    remove: function () {}
}
本页贡献者:

zhangxinxu