一个文笔一般,想到哪是哪的唯心论前端小白。
前言
没错,跟jQuery磕上了!
前两天无奈之下用jQuery写了个分页,今天又写了一个弹框组件。没有特殊的地方,就是弹框组件,和 el-dialog
是一模一样的。
效果大概是这样子的:
觉得可能在不知道什么时候可能用一下,所以做个文档记录一下吧!
思路
需求分析:
- 通过点击dom对象,或者触发js方法,触发 open 方法,打开弹框。
- 弹框自动添加 header 和 footer,并把业务放在 body 中。
- 支持按钮自定义,并可以绑定自定义方法。自定义方法执行完成以后关闭弹框。
使用方法设想
根据最近的开发习惯,dom结构当然是这样的最舒服了!
<div class="cm-dialog" id="dialogDemo"><p>hello world!</p></div><div class="cm-dialog" id="dialogDemo"> <p>hello world!</p> </div><div class="cm-dialog" id="dialogDemo"> <p>hello world!</p> </div>
即我们只关注业务内容,由插件动生成弹框的头部和底部。
然后是js结构则是这样的:
$(function () {const dialogDemo = $('#dialogDemo').initDialog({title: '查看详情',cancel: {label: '取消',handler: function () {console.log('触发了取消');return true;}},submit: {label: '确定',handler: function () {console.log('触发了确定');return true;}}})$('#button').on('click', function () {dialogDemo.trigger('open')})})$(function () { const dialogDemo = $('#dialogDemo').initDialog({ title: '查看详情', cancel: { label: '取消', handler: function () { console.log('触发了取消'); return true; } }, submit: { label: '确定', handler: function () { console.log('触发了确定'); return true; } } }) $('#button').on('click', function () { dialogDemo.trigger('open') }) })$(function () { const dialogDemo = $('#dialogDemo').initDialog({ title: '查看详情', cancel: { label: '取消', handler: function () { console.log('触发了取消'); return true; } }, submit: { label: '确定', handler: function () { console.log('触发了确定'); return true; } } }) $('#button').on('click', function () { dialogDemo.trigger('open') }) })
可以看到,针对jQuery扩展了一个名为 initDialog
的方法,传入配置内容,配置内容包含常用的 title 和两个按钮,分别对应【取消】按钮和【确定】按钮,并绑定两个方法。
关键是最后要使用一个变量来记录这个初始化以后的 dialog 对象,可以使用 trigger 方法去触发内部的 open 方法,打开弹框。
坑点补充
以上这个方案里面有几个点需要注意:
- 如果按钮绑定的方法抛出异常,需要阻止关闭,所以这个方法应该有一个 Boolearn 类型的返回值。
- 如果只有一个按钮或者多个按钮,则需要动态的去渲染按钮。
- body 部分应该有个最大高度,避免超出可视范围的尴尬局面。
开发
开发这块比较简单,提前声明一下,上面三个坑点只有第三个做了,前面两个没做!
意不意外? 但是还会把思路写出来。
开发过程中因为没想明白怎么使用模版把 头部和底部 使用模板渲染出来,一度想着在 cm-dialog 里面再套一层,然后再向里面添加 htm 的方法,后来被 pass 掉了。
最终的方案是这样子的:
$.fn.extend({initDialog: function (conf) {var inner = $(this).html();// 解析 configvar title = conf.title || "标题";var cancel = conf.cancel || null;var submit = conf.submit || null;// 模板var htm = `<div class="cm-dialog-wrap"><div class="cm-dialog-header"><span>${title}</span></div><div class="cm-dialog-body">${inner}</div><div class="cm-dialog-footer"><button class="cm-button-cancel">${cancel.label}</button><button class="cm-button-submit">${submit.label}</button></div></div>`;// 初始化 dialog$(this).html(htm);$(this).hide();// 绑定 open 方法$(this).bind("open", function () {$(this).fadeIn();});// 绑定 close 方法$(this).bind("close", function () {$(this).fadeOut();});// 绑定按钮事件$(".cm-button-cancel").on("click", function () {if(cancel.handler && cancel.handler()){$(this).trigger("close");};});$(".cm-button-submit").on("click", function () {if(submit.handler && submit.handler()){$(this).trigger("close");}});// 返回 dialogreturn $(this);},});$.fn.extend({ initDialog: function (conf) { var inner = $(this).html(); // 解析 config var title = conf.title || "标题"; var cancel = conf.cancel || null; var submit = conf.submit || null; // 模板 var htm = ` <div class="cm-dialog-wrap"> <div class="cm-dialog-header"> <span>${title}</span> </div> <div class="cm-dialog-body"> ${inner} </div> <div class="cm-dialog-footer"> <button class="cm-button-cancel">${cancel.label}</button> <button class="cm-button-submit">${submit.label}</button> </div> </div> `; // 初始化 dialog $(this).html(htm); $(this).hide(); // 绑定 open 方法 $(this).bind("open", function () { $(this).fadeIn(); }); // 绑定 close 方法 $(this).bind("close", function () { $(this).fadeOut(); }); // 绑定按钮事件 $(".cm-button-cancel").on("click", function () { if(cancel.handler && cancel.handler()){ $(this).trigger("close"); }; }); $(".cm-button-submit").on("click", function () { if(submit.handler && submit.handler()){ $(this).trigger("close"); } }); // 返回 dialog return $(this); }, });$.fn.extend({ initDialog: function (conf) { var inner = $(this).html(); // 解析 config var title = conf.title || "标题"; var cancel = conf.cancel || null; var submit = conf.submit || null; // 模板 var htm = ` <div class="cm-dialog-wrap"> <div class="cm-dialog-header"> <span>${title}</span> </div> <div class="cm-dialog-body"> ${inner} </div> <div class="cm-dialog-footer"> <button class="cm-button-cancel">${cancel.label}</button> <button class="cm-button-submit">${submit.label}</button> </div> </div> `; // 初始化 dialog $(this).html(htm); $(this).hide(); // 绑定 open 方法 $(this).bind("open", function () { $(this).fadeIn(); }); // 绑定 close 方法 $(this).bind("close", function () { $(this).fadeOut(); }); // 绑定按钮事件 $(".cm-button-cancel").on("click", function () { if(cancel.handler && cancel.handler()){ $(this).trigger("close"); }; }); $(".cm-button-submit").on("click", function () { if(submit.handler && submit.handler()){ $(this).trigger("close"); } }); // 返回 dialog return $(this); }, });
其实结构很简单,主要是一个思路,就是在方法的一开始,将 inner 提出来,然后放在模板中。这样就实现了dom结构很简单,但是后面会包含头部和底部的开发方式。
然后就是剩余的两个坑点的处理方式:
- 其实已经解决了,返回值为 true 和false 的事情,但是如果是异步方法需要注意了,可以使用 trigger 去触发 close 方法。
- 传入按钮改为一个数组,使用map函数将数组转换成dom结构,并重新绑定一下方法就好了。
代码分享
主要分享三个文件:
dialog.extends.js
$.fn.extend({initDialog: function (conf) {var inner = $(this).html();// 解析 configvar title = conf.title || "标题";var cancel = conf.cancel || null;var submit = conf.submit || null;// 模板var htm = `<div class="cm-dialog-wrap"><div class="cm-dialog-header"><span>${title}</span></div><div class="cm-dialog-body">${inner}</div><div class="cm-dialog-footer"><button class="cm-button-cancel">${cancel.label}</button><button class="cm-button-submit">${submit.label}</button></div></div>`;// 初始化 dialog$(this).html(htm);$(this).hide();// 绑定 open 方法$(this).bind("open", function () {$(this).fadeIn();});// 绑定 close 方法$(this).bind("close", function () {$(this).fadeOut();});// 绑定按钮事件$(".cm-button-cancel").on("click", function () {if(cancel.handler && cancel.handler()){$(this).trigger("close");};});$(".cm-button-submit").on("click", function () {if(submit.handler && submit.handler()){$(this).trigger("close");}});// 返回 dialogreturn $(this);},});$.fn.extend({ initDialog: function (conf) { var inner = $(this).html(); // 解析 config var title = conf.title || "标题"; var cancel = conf.cancel || null; var submit = conf.submit || null; // 模板 var htm = ` <div class="cm-dialog-wrap"> <div class="cm-dialog-header"> <span>${title}</span> </div> <div class="cm-dialog-body"> ${inner} </div> <div class="cm-dialog-footer"> <button class="cm-button-cancel">${cancel.label}</button> <button class="cm-button-submit">${submit.label}</button> </div> </div> `; // 初始化 dialog $(this).html(htm); $(this).hide(); // 绑定 open 方法 $(this).bind("open", function () { $(this).fadeIn(); }); // 绑定 close 方法 $(this).bind("close", function () { $(this).fadeOut(); }); // 绑定按钮事件 $(".cm-button-cancel").on("click", function () { if(cancel.handler && cancel.handler()){ $(this).trigger("close"); }; }); $(".cm-button-submit").on("click", function () { if(submit.handler && submit.handler()){ $(this).trigger("close"); } }); // 返回 dialog return $(this); }, });$.fn.extend({ initDialog: function (conf) { var inner = $(this).html(); // 解析 config var title = conf.title || "标题"; var cancel = conf.cancel || null; var submit = conf.submit || null; // 模板 var htm = ` <div class="cm-dialog-wrap"> <div class="cm-dialog-header"> <span>${title}</span> </div> <div class="cm-dialog-body"> ${inner} </div> <div class="cm-dialog-footer"> <button class="cm-button-cancel">${cancel.label}</button> <button class="cm-button-submit">${submit.label}</button> </div> </div> `; // 初始化 dialog $(this).html(htm); $(this).hide(); // 绑定 open 方法 $(this).bind("open", function () { $(this).fadeIn(); }); // 绑定 close 方法 $(this).bind("close", function () { $(this).fadeOut(); }); // 绑定按钮事件 $(".cm-button-cancel").on("click", function () { if(cancel.handler && cancel.handler()){ $(this).trigger("close"); }; }); $(".cm-button-submit").on("click", function () { if(submit.handler && submit.handler()){ $(this).trigger("close"); } }); // 返回 dialog return $(this); }, });
dialog.extends.css
需要注意,这里只包含了 dialog 相关的样式,需要配合全局样式去使用。
类似:*{ padding :0; margin: 0}
.cm-dialog {height: 100%;width: 100%;position: fixed;z-index: 999;top: 0;left: 0;display: flex;flex-direction: row;align-items: center;justify-content: center;background: rgba(48, 42, 42, 0.5);}.cm-dialog-wrap {height: auto;width: 960px;background: #fff;box-shadow: 0 0 10px #000;position: relative;overflow: hidden;}.cm-dialog-header {position: absolute;top: 0;left: 0;right: 0;padding: 5px 20px;background: #ebecee;height: 40px;line-height: 40px;}.cm-dialog-body {padding: 70px 20px 70px 20px;max-height: 500px;overflow-y: auto;overflow-x: hidden;}.cm-dialog-footer {position: absolute;bottom: 0;background: #ebecee;padding: 5px 20px;left: 0;right: 0;line-height: 40px;text-align: right;}.cm-dialog-footer button {padding: 6px 16px;background: skyblue;border: none;outline: none;border-radius: 4px;cursor: pointer;transition: background linear 0.1s;}/* .cm-dialog-footer button.cm-button-submit {background: red;} */.cm-dialog-footer button:hover {background: rgb(58, 182, 231);}.cm-dialog { height: 100%; width: 100%; position: fixed; z-index: 999; top: 0; left: 0; display: flex; flex-direction: row; align-items: center; justify-content: center; background: rgba(48, 42, 42, 0.5); } .cm-dialog-wrap { height: auto; width: 960px; background: #fff; box-shadow: 0 0 10px #000; position: relative; overflow: hidden; } .cm-dialog-header { position: absolute; top: 0; left: 0; right: 0; padding: 5px 20px; background: #ebecee; height: 40px; line-height: 40px; } .cm-dialog-body { padding: 70px 20px 70px 20px; max-height: 500px; overflow-y: auto; overflow-x: hidden; } .cm-dialog-footer { position: absolute; bottom: 0; background: #ebecee; padding: 5px 20px; left: 0; right: 0; line-height: 40px; text-align: right; } .cm-dialog-footer button { padding: 6px 16px; background: skyblue; border: none; outline: none; border-radius: 4px; cursor: pointer; transition: background linear 0.1s; } /* .cm-dialog-footer button.cm-button-submit { background: red; } */ .cm-dialog-footer button:hover { background: rgb(58, 182, 231); }.cm-dialog { height: 100%; width: 100%; position: fixed; z-index: 999; top: 0; left: 0; display: flex; flex-direction: row; align-items: center; justify-content: center; background: rgba(48, 42, 42, 0.5); } .cm-dialog-wrap { height: auto; width: 960px; background: #fff; box-shadow: 0 0 10px #000; position: relative; overflow: hidden; } .cm-dialog-header { position: absolute; top: 0; left: 0; right: 0; padding: 5px 20px; background: #ebecee; height: 40px; line-height: 40px; } .cm-dialog-body { padding: 70px 20px 70px 20px; max-height: 500px; overflow-y: auto; overflow-x: hidden; } .cm-dialog-footer { position: absolute; bottom: 0; background: #ebecee; padding: 5px 20px; left: 0; right: 0; line-height: 40px; text-align: right; } .cm-dialog-footer button { padding: 6px 16px; background: skyblue; border: none; outline: none; border-radius: 4px; cursor: pointer; transition: background linear 0.1s; } /* .cm-dialog-footer button.cm-button-submit { background: red; } */ .cm-dialog-footer button:hover { background: rgb(58, 182, 231); }
dialog-demo.html
就是展示一下怎么用。
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><link rel="stylesheet" href="./index.css"><link rel="stylesheet" href="./dialog.extend.css"></head><body><button id="button">Click Me!</button><div class="cm-dialog" id="dialogDemo"><p>hello world!</p></div></body><script src="./jquery.js"></script><script src="./dialog.extends.js"></script><script>$(function () {const dialogDemo = $('#dialogDemo').initDialog({title: '查看详情',cancel: {label: '取消',handler: function () {console.log('触发了取消');}},submit: {label: '确定',handler: function () {console.log('触发了确定');}}})$('#button').on('click', function () {dialogDemo.trigger('open')})})</script></html><!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <link rel="stylesheet" href="./index.css"> <link rel="stylesheet" href="./dialog.extend.css"> </head> <body> <button id="button">Click Me!</button> <div class="cm-dialog" id="dialogDemo"> <p>hello world!</p> </div> </body> <script src="./jquery.js"></script> <script src="./dialog.extends.js"></script> <script> $(function () { const dialogDemo = $('#dialogDemo').initDialog({ title: '查看详情', cancel: { label: '取消', handler: function () { console.log('触发了取消'); } }, submit: { label: '确定', handler: function () { console.log('触发了确定'); } } }) $('#button').on('click', function () { dialogDemo.trigger('open') }) }) </script> </html><!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <link rel="stylesheet" href="./index.css"> <link rel="stylesheet" href="./dialog.extend.css"> </head> <body> <button id="button">Click Me!</button> <div class="cm-dialog" id="dialogDemo"> <p>hello world!</p> </div> </body> <script src="./jquery.js"></script> <script src="./dialog.extends.js"></script> <script> $(function () { const dialogDemo = $('#dialogDemo').initDialog({ title: '查看详情', cancel: { label: '取消', handler: function () { console.log('触发了取消'); } }, submit: { label: '确定', handler: function () { console.log('触发了确定'); } } }) $('#button').on('click', function () { dialogDemo.trigger('open') }) }) </script> </html>
后记
思路很简单,代码量也很少,关键就是一个将原模板里面的内容重写一遍的思路。
有所思必有所得:
- 一个开箱即用的 dialog jQuery 插件。
- 一个 jQuery 或者原生代码中使用模板开发的开发思路。
- MVVM 模式的浅显探索?