全程记录开发一个完整的JavaScript组件教程

在浏览器中存在一些内置的控件 Alert,Confirm等,但是这些控件通常根据浏览器产商的不同而形态各异,视觉效果往往达不到UI设计师的要求。更重要的是,这类内置控件的风格很难与形形色色的各种风格迥异的互联网产品的设计风格统一。

因此,优秀的前端开发者们各自开发自己的个性化控件来替代浏览器内置的这些控件。当然,这类组件在网络上已经有不计其数相当优秀的,写这篇文章的目的是为了通过这种方式,与更多的开发者互相交流,互相学习,共同进步。

 功能介绍

    取代浏览器自带的Alert、Confirm控件

    自定义界面样式

    使用方式与内置控件基本保持一致

效果预览

1、Alert控件

2、Confirm控件

  开发过程

1. 组件结构设计

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

 代码如下 复制代码
alert("内置Alert控件");
if (confirm("关闭内置Confirm控件?")) {
    alert("True");
} else {
    alert("False");
}

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

 代码如下 复制代码
var obj = new Winpop(); // 创建一个Winpop的实例对象
// 覆盖alert控件
window.alert = function(str) {
    obj.alert.call(obj, str);
};
// 覆盖confirm控件
window.confirm = function(str, cb) {
    obj.confirm.call(obj, str, cb);
};

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

 代码如下 复制代码
alert("自定义Alert组件");
confirm("关闭自定义Confirm组件?", function(flag){
    if (flag) {
        alert("True");
    } else {
        alert("False");
    }
});

2. 组件代码设计

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

 代码如下 复制代码
(function(window, undefined) {
    function JsClassName(cfg) {
        var config = cfg || {};
        this.get = function(n) {
            return config[n];
        }
        this.set = function(n, v) {
            config[n] = v;
        }
        this.init();
    }
    JsClassName.prototype = {
        init: function(){},
        otherMethod: function(){}
    };
    window.JsClassName = window.JsClassName || JsClassName;
})(window);

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

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

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

 代码如下 复制代码

(function(window, jQuery, undefined) {
 
    var HTMLS = {
        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>',
        alert: '<input type="button" class="J_AltBtn pop-btn alert-button" value="确定">',
        confirm: '<input type="button" class="J_CfmFalse pop-btn confirm-false" value="取消">' + '<input type="button" class="J_CfmTrue pop-btn confirm-true" value="确定">'
    }
 
    function Winpop() {
        var config = {};
        this.get = function(n) {
            return config[n];
        }
 
        this.set = function(n, v) {
            config[n] = v;
        }
        this.init();
    }
 
    Winpop.prototype = {
        init: function() {
            this.createDom();
            this.bindEvent();
        },
        createDom: function() {
            var body = jQuery("body"),
                ovl = jQuery("#J_WinpopBox");
 
            if (ovl.length === 0) {
                body.append(HTMLS.ovl);
            }
 
            this.set("ovl", jQuery("#J_WinpopBox"));
            this.set("mask", jQuery("#J_WinpopMask"));
        },
        bindEvent: function() {
            var _this = this,
                ovl = _this.get("ovl"),
                mask = _this.get("mask");
            ovl.on("click", ".J_AltBtn", function(e) {
                _this.hide();
            });
            ovl.on("click", ".J_CfmTrue", function(e) {
                var cb = _this.get("confirmBack");
                _this.hide();
                cb && cb(true);
            });
            ovl.on("click", ".J_CfmFalse", function(e) {
                var cb = _this.get("confirmBack");
                _this.hide();
                cb && cb(false);
            });
            mask.on("click", function(e) {
                _this.hide();
            });
            jQuery(document).on("keyup", function(e) {
                var kc = e.keyCode,
                    cb = _this.get("confirmBack");;
                if (kc === 27) {
                    _this.hide();
                } else if (kc === 13) {
                    _this.hide();
                    if (_this.get("type") === "confirm") {
                        cb && cb(true);
                    }
                }
            });
        },
        alert: function(str, btnstr) {
            var str = typeof str === 'string' ? str : str.toString(),
                ovl = this.get("ovl");
            this.set("type", "alert");
            ovl.find(".J_WinpopMain").<span style="width: auto; height: auto; float: none;" id="4_nwp"><a style="text-decoration: none;" mpid="4" target="_blank" href="http://cpro.baidu.com/cpro/ui/uijs.php?c=news&cf=1001&ch=0&di=128&fv=15&jk=76a947085d99fcf3&k=html&k0=html&kdi0=0&luki=2&n=10&p=baidu&q=06011078_cpr&rb=0&rs=1&seller_id=1&sid=f3fc995d847a976&ssp2=1&stid=0&t=tpclicked3_hc&tu=u1922429&u=http%3A%2F%2Fwww%2Eadmin10000%2Ecom%2Fdocument%2F5961%2Ehtml&urlid=0" id="4_nwl"><span style="color:#0000ff;font-size:14px;width:auto;height:auto;float:none;">html</span></a></span>(str);
            if (typeof btnstr == "undefined") {
                ovl.find(".J_WinpopBtns").html(HTMLS.alert);
            } else {
                ovl.find(".J_WinpopBtns").html(btnstr);
            }
            this.show();
        },
        confirm: function(str, callback) {
            var str = typeof str === 'string' ? str : str.toString(),
                ovl = this.get("ovl");
            this.set("type", "confirm");
            ovl.find(".J_WinpopMain").<span style="width: auto; height: auto; float: none;" id="5_nwp"><a style="text-decoration: none;" mpid="5" target="_blank" href="http://cpro.baidu.com/cpro/ui/uijs.php?c=news&cf=1001&ch=0&di=128&fv=15&jk=76a947085d99fcf3&k=html&k0=html&kdi0=0&luki=2&n=10&p=baidu&q=06011078_cpr&rb=0&rs=1&seller_id=1&sid=f3fc995d847a976&ssp2=1&stid=0&t=tpclicked3_hc&tu=u1922429&u=http%3A%2F%2Fwww%2Eadmin10000%2Ecom%2Fdocument%2F5961%2Ehtml&urlid=0" id="5_nwl"><span style="color:#0000ff;font-size:14px;width:auto;height:auto;float:none;">html</span></a></span>(str);
            ovl.find(".J_WinpopBtns").html(HTMLS.confirm);
            this.set("confirmBack", (callback || function() {}));
            this.show();
        },
        show: function() {
            this.get("ovl").show();
            this.get("mask").show();
        },
        hide: function() {
            var ovl = this.get("ovl");
            ovl.find(".J_WinpopMain").html("");
            ovl.find(".J_WinpopBtns").<span style="width: auto; height: auto; float: none;" id="6_nwp"><a style="text-decoration: none;" mpid="6" target="_blank" href="http://cpro.baidu.com/cpro/ui/uijs.php?c=news&cf=1001&ch=0&di=128&fv=15&jk=76a947085d99fcf3&k=html&k0=html&kdi0=0&luki=2&n=10&p=baidu&q=06011078_cpr&rb=0&rs=1&seller_id=1&sid=f3fc995d847a976&ssp2=1&stid=0&t=tpclicked3_hc&tu=u1922429&u=http%3A%2F%2Fwww%2Eadmin10000%2Ecom%2Fdocument%2F5961%2Ehtml&urlid=0" id="6_nwl"><span style="color:#0000ff;font-size:14px;width:auto;height:auto;float:none;">html</span></a></span>("");
            ovl.hide();
            this.get("mask").hide();
        },
        destory: function() {
            this.get("ovl").remove();
            this.get("mask").remove();
            delete window.alert;
            delete window.confirm;
        }
    };
 
    var obj = new Winpop();
    window.alert = function(str) {
        obj.alert.call(obj, str);
    };
    window.confirm = function(str, cb) {
        obj.confirm.call(obj, str, cb);
    };
})(window, jQuery);

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

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

最后

作为一个前端开发工程师,个人觉得Javascript组件开发是一件很有意思的事情,其中乐趣只有自己亲自动手尝试了才会体会得到。前端组件开发往往需要Javascript、CSS和html相互配合,才能事半功倍,上面提到的Winpop也不例外,

时间: 2024-09-08 11:39:20

全程记录开发一个完整的JavaScript组件教程的相关文章

如何开发一个完整的JavaScript组件

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

FCKeditor提供了一个完整的JavaScript API_网页编辑器

FCKeditor offers a complete JavaScript API so you can interact with it once the editor is loaded and running. FCKeditor提供了一个完整的JavaScript API(Application Public Interface),你可以利用这些API来处理FCK编辑器,只要它被加载完成或在正在运行中. Retrieving an editor instanceOnce loaded,

手把手教您制作一个完整网站(内附教程)

手把手教您制作一个完整网站(内附教程) 1.搞个简单的策划先. 至少应该有一个大概的方向吧,先确定网站的类型,是地区门户.行业门户,还是下载.电影.论坛等等.然后基本确定网站的名称. 2.找一个合适的域名. 域名注册.com(国际域名)和.cn(国内域名)为宜,域名最好不要太长.且有一定的意义.容易记,现在好的域名已经不多了,你可灵活的使用数字.英文单词.拼音等的组合,在域名的前.后加上i.e.51.ok.hao.88.163等,可以灵活的组合出许多好的域名.域名注册信息查询 http://ww

PHP开发一个完整、安全的用户登录系统

在使用PHP编程的时候,我有一个习惯,不太喜欢使用现成的库文件,例如PHPLib或者其它类似的库,在这个系统中,我也打算自己写一个库文件,它需要处理认证.确认email,更新帐号(密码,email)等事情. 为了在保证该系统安全的同时,不会加重我现有数据库的负担.因此这个新的系统要依赖cookies.这确实是一个两难的选择,因为如果只是设置一个用户名的cookie,是很不安全的,这行不通,但从数据库的负担考虑,我也不能加入一个简单的无序码而交由我的数据库来进行验证. 解决的方法是同时设置两个co

使用WSAD开发一个基于BMP EJB的J2EE应用-用户注册及管理

一.引言 EJB1.1规范将EJB分为两种类型:会话bean 及 实体 Bean.会话 Bean是对业务过程的 封装,就象是一个动词,描述业务流程及方法;实体 Bean是对业务数据的封装,就象是一 个名词,描述了业务数据,它通常是数据库记录的对象体现. 实体 Bean 又分为CMP(Container Management Bean - 容器管理持久性)及BMP(Bean Management Bean - Bean管理持久性)两种类型.CMP封装了复杂的数据库存储机制,为我 们提供了数据记录的

JavaScript 组件之旅(一)分析和设计_javascript技巧

另一方面,由于 JavaScript 通常会和宿主环境(比如浏览器)紧密结合,因此缺乏功能强大而简单易用的开发工具.在这样的环境中,开发组件或框架成为一项具有挑战的工作.这次,我们将以一个简易的 JavaScript 组件开发为契机,逐步展开组件的分析.设计.实现.构建和测试等任务,探讨组件开发过程涉及的方方面面.这些探讨将分 4 篇陆续张贴出来(链接将在张贴后更新): 分析和设计组件 编码实现和算法 用 Ant 构建组件 测试 JavaScript 组件 现在,假设我们要从头开始设计并实现一个

怎样在不使用框架的基础上开发一个 Javascript 组件

本文讲的是怎样在不使用框架的基础上开发一个 Javascript 组件, 许多开发者(包括我)犯的一个错误是当遇到问题时他们总是自上而下地考虑问题.他们想问题的时候,总是从考虑框架(Framework),插件(Plugin),预处理器(Pre-processors),后处理器(Post-processors),面向对象模式(objected-oriented patterns)等等这些方面出发,他们也可能会从他们以前看过的一篇文章来考虑.而这时如果有一个生成器(Generator)的话,他们当然

JavaScript组件开发完整示例_javascript技巧

本文实例讲述了JavaScript组件开发的技巧.分享给大家供大家参考,具体如下: 使用JavaScript,按照面向对象的思想来构建组件. 现以构建一个TAB组件为例. 从功能上讲,组件包括可视部分和逻辑控制部分:从代码结构上讲,组件包括代码部分和资源部分(样式.图片等). 组件的特点:高内聚,低耦合(不与其他代码逻辑交叉,可以继承,包含):封装性(隐藏私有方法和变量):可重用性(可反复多次使用,用来组装更复杂的应用). <html> <head> <meta http-e

开发模块化的JavaScript组件

现如今,虽然多数的web应用都使用了大量的JavaScript,但如何保持客户端功能的专注性.健壮性和可维护性依然是一个很大的挑战. 尽管其它编程语言和系统都已经将关注分离和DRY这样的基本原则视为理所当然的宗旨,但往往在进行浏览器端应用开发的时候,这些原则就被忽视了. 造成这一现象的部分原因是JavaScript语言本身就在不断挣扎的历史,在很长的一段时间内,它都难以获得开发者的认真关注和对待. 而更重要的原因或许是源于服务端与客户端的差异造成的.虽然在这方面已经有大量的架构风格方面的概念,例