作为一名开发者,大家应该都知道在浏览器中存在一些内置的控件:Alert,Confirm等,但是这些控件通常根据浏览器产商的不同而形态各异,视觉效果往往达不到UI设计师的要求。更重要的是,这类内置控件的风格很难与形形色色的各种风格迥异的互联网产品的设计风格统一。因此,优秀的前端开发者们各自开发自己的个性化控件来替代浏览器内置的这些控件。当然,这类组件在网络上已经有不计其数相当优秀的,写这篇文章的目的不是为了说明我开发的这个组件有多优秀,也不是为了炫耀什么,只是希望通过这种方式,与更多的开发者互相交流,互相学习,共同进步。好,废话不多说,言归正传。
功能介绍
取代浏览器自带的Alert、Confirm控件
自定义界面样式
使用方式与内置控件基本保持一致
效果预览
1、Alert控件
2、Confirm控件
3、完整代码,在线预览(见底部,提供压缩包下载)
开发过程
1. 组件结构设计
首先,我们来看下内置组件的基本使用方法:
1 alert("内置Alert控件");
2if (confirm("关闭内置Confirm控件?")) {
3 alert("True");
4 } else {
5 alert("False");
6 }
为了保证我们的组件使用方式和内置控件保持一致,所以我们必须考虑覆盖内置控件。考虑到组件开发的风格统一,易用,易维护,以及面向对象等特性,我计划将自定义的alert和confirm方法作为一个类(Winpop)的实例方法,最后用实例方法去覆盖系统内置控件的方法。为了达到目的,我的基本做法如下:
1var obj = new Winpop(); // 创建一个Winpop的实例对象2// 覆盖alert控件3window.alert = function(str) {
4 obj.alert.call(obj, str);
5 };
6// 覆盖confirm控件7window.confirm = function(str, cb) {
8 obj.confirm.call(obj, str, cb);
9 };
需要注意的是,由于浏览器内置的控件可以阻止浏览器的其他行为,而我们自定义的组件并不能具备这种能力,为了尽可能的做到统一,正如预览图上看到的,我们在弹出自定义组件的时候使用了一个全屏半透明遮罩层。也正是由于上述原因,confirm组件的使用方式也做了一些细微的调整,由内置返回布尔值的方式,改为使用回调函数的方式,以确保可以正确的添加“确定”和“取消”的逻辑。因此,自定义组件的使用方式就变成了下面这种形式:
1 alert("自定义Alert组件");
2 confirm("关闭自定义Confirm组件?", function(flag){
3if (flag) {
4 alert("True");
5 } else {
6 alert("False");
7 }
8 });
2. 组件代码设计
在正式介绍Winpop组件的代码之前,我们先来看一下一个Javascript组件的基本结构:
1 (function(window, undefined) {
2functionJsClassName(cfg) {
3var config = cfg || {};
4this.get = function(n) {
5return config[n];
6 }
7this.set = function(n, v) {
8 config[n] = v;
9 }
10this.init();
11 }
12 JsClassName.prototype = {
13 init: function(){},
14 otherMethod: function(){}
15 };
16window.JsClassName = window.JsClassName || JsClassName;
17 })(window);
使用一个自执行的匿名函数将我们的组件代码包裹起来,尽可能的减少全局污染,最后再将我们的类附到全局window对象上,这是一种比较推荐的做法。
构造函数中的get、set方法不是必须的,只是笔者的个人习惯而已,觉得这样写可以将配置参数和其他组件内部全局变量缓存和读取的调用方式统一,似乎也更具有面向对象的型。欢迎读者们说说各自的想法,说说这样写到底好不好。
接下来我们一起看下Winpop组件的完整代码:
1 (function(window, jQuery, undefined) {
23var HTMLS = {
4 ovl: '<div id="J_WinpopMask"></div>' + '<div id="J_WinpopBox">' + '<div></div>' + '<div></div>' + '</div>',
5 alert: '<input type="button" value="确定">',
6 confirm: '<input type="button" value="取消">' + '<input type="button" value="确定">'7 }
89functionWinpop() {
10var config = {};
11this.get = function(n) {
12return config[n];
13 }
1415this.set = function(n, v) {
16 config[n] = v;
17 }
18this.init();
19 }
2021 Winpop.prototype = {
22 init: function() {
23this.createDom();
24this.bindEvent();
25 },
26 createDom: function() {
27var body = jQuery("body"),
28 ovl = jQuery("#J_WinpopBox");
2930if (ovl.length === 0) {
31 body.append(HTMLS.ovl);
32 }
3334this.set("ovl", jQuery("#J_WinpopBox"));
35this.set("mask", jQuery("#J_WinpopMask"));
36 },
37 bindEvent: function() {
38var _this = this,
39 ovl = _this.get("ovl"),
40 mask = _this.get("mask");
41 ovl.on("click", ".J_AltBtn", function(e) {
42 _this.hide();
43 });
44 ovl.on("click", ".J_CfmTrue", function(e) {
45var cb = _this.get("confirmBack");
46 _this.hide();
47 cb && cb(true);
48 });
49 ovl.on("click", ".J_CfmFalse", function(e) {
50var cb = _this.get("confirmBack");
51 _this.hide();
52 cb && cb(false);
53 });
54 mask.on("click", function(e) {
55 _this.hide();
56 });
57 jQuery(document).on("keyup", function(e) {
58var kc = e.keyCode,
59 cb = _this.get("confirmBack");;
60if (kc === 27) {
61 _this.hide();
62 } elseif (kc === 13) {
63 _this.hide();
64if (_this.get("type") === "confirm") {
65