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

文档 Github➹

Dialog模态层弹框

安装与调用

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

<link rel="stylesheet" href="https://qidian.gtimg.com/lulu/hope/ui/Dialog/index.css">
<script type="module" src="https://qidian.gtimg.com/lulu/hope/ui/Dialog/index.js"></script>

或者:

<script type="module">
    import Dialog from "https://qidian.gtimg.com/lulu/edge/js/common/ui/Dialog.js";
</script>

如果需要用到 Alert 或者 Confirm 弹框,则还需要引入 Button/index.css:

<link rel="stylesheet" href="https://qidian.gtimg.com/lulu/hope/ui/Button/index.css">

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

<link rel="stylesheet" href="https://qidian.gtimg.com/lulu/hope/ui/Loading/index.css">

概要

Hope 主题和 Edge 主题中的 Dialog 组件实现原理类似,可以理解为对 HTML5 原生的 <dialog> 元素进行的扩展,有别于 peak、pure 主题中原型组件。

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

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

例如:

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

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

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

不过实际开发通常不会这么使用,因为在移动端,结构提前预置的弹出式交互多使用 Popup 弹出层实现,Dialog 弹框更多的是模拟原生的 alertconfirm 效果,而这两个交互效果的执行普遍出现在 JS 业务逻辑代码中,因此,Dialog 组件更常见的是使用 JS ,也就是 Dialog 类进行构建。

Dialog 类使用指南

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

1. 起步

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

new Dialog()

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

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

const eleDialog = new Dialog()

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

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

当然,也可以级联使用:

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

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

2. alert 信息提示框

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

我们可以type参数指定提示的类型,除了这里使用的 'success' 类型,其他内置类型还有:'remind'提示,'danger'警示。

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

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

3. confirm 确认提示框

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

同时示意了通过使用 buttons 参数指定按钮的文案、类型以及事件。

4. 普通的内容弹框

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

5. 高度拉伸弹框

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

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: [{}]
});

6. loading 弹框

new Dialog().loading();

有时候,弹框内容是 Ajax 动态请求的,此时在 Ajax 成功之前是需要一个加载状态的。

体验更好的做法是根据最终呈现布局,局部动态 loading,但是有时候我们想要偷懒,不想再写一个 loading 布局,则就可以使用这里的 loading 弹框。例如下面这个示意:

const eleDialog = new Dialog({
    width: 288
}).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')
    });
});

这里使用 setParams 方法传参,让 loading 弹框替换成内容弹框。

7. 弹框打开 DOM 元素

<p id="t0" class="dn">
    下拉:<select style="width:150px;padding:.5rem;">
        <option>选项1</option>
        <option>选项2</option>
    </select>
</p>
button.addEventListener('click', function () {
    // 如果弹框已创建,就直接显示
    if (this.dialog) {
        this.dialog.show();
    } else {
        this.dialog = new Dialog({
            title: 'DOM载入测试',
            content: t0,
            buttons: [{}, {}]
        });
    }
});

下拉:

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

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

接下来深入介绍下弹框组件的实现原理、语法和参数等。

弹框的显示、隐藏

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

  1. dialog.open = true;
  2. dialog.setAttribute('open', '');
  3. dialog.show();   // 推荐,自定义若干细节

弹框隐藏有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="dia" is="ui-dialog"></dialog>');
dia.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="我是标题">Hello, LuLu UI!</dialog>

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

dialog2.show();
Hello, LuLu UI!

其中,<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 类的基础语法

基本的弹框使用方法为:

const eleDialog = new Dialog(options);

其中:

options
options是可选参数,支持的参数见下表:
参数名称 支持类型 默认值 释义
title String '' 表示弹框的标题。
content String
Object
Function
'' 表示弹框显示的主体内容。可以是 HTML 字符串,或者是 DOM 对象,也可以是返回 HTML 字符串或 DOM 对象的函数。参数类型不同,弹框关闭的方法也不一样,DOM 对象关闭执行 hide() 方法,HTML 内容执行 remove() 方法。
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: '',
    className: '',
    events: {}
}

其中:

参数名称 支持类型 默认值 释义
type String 'primary'或'' 表示按钮的类型。和Button.css中按钮类型关键字对应。默认值与当前对象在buttons参数数组中的位置有关。第一个按钮对象,默认值是'primary',也就是蓝色按钮,也就是如果你的弹框只有一个按钮,默认是蓝色。之后的按钮,默认则是白色按钮,如白色的“取消”按钮。
value String '确定'或'取消' 表示按钮的文字内容。默认值与当前对象在buttons参数数组中的位置有关。如果在数组的第一个选项中,默认值是'确定',如果在后面的选项,则按钮默认值是'取消'
form String '' 表示按钮所属的表单元素的id,如果form属性值有设置,则按钮会自动变成'submit'类型,点击该按钮会触发对应表单元素的'submit'行为。
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占位。

按钮中的 form 参数

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

昵称:最帅最俊朗

昵称:<output>最帅最俊朗</output>
<css-icon id="b1"></css-icon>
b1.addEventListener('click', function () {
    const op = this.previousElementSibling;
    new Dialog({
        title: '修改昵称',
        content: `<form id="f0">
            姓名:<input value="${op.textContent}" required>
        </form>`,
        buttons: [{
            value: '修改',
            form: 'f0'
        }],
        onShow: function () {
            f0.addEventListener('submit', event => {
                event.preventDefault();
                // 输入框内容赋值
                op.textContent = f0.querySelector('input').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>
Object.assign(dialog5.show().params, {
    title: '我是标题',
    height: 200,
    buttons: [{
        value: '我是按钮'
    }]
});

等同于:

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

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

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

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

事件的底层处理

设置 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="f1"></form>`,
    buttons: [{ form: 'f1' }]
    onShow: function () {
        f1.addEventListener('submit', event => {
            event.preventDefault();
            // 弹框关闭
            this.remove();
        });
    }
});
const dialog = new Dialog({
    content: `<form id="f1"></form>`,
    buttons: [{ form: 'f1' }]
});
f1.addEventListener('submit', event => {
    event.preventDefault();
    // 弹框关闭
    dialog.remove();
});

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

Alert 弹框语法

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

dialog.alert(content, alertOptions);

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

const 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> 元素。例如下面这种常用的用法:

const dialog = 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 个红色警示“确定”按钮。

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

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

Loading 弹框语法

语法如下:

const dialog = new Dialog(options).loading();

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

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

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

暴露的属性和方法

<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