Bootstrap 4 中 Alerts 实现

Alert 的使用说明

http://v4-alpha.getbootstrap.com/components/alerts/

JavaScript behavior

Triggers

Enable dismissal of an alert via JavaScript:

$(".alert").alert()

Or with data attributes on a button within the alert, as demonstrated above:

Note that closing an alert will remove it from the DOM.

Methods

Method Description
$().alert() Makes an alert listen for click events on descendant elements which have the data-dismiss="alert" attribute. (Not necessary when using the data-api’s auto-initialization.)
$().alert('close') Closes an alert by removing it from the DOM. If the .fade and .in classes are present on the element, the alert will fade out before it is removed.
 
$(".alert").alert('close')

 

Events

Bootstrap’s alert plugin exposes a few events for hooking into alert functionality.

Event Description
close.bs.alert This event fires immediately when the close instance method is called.
closed.bs.alert This event is fired when the alert has been closed (will wait for CSS transitions to complete).
 
$('#myAlert').on('closed.bs.alert', function () {
  // do something…
})

获取 bootstrap 4 

bootstrap v4 

源代码分析

外层函数分析

从代码的最外层来看,实际上是一个立即执行的函数,jQuery 就是我们熟悉的 jQuery 库。

+function ($) {

} (jQuery);

对于在 function 之前使用 + 的用法,这里有详细的说明:function与感叹号

加上 Alert 之后,我们函数内部会成为嵌套的立即执行函数。

+function ($) {

    var Alert = (function ($) {

    })(jQuery);

} (jQuery);

有点问题,编码风格不一致,这次里面使用了常见的括号方式来处理立即执行函数。

几个辅助函数

创建类的函数 _createClass.

在 JavaScript 中,如何才能定义一个类呢?

类的作用在于同类的对象可以共享处理方法和数据,因此,在类中,必须有一个方式来共享所有同类对象所共享的成员,在 JavaSript 中,基本的方式是通过 function 的原型 prototype 来实现的。定义在 function 的 prototype 对象上的成员,可以被通过这个函数 new 出来的对象所共享。

Bootstrap 4 中 Alerts 实现_第1张图片

所以,通常一个类是一个函数,我们把希望共享的成员定义在它的原型对象上。

同时,还可以定义静态的成员,不需要通过对象实例来放问,可以直接通过类型来访问,这些静态成员可以直接定义在这个 function 对象上,没错,function 也是一个对象。

这样的话,在创建一个类的时候,我们需要为这个函数定义实例成员和静态成员,这就是 _createClass 这个函数的职责。

先看简化版本,这个函数不是直接定义出来的,是调用一个匿名函数返回的。这又是一个立即执行函数。

var _createClass = (function () {

    return function () {
    
    }; 
})();

在它的内部定义了一个私有的函数,这个函数在外部不可调用。我们再扩展一下。这样看的更加清楚一些。

var _createClass = (function () {

    // 内部的私有函数,外部不可调用.
    function defineProperties(target, props) {
      }
    }

    // 返回的函数,外部可以调用
    return function (Constructor, protoProps, staticProps) {
       defineProperties(Constructor.prototype, protoProps);
       return Constructor;
    };

})();

内部的这个 defineProperties 做什么呢?就是为目标对象定义属性。属性需要实现一些规范: Object.prototype, 下面的代码就是实现规范的要求.

Object.prototype 属性的属性特性:
writable false
enumerable false
configurable false
// define prpperties on object.
function defineProperties(target, props) {
  for (var i = 0; i < props.length; i++) {
    var descriptor = props[i];
    descriptor.enumerable = descriptor.enumerable || false;
    descriptor.configurable = true;
    if ('value' in descriptor)
      descriptor.writable = true;
          
    // https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
    Object.defineProperty(target, descriptor.key, descriptor);
  }
}

所以, 创建类型就是传递两个分别描述实例成员和静态成员的数组了.

_createClass 的全部代码如下:

  var _createClass = (function () {

    // define prpperties on object.
    function defineProperties(target, props) {
      for (var i = 0; i < props.length; i++) {
        var descriptor = props[i];
        descriptor.enumerable = descriptor.enumerable || false;
        descriptor.configurable = true;
        if ('value' in descriptor)
          descriptor.writable = true;
          
        // https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
        Object.defineProperty(target, descriptor.key, descriptor);
      }
    }

    // Constructor: target function
    // protoProps: prototype of function
    // staticProps: static properties
    return function (Constructor, protoProps, staticProps) {
      if (protoProps)
        defineProperties(Constructor.prototype, protoProps);
      if (staticProps)
        defineProperties(Constructor, staticProps);
      return Constructor;
    };
  })();

 Alert 定义

里面的 Alert 是一个变量,这个变量是一个内部函数 Alert 返回的. 而这个内部函数就是我们的 Alert 类定义. 可以看到在这个函数定义之后, 就被添加了我们类的成员.

var Alert = (function ($) {
    function Alert(element) {
        _classCallCheck(this, Alert);

        this._element = element;
    }

  _createClass(Alert, [{......}]);

  return Alert;
})();

 再看剩下处理内容.

在 document 上注册了一个名为 "click.bs.alert.data-api" 的事件处理程序, 在 jQuery 中吧on 函数的定义如下:

.on( events [, selector ] [, data ], handler )

see also: http://api.jquery.com/on/

第一个参数是事件名称,有可选的选择器,还有事件处理程序.

$(document).on(Event.CLICK_DATA_API, Selector.DISMISS, Alert._handleDismiss(new Alert()));

 

最后, 是适配到 jQuery 的原型中, 以便直接通过 jQuery 对象实例进行处理. 其中的工作NAME 这里就是 alert 了.

/**
* ------------------------------------------------------------------------
* jQuery
* ------------------------------------------------------------------------
*/

$.fn[NAME] = Alert._jQueryInterface;
$.fn[NAME].Constructor = Alert;
$.fn[NAME].noConflict = function () {
    $.fn[NAME] = JQUERY_NO_CONFLICT;
    return Alert._jQueryInterface;
};

return Alert;

 

这里的工作_jQueryInterface 就是一个函数.

function _jQueryInterface(config) {
    return this.each(function () {
        var $element = $(this);
        var data = $element.data(DATA_KEY);

        if (!data) {
            data = new Alert(this);
            $element.data(DATA_KEY, data);
        }

        if (config === 'close') {
            data[config](this);
        }
    });
}

 

注册事件处理函数

// Event.CLICK_DATA_API: click.bs.alert.data-api
// Selector.DISMISS: [data-dismiss="alert"]
// register a event handler
$(document).on(Event.CLICK_DATA_API, Selector.DISMISS, Alert._handleDismiss(new Alert()));

 注意,这里的事件名称并不是通常的 click, 而是 click.bs.alert.data-api,这里涉及到 jQuery 的事件命名空间问题,可以到这里参考 jQuery 的文档:http://api.jquery.com/on/#event-names

作用就是我们可以在取消某些事件处理注册的时候,不会影响其它已经注册的事件处理程序。

目标选择器则为:[data-dismiss="alert"] ,这正是 bootstrap 所要求的按钮必须拥有的属性。

 

 全部的 Alert 代码.

/**
 * --------------------------------------------------------------------------
 * Bootstrap (v4.0.0-alpha.2): alert.js
 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
 * --------------------------------------------------------------------------
 */

  var Alert = (function ($) {

    /**
     * ------------------------------------------------------------------------
     * Constants
     * ------------------------------------------------------------------------
     */

    var NAME = 'alert';
    var VERSION = '4.0.0-alpha';
    var DATA_KEY = 'bs.alert';
    var EVENT_KEY = '.' + DATA_KEY;
    var DATA_API_KEY = '.data-api';
    var JQUERY_NO_CONFLICT = $.fn[NAME];
    var TRANSITION_DURATION = 150;

    var Selector = {
      DISMISS: '[data-dismiss="alert"]'
    };

    var Event = {
      CLOSE: 'close' + EVENT_KEY,
      CLOSED: 'closed' + EVENT_KEY,
      CLICK_DATA_API: 'click' + EVENT_KEY + DATA_API_KEY
    };

    var ClassName = {
      ALERT: 'alert',
      FADE: 'fade',
      IN: 'in'
    };

    /**
     * ------------------------------------------------------------------------
     * Class Definition
     * ------------------------------------------------------------------------
     */

    var Alert = (function () {
      
      // class define
      function Alert(element) {
        _classCallCheck(this, Alert);

        this._element = element;
      }

      /**
       * ------------------------------------------------------------------------
       * Data Api implementation
       * ------------------------------------------------------------------------
       */

      // getters

      _createClass(Alert, [{
        key: 'close',

        // public

        value: function close(element) {
          element = element || this._element;

          var rootElement = this._getRootElement(element);
          var customEvent = this._triggerCloseEvent(rootElement);

          if (customEvent.isDefaultPrevented()) {
            return;
          }

          this._removeElement(rootElement);
        }
      }, {
          key: 'dispose',
          value: function dispose() {
            $.removeData(this._element, DATA_KEY);
            this._element = null;
          }

          // private

        }, {
          key: '_getRootElement',
          value: function _getRootElement(element) {
            var selector = Util.getSelectorFromElement(element);
            var parent = false;

            if (selector) {
              parent = $(selector)[0];
            }

            if (!parent) {
              parent = $(element).closest('.' + ClassName.ALERT)[0];
            }

            return parent;
          }
        }, {
          key: '_triggerCloseEvent',
          value: function _triggerCloseEvent(element) {
            var closeEvent = $.Event(Event.CLOSE);

            $(element).trigger(closeEvent);
            return closeEvent;
          }
        }, {
          key: '_removeElement',
          value: function _removeElement(element) {
            $(element).removeClass(ClassName.IN);

            if (!Util.supportsTransitionEnd() || !$(element).hasClass(ClassName.FADE)) {
              this._destroyElement(element);
              return;
            }

            $(element).one(Util.TRANSITION_END, $.proxy(this._destroyElement, this, element)).emulateTransitionEnd(TRANSITION_DURATION);
          }
        }, {
          key: '_destroyElement',
          value: function _destroyElement(element) {
            $(element).detach().trigger(Event.CLOSED).remove();
          }

          // static

        }], [{
          key: '_jQueryInterface',
          value: function _jQueryInterface(config) {
            return this.each(function () {
              var $element = $(this);
              var data = $element.data(DATA_KEY);

              if (!data) {
                data = new Alert(this);
                $element.data(DATA_KEY, data);
              }

              if (config === 'close') {
                data[config](this);
              }
            });
          }
        }, {
            key: '_handleDismiss',
            value: function _handleDismiss(alertInstance) {
              return function (event) {
                if (event) {
                  event.preventDefault();
                }

                alertInstance.close(this);
              };
            }
          }, {
            key: 'VERSION',
            get: function get() {
              return VERSION;
            }
          }]);

      return Alert;
    })();

    $(document).on(Event.CLICK_DATA_API, Selector.DISMISS, Alert._handleDismiss(new Alert()));

    /**
     * ------------------------------------------------------------------------
     * jQuery
     * ------------------------------------------------------------------------
     */

    $.fn[NAME] = Alert._jQueryInterface;
    $.fn[NAME].Constructor = Alert;
    $.fn[NAME].noConflict = function () {
      $.fn[NAME] = JQUERY_NO_CONFLICT;
      return Alert._jQueryInterface;
    };

    return Alert;
  })(jQuery);
  
} (jQuery);

 使用的样式

.alert {
  padding: 15px;
  margin-bottom: 1rem;
  border: 1px solid transparent;
  border-radius: .25rem;
}

.alert > p,
.alert > ul {
  margin-bottom: 0;
}

.alert > p + p {
  margin-top: 5px;
}

.alert-heading {
  color: inherit;
}

.alert-link {
  font-weight: bold;
}

.alert-dismissible {
  padding-right: 35px;
}

.alert-dismissible .close {
  position: relative;
  top: -2px;
  right: -21px;
  color: inherit;
}

.alert-success {
  color: #3c763d;
  background-color: #dff0d8;
  border-color: #d0e9c6;
}

.alert-success hr {
  border-top-color: #c1e2b3;
}

.alert-success .alert-link {
  color: #2b542c;
}

.alert-info {
  color: #31708f;
  background-color: #d9edf7;
  border-color: #bcdff1;
}

.alert-info hr {
  border-top-color: #a6d5ec;
}

.alert-info .alert-link {
  color: #245269;
}

.alert-warning {
  color: #8a6d3b;
  background-color: #fcf8e3;
  border-color: #faf2cc;
}

.alert-warning hr {
  border-top-color: #f7ecb5;
}

.alert-warning .alert-link {
  color: #66512c;
}

.alert-danger {
  color: #a94442;
  background-color: #f2dede;
  border-color: #ebcccc;
}

.alert-danger hr {
  border-top-color: #e4b9b9;
}

.alert-danger .alert-link {
  color: #843534;
}

 

 待续......

 

你可能感兴趣的:(Bootstrap 4 中 Alerts 实现)