发布于 2016-03-02 09:54:16 | 138 次阅读 | 评论: 0 | 来源: 分享

这里有新鲜出炉的Bootstrap v3教程,程序狗速度看过来!

Bootstrap Web前端CSS框架

Bootstrap是Twitter推出的一个开源的用于前端开发的工具包。它由Twitter的设计师Mark Otto和Jacob Thornton合作开发,是一个CSS/HTML框架。Bootstrap提供了优雅的HTML和CSS规范,它即是由动态CSS语言Less写成。Bootstrap一经推出后颇受欢迎,一直是GitHub上的热门开源项目,包括NASA的MSNBC(微软全国广播公司)的Breaking News都使用了该项目。


 由于浏览器提供的alert和confirm框体验不好,而且浏览器没有提供一个标准的以对话框的形式显示自定义HTML的弹框函数,所以很多项目都会自定义对话框组件。本篇文章介绍自己在项目中基于bootstrap的modal组件,自定义alert,confirm和modal对话框的经验,相对比较简单实用,希望能对你有所参考价值。(

如果你有javascript的组件开发经验,我这个层级的代码相信你一下子就能看明白。源码中我还给出了一个demo,这个demo模拟了一个比较贴近现实需求的一个场景:

1)用户点击界面上的某个按钮,打开之前定义的一个modal框:

2)用户在打开的modal框内填写一些表单,点击确定的时候,会触发一些校验:

没填email时:

填写了email之后:

这两个提示其实是为了演示Alert和Confirm的效果硬塞进去的,实际上可能没有这么别扭的功能。

3)在提示Password为空的时候,细心的人会发现那个确定按钮处于一个禁用的状态,这个考虑是因为确定按钮最终要完成的是一些异步任务,在异步任务成功完成之前,我希望modal组件都不要关闭,并且能够控制已点击的按钮不能重复点击;

4)我用setTimeout模拟了一个异步任务,这个异步任务在点击确定按钮之后,3s才会回调,并且:

当email输入admin

当email输入其它值得时候,会给出提交失败的提示,并且modal框会依然显示在那里:

在组件定义里面,尤其是注册按钮这一块,我加了一些AOP编程的处理,同时利用了jquery的延迟对象,来实现我需要的异步编程,详情请阅读源码,有问题可以在评论区交流赐教。

2. 组件需求

有时候为了写一个好用的组件,只需要把它的大概原型和要对外部提供的接口确定下来,就已经完成这个组件编写最重要的工作了,虽然还没有开始编码。以本文要编写的这几个组件来说,我想要的这几个组件的原型和调用形式分别是这样的:

1)自定义alert框

原型是:

调用时最多需要两个参数,一个msg用来传递要显示的提示内容,一个onOk用来处理确定按钮点击时候的回调,调用形式有以下2种:


//1
Alert('您选择的订单状态不符合当前操作的条件,请刷新列表显示最新数据后再继续操作!');
//2
Alert({
    msg: '您选择的订单状态不符合当前操作的条件,请刷新列表显示最新数据后再继续操作!',
    onOk: function(){
    }
});

第一种是没有回调的情况,那么直接传递msg即可,第二种是有回调的情况,用options对象的方式来传递msg和onOks回调这两个参数。不管onOk回调有没有,点击按钮的时候都要关闭弹框。

2)自定义confirm框

这个框的原型跟alert框只差一个按钮:

调用形式只有一种:


Confirm({
    msg: '您选择的订单状态不符合当前操作的条件,请确认是否要继续操作!',
    onOk: function(){

    },
    onCancel: function(){

    }
});

onCancel是在点击取消按钮时候的回调。不管onOk和onCancel回调有没有,点击按钮的时候都要关闭弹框。onCancel回调可以没有。

3)自定义modal框

原型:

调用形式:


var modal = new Modal({
    title: '',
    content: '',
    width: 600,
    buttons: [
        {
            html: '确定',
            selector: '.btn-ok',
            callback: function(){
                //点击确定按钮的回调
            }
        },
        {
            html: '取消',
            selector: '.btn-cancel',
            callback: function(){
                //点击取消按钮的回调
            }
        }
    ],
    onContentReady: function(){
        //当modal添加到DOM并且初始化完毕时的事件回调,每个modal实例这个回调只会被触发一次
    },
    onContentChange: function(){
        //当调用modal.setContent类似的方法改变了modal内容时的事件回调
    },
    onModalShow: function(){
        //当调用modal.open类似方法显示modal时都会触发的事件回调
    },
    onModalHide: function(){
        //当调用modal.hide类似方法隐藏modal时都会触发的事件回调
    }
});

$('#btn-audit').click(function(){
    modal.open();
});

跟Alert和Confirm不同的是,一个页面里面只需要一个Alert和Confirm的实例,但是可能需要多个Modal的实例,所以每个Modal对象都需要单独new一下。由于每个Modal要完成的事情都不相同,所以:

需要一个title参数来设置名称,表达这个Modal正在处理的事情;

content参数表示Modal的html内容;

width参数设置Modal的宽度,Modal的高度保持auto;

buttons参数用来配置这个Modal上面的按钮,一般情况下Modal组件只需要两个按钮(确定和取消)就够了,但也有少数情况需要多个按钮,所以把按钮做成配置的方式相对灵活一点,每个按钮用三个参数来配置,html表示按钮的html结构,selector方便注册回调的时候通过事件委托的方式来处理,callback配置按钮点击时的回调;

onContentReady这个事件回调,可以在Modal初始化完毕的时候,主动去初始化Modal内部html的一些组件;由于组件初始化一般只进行一次,所以放在这个回调里面最合适;

onContentChange回调,在一个Modal需要被用作不同的场景,显示不同的HTML的内容时会派上用场,但是不是非常的好用,处理起来逻辑会稍微偏复杂,如果一个Modal实例只做一件事情的时候,onContentChange这个回调就用不到了;

onModalShow这个回调在每次显示Modal的时候都会显示,使用的场景有很多,比如某个Modal用来填写一些表单内容,下次填写的时候需要reset一下表单才能给用户使用,这种处理在这个回调里面处理就比较合适;

onModalHide这个回调有用,不过能够用到的场景不多,算是预留的一个接口。

4)其它需求

所有类型的弹框都做成虚拟模态的形式,显示框的同时加一个遮罩;

所有框都不需要支持拖动和大小调整;

alert和dialog框的标题,按钮数量、按钮位置、按钮文字都固定。

实际上

遮罩这个效果,bootstrap的modal组件本身就已经支持了;

拖动和大小调整,这个功能属于锦上添花,但是对软件本身来说,并一定有多少额外的好处,所以我选择不做这种多余的处理;

alert和dialog不需要做太过个性化,能够统一风格,改变浏览器原生的弹框体验即可。

5)DEMO中调用实例

接下来演示下我在完成这三个组件开发之后,实际使用过程中调用这些组件的方式:


var modal = new Modal({
    title: '测试modal',
    content: $('#modal-tpl').html(),
    width: 500,
    onOk: function(){
        var $form = this.$modal.find('form');
        var data = $form.serializeArray();
        var postData = {};
        data.forEach(function(obj){
           postData[obj.name] = obj.value;
        });

        if(!postData.email) {
            Alert('请输入EMAIL!');
            return false;
        }

        var deferred = $.Deferred();
        if(!postData.password) {
            Confirm({
                msg: 'Password为空,是否要继续?',
                onOk: function(){
                    _post();
                },
                onCancel: function(){
                    deferred.reject();
                }
            })
        } else {
            _post();
        }

        return $.when(deferred);

        function _post(){
            //模拟异步任务
            setTimeout(function(){
                if(postData.email === 'admin@admin') {
                    Alert({
                        msg: '提交成功!',
                        onOk: function(){
                            deferred.resolve();
                        }
                    });
                } else {
                    Alert({
                        msg: '提交失败!',
                        onOk: function(){
                            deferred.reject();
                        }
                    });
                }
            },3000);
        }
    },
    onModalShow: function () {
        var $form = this.$modal.find('form');
        $form[0].reset();
    }
});
$('#btn-modal').click(function () {
    modal.open();
});

3. 实现要点

1)最基础的一点,要对bootstrap的modal组件源码有所了解

初始化方式:$modal.modal()

打开:$modal.modal(‘show’)

关闭:$modal.modal(hide)

事件:bootstrap大部分带过渡效果的组件的事件都是成对的,并且一个是现在时,一个是完成时,modal组件定义了2对:

show.bs.modal和shown.bs.modal,hide.bs.modal和hidden.bs.modal。

这两对事件分别在打开和关闭的过渡效果执行前后触发。从我要定义的组件需求来说,定义组件的时候需要show.bs.modal和hidden.bs.modal这两个事件,在侦听到bootstrap的modal组件派发这两个事件的时候,派发自己定义的组件的事件:modalShow和modalHide。

选项:

backdrop: 是否显示遮罩;

keyboard: 是否支持键盘回调;

show:是否在初始化完毕就立即显示。

这三个选项默认都是true,但是在我定义组件的时候,我都配置成了false,键盘回调这种特性暂时不考虑,所以配置为true;当调用bootstrap的modal初始化的时候当然不能立即显示弹框,所以也不能配置为true;backdrop配置为false的原因在下一点介绍。

2)遮罩处理

如果启用bootstrap的遮罩,会发现在点击遮罩部分的时候,弹框就会自动关掉了,这不是我期望的虚拟模态效果,所以必须把backdrop配置为false。但是把这个选项配置为false之后,又会引发一个新问题,就是组件没有了遮罩效果,所以为了兼顾这两个问题,只能自己写一个简单的遮罩处理:


var $body = $(document.body),
    BackDrop = (function () {
        var $backDrop,
            count = 0,
            create = function () {
                $backDrop = $('').appendTo($body);
            };

        return {
            show: function () {
                !$backDrop & create();
                $backDrop[0].style.display = 'block';
                count++;
            },
            hide: function () {
                count--;
                if (!count) {
                    $backDrop.remove();
                    $backDrop = undefined;
                }
            }
        }
    })();

这段代码中引入count变量的原因是因为BackDrop是一个全局的单例对象,当调用多个modal实例的open方法的时候,都会调用BackDrop的show方法,为了保证在调用BackDrop的hide方法时,能够确保在所有的modal实例都关闭之后再隐藏Backdrop,所以就加了一个count变量来记录BackDrop的show方法被调用了多少次,只有当count为0的时候,调用BackDrop的hide方法才会真正隐藏BackDrop。

3)组件的选项的默认值定义


ModalDialog.defaults = {
    title: '',
    content: '',
    width: 600,
    buttons: [
        {
            html: '<button type="button" class="btn btn-sm btn-primary btn-ok">确定</button>',
            selector: '.btn-ok',
            callback: getDefaultBtnCallbackProxy('onOk')
        },
        {
            html: '<button type="button" class="btn btn-sm btn-default btn-cancel">取消</button>',
            selector: '.btn-cancel',
            callback: getDefaultBtnCallbackProxy('onCancel')
        }
    ],
    onOk: $.noop,
    onCancel: $.noop,
    onContentReady: $.noop,
    onContentChange: $.noop,//content替换之后的回调
    onModalShow: $.noop,
    onModalHide: $.noop//modal关闭之后的回调
};

通过buttons配置两个默认的按钮,确定和取消,然后为了简化这两个默认按钮的回调配置,把这两个按钮的接口进一步扩展到了上一级别,onOk和onCancel分别会在点击确定和取消的时候被调用,这两个选项完全是函数回调,不像onContentReady这种属于事件回调。getDefaultBtnCallbackProxy用来辅助注册onOk和onCancel:


var getDefaultBtnCallbackProxy = function (callbackName) {
    return function () {
        var opts = this.options,
            callback = opts[callbackName] & typeof opts[callbackName] === 'function' ? opts[callbackName] : '';

        return callback & callback.apply(this, arguments);
    }
}

里面的this会被绑定到modal实例上。

4)构造函数:


function ModalDialog(options) {
    this.options = this.getOptions(options);
    this.$modal = undefined;
    this.$modalTitle = undefined;
    this.$modalBody = undefined;
    this.$modalFooter = undefined;
    this.state = undefined;
}

这个主要是声明了用到的一些实例变量。

5)关键的原型方法和函数


open: function (state) {
    this.state = state;
    !this.$modal & initModal(this, this.options);
    BackDrop.show();
    this.$modal.modal('show');
}

这是个原型方法,组件的初始化也是在这个方法调用的时候执行的(延迟初始化)。


initModal = function (that, opts) {
    var $modal = createModal(that);
    that.setTitle(opts.title);
    that.setContent(opts.content);
    that.addButtons(opts.buttons);
    that.setWidth(opts.width);
    bindHandler(that, opts);
    $modal.modal();//调用bootstrap的Modal组件
    $modal.trigger('contentReady');
}

这是个函数,用来初始化组件。其中:

setTitle是个原型方法,用来设置modal的标题;

setContent是个原型方法,用来设置modal的html内容;

addButtons是个原型方法,用来注册按钮;

setWidth是个原型方法,用来设置modal的宽度;

bindHandler是个函数,用来注册modal的那些事件;

倒数第二步调用$modal.modal()初始化bootstrap的modal组件;

最后一步触发contentReady事件。

bindHandler源码:


bindHandler = function (that, opts) {
    var $modal = that.$modal;
    typeof opts.onContentChange === 'function' & $modal.on('contentChange', $.proxy(opts.onContentChange, that));
    typeof opts.onContentReady === 'function' & $modal.on('contentReady', $.proxy(opts.onContentReady, that));
    typeof opts.onModalShow === 'function' & $modal.on('modalShow', $.proxy(opts.onModalShow, that));
    typeof opts.onModalHide === 'function' & $modal.on('modalHide', $.proxy(opts.onModalHide, that));
    $modal.on('show.bs.modal', function () {
        $modal.trigger('modalShow');
    });
    $modal.on('hidden.bs.modal', function () {
        $modal.trigger('modalHide');
    });
}

为了方便使用,我把onContentChange这几个回调的上下文绑定到了当前的modal实例。最后两个事件侦听就是把bootstrap的事件封装成了我定义的modal事件。

addButtons源码:


addButtons: function (buttons) {
    var buttons = !$.isArray(buttons) ? [] : buttons,
        that = this,
        htmlS = [];
    buttons.forEach(function (btn) {
        htmlS.push(btn.html);

        btn.selector & that.$modal.on('click', btn.selector, $.proxy(function (e) {

            var self = this,
                $btn = $(e.currentTarget);

            //先禁用按钮
            $btn[0].disabled = true;

            var callback = typeof btn.callback === 'function' ? btn.callback : '',
                ret = callback & callback.apply(self, arguments);

            if (ret === false) {
                $btn[0].disabled = false;
                return;
            }

            if (typeof(ret) === 'object' & 'done' in ret && typeof ret['done'] === 'function') {
                //异步任务只有在成功回调的时候关闭Modal
                ret.done(function () {
                    that.hide();
                }).always(function () {
                    $btn[0].disabled = false;
                });
            } else {
                $btn[0].disabled = false;
                that.hide();
            }

        }, that));
    });

    this.$modalFooter.prepend($(htmlS.join('')));
}

从这个代码可以看出:

当按钮点击之后,按钮就会被禁用;

当按钮返回false的时候,按钮恢复,但是modal不会被关闭,说明当前的一些操作被代码给拦下来了;

当按钮返回的是一个延迟对象的时候,会等到延迟对象完成的时候才会恢复按钮,并且只有在延迟对象resolve的时候才会关闭modal;

否则就恢复按钮,并主动关闭modal。

在这段代码里面考虑了:

按钮的防重复点击,modal的自动关闭以及异步任务的处理。

6)封装Alert和Confirm

Alert和Confirm其实就是一个特殊的modal,另外这两个组件还可以共用一个modal,了解到这些基础之后,组件就可以这样定义:


var Alert, Confirm;
(function () {
    var modal,
        Proxy = function (isAlert) {
            return function () {
                if (arguments.length != 1) return;
                var msg = typeof arguments[0] === 'string' & arguments[0] || arguments[0].msg || '',
                    onOk = typeof arguments[0] === 'object' && typeof arguments[0].onOk === 'function' && arguments[0].onOk,
                    onCancel = typeof arguments[0] === 'object' && typeof arguments[0].onCancel === 'function' && arguments[0].onCancel,
                    width = typeof arguments[0] === 'object' && arguments[0].width || 400,
                    _onModalShow = function () {
                        this.setWidth(width);
                        this.setContent(msg);
                        this[(isAlert ? 'hide' : 'show') + 'Button']('.btn-cancel');
                    },
                    _onModalHide = function () {
                        this.setContent('');
                    };

                //延迟初始化modal
                if(!modal) {
                    modal = new Modal({
                        'title': '操作提示',
                        onModalShow: _onModalShow,
                        onModalHide: _onModalHide,
                        onContentReady: function(){
                            this.$modalBody.css({
                                'padding-top': '30px',
                                'padding-bottom': '30px'
                            })
                        }
                    });
                } else {
                    //如果modal已经初始化则需要重新监听事件
                    var $modal = modal.$modal;
                    $modal.off('modalShow modalHide');
                    $modal.off('modalShow modalHide');
                    $modal.on('modalShow', $.proxy(_onModalShow, modal));
                    $modal.on('modalHide', $.proxy(_onModalHide, modal));
                }

                modal.setOptions({
                    onOk: onOk || $.noop,
                    onCancel: onCancel || $.noop
                });

                modal.open();
            }
        };

    Alert = Proxy(true);
    Confirm = Proxy();
})();

这段代码里:

首先考虑到了延迟初始化这个全局的modal组件;

由于onModalHide和onModalShow这两个回调属于事件回调,在初始化组件的时候通过options传进去的参数,不能通过修改options的方式来更改回调,只能通过重新注册的方式来处理;而onOk和onCancel属于函数回调,只要更改了options里面的引用,回调就能更改;

考虑到Alert和Confirm内容的长短,新加了一个参数width,以便调节框的宽度。

4. 小结

本文介绍的是自己在定义js组件过程中的一些方法和实践,代码偏多,不容易引起人的阅读兴趣,但是文中介绍的方法比较简单,而且这三个组件我已经用到好几个项目里面,从目前来看,能够解决我所有需要的弹框需求,所以我把它推荐出来,希望能给有需要的人带来帮助。如果你对代码有兴趣,欢迎下载源码了解详细的实现,任何问题都可在评论里面与我交流。谢谢阅读:)

代码下载



相关阅读 :
利用bootstrap的modal组件自定义alert,confirm和modal对话框
bootstrap 使用modal加载kindeditor编辑器弹出层文本框不能输入的问题解决方法
bootstrap的Icon图表的示例代码
bootstrap的使用方式-如何引入
让IE6 IE7 IE8 IE9 IE10 IE11支持Bootstrap的方法
bootstrap的carousel支持滑动滚屏示例
让IE6 IE7 IE8 IE9 IE10 IE11支持Bootstrap的解决方法
12款优秀的 Twitter Bootstrap 组件和工具
15 个很棒的 Bootstrap UI 界面编辑器
20 款最棒的 jQuery Bootstrap 插件
20 个免费的 Bootstrap 模板
15款优秀的 Twitter Bootstrap 开发工具
最新网友评论  共有(0)条评论 发布评论 返回顶部

Copyright © 2007-2015 PHPERZ.COM All Rights Reserved   冀ICP备14009818号  版权声明  广告服务