作为一名开发者,大家应该都知道在浏览器中存在一些内置的控件:Alert,Confirm等,但是这些控件通常根据浏览器产商的不同而形态各异,视觉效果往往达不到UI设计师的要求。更重要的是,这类内置控件的风格很难与形形色色的各种风格迥异的互联网产品的设计风格统一。因此,优秀的前端开发者们各自开发自己的个性化控件来替代浏览器内置的这些控件。当然,这类组件在网络上已经有不计其数相当优秀的,写这篇文章的目的不是为了说明我开发的这个组件有多优秀,也不是为了炫耀什么,只是希望通过这种方式,与更多的开发者互相交流,互相学习,共同进步。好,废话不多说,言归正传。

功能介绍

  • 取代浏览器自带的Alert、Confirm控件
  • 自定义界面样式
  • 使用方式与内置控件基本保持一致

效果预览

1、Alert控件

2、Confirm控件

3、完整代码,在线预览(见底部,提供压缩包下载)

开发过程

1. 组件结构设计

首先,我们来看下内置组件的基本使用方法:

  1. alert("内置Alert控件");
  2. if (confirm("关闭内置Confirm控件?")) {
  3. alert("True");
  4. } else {
  5. alert("False");
  6. }

为了保证我们的组件使用方式和内置控件保持一致,所以我们必须考虑覆盖内置控件。考虑到组件开发的风格统一,易用,易维护,以及面向对象等特性,我计划将自定义的alert和confirm方法作为一个类(Winpop)的实例方法,最后用实例方法去覆盖系统内置控件的方法。为了达到目的,我的基本做法如下:

  1. var obj = new Winpop(); // 创建一个Winpop的实例对象
  2. // 覆盖alert控件
  3. window.alert = function(str) {
  4. obj.alert.call(obj, str);
  5. };
  6. // 覆盖confirm控件
  7. window.confirm = function(str, cb) {
  8. obj.confirm.call(obj, str, cb);
  9. };

需要注意的是,由于浏览器内置的控件可以阻止浏览器的其他行为,而我们自定义的组件并不能具备这种能力,为了尽可能的做到统一,正如预览图上看到的,我们在弹出自定义组件的时候使用了一个全屏半透明遮罩层。也正是由于上述原因,confirm组件的使用方式也做了一些细微的调整,由内置返回布尔值的方式,改为使用回调函数的方式,以确保可以正确的添加“确定”和“取消”的逻辑。因此,自定义组件的使用方式就变成了下面这种形式:

  1. alert("自定义Alert组件");
  2. confirm("关闭自定义Confirm组件?", function(flag){
  3. if (flag) {
  4. alert("True");
  5. } else {
  6. alert("False");
  7. }
  8. });
2. 组件代码设计

在正式介绍Winpop组件的代码之前,我们先来看一下一个Javascript组件的基本结构:

  1. (function(window, undefined) {
  2. function JsClassName(cfg) {
  3. var config = cfg || {};
  4. this.get = function(n) {
  5. return config[n];
  6. }
  7. this.set = function(n, v) {
  8. config[n] = v;
  9. }
  10. this.init();
  11. }
  12. JsClassName.prototype = {
  13. init: function(){},
  14. otherMethod: function(){}
  15. };
  16. window.JsClassName = window.JsClassName || JsClassName;
  17. })(window);

使用一个自执行的匿名函数将我们的组件代码包裹起来,尽可能的减少全局污染,最后再将我们的类附到全局window对象上,这是一种比较推荐的做法。

构造函数中的get、set方法不是必须的,只是笔者的个人习惯而已,觉得这样写可以将配置参数和其他组件内部全局变量缓存和读取的调用方式统一,似乎也更具有面向对象的型。欢迎读者们说说各自的想法,说说这样写到底好不好。

接下来我们一起看下Winpop组件的完整代码:

  1. (function(window, jQuery, undefined) {
  2.  
  3. var HTMLS = {
  4. ovl: '<div class="J_WinpopMask winpop-mask" id="J_WinpopMask"></div>' + '<div class="J_WinpopBox winpop-box" id="J_WinpopBox">' + '<div class="J_WinpopMain winpop-main"></div>' + '<div class="J_WinpopBtns winpop-btns"></div>' + '</div>',
  5. alert: '<input type="button" class="J_AltBtn pop-btn alert-button" value="确定">',
  6. confirm: '<input type="button" class="J_CfmFalse pop-btn confirm-false" value="取消">' + '<input type="button" class="J_CfmTrue pop-btn confirm-true" value="确定">'
  7. }
  8.  
  9. function Winpop() {
  10. var config = {};
  11. this.get = function(n) {
  12. return config[n];
  13. }
  14.  
  15. this.set = function(n, v) {
  16. config[n] = v;
  17. }
  18. this.init();
  19. }
  20.  
  21. Winpop.prototype = {
  22. init: function() {
  23. this.createDom();
  24. this.bindEvent();
  25. },
  26. createDom: function() {
  27. var body = jQuery("body"),
  28. ovl = jQuery("#J_WinpopBox");
  29.  
  30. if (ovl.length === 0) {
  31. body.append(HTMLS.ovl);
  32. }
  33.  
  34. this.set("ovl", jQuery("#J_WinpopBox"));
  35. this.set("mask", jQuery("#J_WinpopMask"));
  36. },
  37. bindEvent: function() {
  38. var _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) {
  45. var cb = _this.get("confirmBack");
  46. _this.hide();
  47. cb && cb(true);
  48. });
  49. ovl.on("click", ".J_CfmFalse", function(e) {
  50. var 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) {
  58. var kc = e.keyCode,
  59. cb = _this.get("confirmBack");;
  60. if (kc === 27) {
  61. _this.hide();
  62. } else if (kc === 13) {
  63. _this.hide();
  64. if (_this.get("type") === "confirm") {
  65. cb && cb(true);
  66. }
  67. }
  68. });
  69. },
  70. alert: function(str, btnstr) {
  71. var str = typeof str === 'string' ? str : str.toString(),
  72. ovl = this.get("ovl");
  73. this.set("type", "alert");
  74. ovl.find(".J_WinpopMain").html(str);
  75. if (typeof btnstr == "undefined") {
  76. ovl.find(".J_WinpopBtns").html(HTMLS.alert);
  77. } else {
  78. ovl.find(".J_WinpopBtns").html(btnstr);
  79. }
  80. this.show();
  81. },
  82. confirm: function(str, callback) {
  83. var str = typeof str === 'string' ? str : str.toString(),
  84. ovl = this.get("ovl");
  85. this.set("type", "confirm");
  86. ovl.find(".J_WinpopMain").html(str);
  87. ovl.find(".J_WinpopBtns").html(HTMLS.confirm);
  88. this.set("confirmBack", (callback || function() {}));
  89. this.show();
  90. },
  91. show: function() {
  92. this.get("ovl").show();
  93. this.get("mask").show();
  94. },
  95. hide: function() {
  96. var ovl = this.get("ovl");
  97. ovl.find(".J_WinpopMain").html("");
  98. ovl.find(".J_WinpopBtns").html("");
  99. ovl.hide();
  100. this.get("mask").hide();
  101. },
  102. destory: function() {
  103. this.get("ovl").remove();
  104. this.get("mask").remove();
  105. delete window.alert;
  106. delete window.confirm;
  107. }
  108. };
  109.  
  110. var obj = new Winpop();
  111. window.alert = function(str) {
  112. obj.alert.call(obj, str);
  113. };
  114. window.confirm = function(str, cb) {
  115. obj.confirm.call(obj, str, cb);
  116. };
  117. })(window, jQuery);

代码略多,关键做以下几点说明:

  • 笔者偷了懒,使用了jQuery,使用之前请先保证已经引入了jQuery
  • 自定义组件结构最终是追加到body中的,所以在引入以上js之前,请先确保文档已经加载完成
  • 组件添加了按ESC、点遮罩层隐藏组件功能
  • 注意:虽然本例中未用到 destory 方法,但读者朋友可以注意一下该方法中的 delete window.alert 和 delete window.confirm ,这样写的目的是保证在自定义组件销毁后,将Alert、Confirm控件恢复到浏览器内置效果
  • 组件最后如果加上 window.Winpop = Winpop ,就可以将对象全局化供其他类调用了

最后

作为一个前端开发工程师,个人觉得Javascript组件开发是一件很有意思的事情,其中乐趣只有自己亲自动手尝试了才会体会得到。前端组件开发往往需要Javascript、CSS和html相互配合,才能事半功倍,上面提到的Winpop也不例外,这里给大家提供一个完整的demo压缩包,有兴趣的读者朋友,欢迎传播。

作者博客:百码山庄

开发一个完整的JavaScript组件的更多相关文章

  1. php怎么做网站?如何用PHP开发一个完整的网站?

    1.PHPer应具备的知识 (1)PHP知识: 熟练掌握基础函数,PHP语句(条件.循环),数组(排序.读取),函数(内部 构造),运算(数学 逻辑),面向对象(继承 接口 封装 多态静态属性)等. ...

  2. 【如何快速的开发一个完整的iOS直播app】(美颜篇)

    原文转自:袁峥Seemygo    感谢分享.自我学习 前言 在看这篇之前,如果您还不了解直播原理,请查看这篇文章如何快速的开发一个完整的iOS直播app(原理篇) 开发一款直播app,美颜功能是很重 ...

  3. 【如何快速的开发一个完整的iOS直播app】(采集篇)

    原文转自:袁峥Seemygo    感谢分享.自我学习 前言 在看这篇之前,如果您还不了解直播原理,请查看这篇文章如何快速的开发一个完整的iOS直播app(原理篇) 开发一款直播app,首先需要采集主 ...

  4. 【如何快速的开发一个完整的iOS直播app】(播放篇)

    原文转自:袁峥Seemygo    感谢分享.自我学习 前言 在看这篇之前,如果您还不了解直播原理,请查看上篇文章如何快速的开发一个完整的iOS直播app(原理篇) 开发一款直播app,集成ijkpl ...

  5. 【如何快速的开发一个完整的iOS直播app】(原理篇)

    原文转自:袁峥Seemygo    感谢分享.自我学习 目录 [如何快速的开发一个完整的iOS直播app](原理篇) [如何快速的开发一个完整的iOS直播app](播放篇) [如何快速的开发一个完整的 ...

  6. 【如何快速的开发一个完整的iOS直播app】(推流篇)

    前言 在看这篇之前,如果您还不了解直播原理,请查看这篇文章如何快速的开发一个完整的iOS直播app(原理篇) 开发一款直播app,肯定需要流媒体服务器,本篇主要讲解直播中流媒体服务器搭建,并且讲解了如 ...

  7. 【如何快速的开发一个完整的 iOS 直播 app】(美颜篇)

    来源:袁峥Seemygo 链接:http://www.jianshu.com/p/4646894245ba 前言 在看这篇之前,如果您还不了解直播原理,请查看这篇文章如何快速的开发一个完整的iOS直播 ...

  8. 如何快速的开发一个完整的iOS直播app(原理篇)

    目录 [如何快速的开发一个完整的iOS直播app](原理篇) [如何快速的开发一个完整的iOS直播app](播放篇) [如何快速的开发一个完整的iOS直播app](采集篇) 前言 大半年没写博客了,但 ...

  9. 如何快速的开发一个完整的iOS直播app(美颜篇)

    前言 在看这篇之前,如果您还不了解直播原理,请查看这篇文章如何快速的开发一个完整的iOS直播app(原理篇) 开发一款直播app,美颜功能是很重要的,如果没有美颜功能,可能分分钟钟掉粉千万,本篇主要讲 ...

随机推荐

  1. sqoop的安装和使用

    在sqoop使用前,应先安装好hive和zookeeper,还要在一台虚拟机里安装好mysql 1.先将zookeeper启动:zkServer.sh start,集群启动起来:start-all.s ...

  2. 【CSS】Beginner5:Margins&Padding

    1.Properties for spacing-out elements 外边距:A margin is the space space outside something 内边距:padding ...

  3. HDU 1117 免费馅饼 二维动态规划

    思路:a[i][j]表示j秒在i位置的数目,dp[i][j]表示j秒在i位置最大可以收到的数目. 转移方程:d[i][j]=max(dp[i-1][j],dp[i-1][j-1],dp[i-1][j+ ...

  4. Weka 入门3

    这次我们介绍Evaluation类.在上一次中我们只是单纯的预测了分类值,并没有其他评价数据.这场我们使用Evalution类.首先初始化一个Evaluation对象,Evaluation类没有无参的 ...

  5. HW3.21

    import java.util.Scanner; public class Solution { public static void main(String[] args) { Scanner i ...

  6. 恢复oracle中误删除drop掉的表

    查看回收站中表 select object_name,original_name,partition_name,type,ts_name,createtime,droptime from recycl ...

  7. fzu2109--Mountain Number(数位dp)

     Problem Description One integer number x is called "Mountain Number" if: (1) x>0 and x ...

  8. 北京Uber优步司机奖励政策(2月6日)

    滴快车单单2.5倍,注册地址:http://www.udache.com/ 如何注册Uber司机(全国版最新最详细注册流程)/月入2万/不用抢单:http://www.cnblogs.com/mfry ...

  9. A Tour of Go For continued

    As in C or Java, you can leave the pre and post statements empty. package main import "fmt" ...

  10. javascript获取元素的计算样式

    使用css控制页面有4种方式,分别为行内样式(内联样式).内嵌式.链接式.导入式. 行内样式(内联样式)即写在html标签中的style属性中,如<div style="width:1 ...