bootstrap插件概述

好像没有找到过关于bootstrap插件部分的文章,前两天在做一个input放大镜,是采用bootstrap插件编码规范,从而发现原来bootstrap的编码非常让人愉悦。故而想系统性的了解一下。

一、自定义编译js文件

默认bootstrap.min.js提供12个完整插件脚本,我没有找到可以通过网上直接构建的,应该有只是源代码结构非常漂亮,每一个写前端的都心需要自己动手构建一个比较短小的bootstrap插件脚本库出来。

bootstrap是通过 grunt 来构建的,关于grunt使用见参考资料。

bootstrap: {
    src: [
        'js/transition.js',
        'js/alert.js',
        'js/button.js',
        'js/carousel.js',
        'js/collapse.js',
        'js/dropdown.js',
        'js/modal.js',
        'js/tooltip.js',
        'js/popover.js',
        'js/scrollspy.js',
        'js/tab.js',
        'js/affix.js'
    ],
    dest: 'dist/js/<%= pkg.name %>.js'
}

以上是 Gruntfile.js 的一部分【他JSON格式】,用于拼接所有插件脚本,所以很简单只需要把不需要的加个 // 即可。

当然这里只有JS部分,还得去掉CSS部分, bootstrap采用的是LESS编写CSS,存放于 /less/bootstrap.less 也只需要找到我们并不需要的插件样式的 @import "xxx.less"

完成所有操作所以,只需要在源代码根目录下,输入CMD命令: grunt dist,会自动在 dist 目录下重新生成一份新的文件。

二、闭包

+function ($) {
  'use strict';

  // BUTTON PUBLIC CLASS DEFINITION
  // ==============================

  var Button = function (element, options) {
    this.$element  = $(element)
    this.options   = $.extend({}, Button.DEFAULTS, options)
    this.isLoading = false
  }}(jQuery);

很典型的一个闭包应用,这里将 jQuery 对象做为 $ 参数,这样可以避免闭包类使用的是 jQuery 对象,而不是其他类库或自己定义的 $

细心的人还会发现,为什么 function 前面会有一个 + 号,这是一个保障作用,特别是将bootstrap脚本库作为项目当中的一个库;并将所有脚本压缩成一个文件时,如果紧邻前面脚本没有加上 ; 来区分结束时,就会合在一起。有时我们也会使用 ;function(){} 逗号。

三、避免插件同名冲突

  var old = $.fn.button

  $.fn.button             = Plugin
  $.fn.button.Constructor = Button


  // BUTTON NO CONFLICT
  // ==================

  $.fn.button.noConflict = function () {
    $.fn.button = old
    return this
  }

$.fn.button.noConflict 每一个插件都会相类似的代码,用来处理插件同名冲突问题,比如:button,在 jquery.ui 下也有一个同名,假如你同时引用这两个类库时,就会产生两个问题:

  1. jquery.ui 在 bootstrap 前面,这个时候你使用 $('').button() 调用的是 bootstrap 的 button
  2. bootstrap 在 jquery.ui 前面,这个时候你使用 $('').button() 调用的是 jquery.ui 的 button

为什么?因为加载顺序的关系插件被覆盖了,而对于 jquery.ui 而言并未有同时冲突的处理,所以这里强烈要求将 jquery.ui库放在bootstrap前面,有这个前提,再来看我下面的示例,就会明白 noConflict 有什么作用。

//$.fn.button.noConflict();
$('button').button();

jquery.ui库放在bootstrap前面 的前提下,我注释掉第1行代码时,调用的是 bootstrapbutton 插件。反之调用的是 jquery.ui 下的 button 插件。怎么样?有没有感叹 bootstrap 的编码方式很完美。

四、data-* 属性

所有bootstrap的插件都可以不需要在页面上写上脚本调用代码,包括插件参数,都是以 data-参数名="值" 这种形式出现,有没有觉得很高大上。

<button type="button" class="btn btn-primary" data-toggle="button" aria-pressed="false" autocomplete="off">
  Single toggle
</button>

以上当我点击按钮时会自动加载 .action 样式,再次点击取消样式。而实现这一切要归于以下代码:

  $(document)
    .on('click.bs.button.data-api', '[data-toggle^="button"]', function (e) {
      var $btn = $(e.target)
      if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn')
      Plugin.call($btn, 'toggle')
      e.preventDefault()
    })
    .on('focus.bs.button.data-api blur.bs.button.data-api', '[data-toggle^="button"]', function (e) {
      $(e.target).closest('.btn').toggleClass('focus', /^focus(in)?$/.test(e.type))
    })

这里的 click.bs.button.data-api 带有多级的事件命名空间,虽然不太会有重复的事件名称存在,但是如果你有洁癖不想bootstrap监控这些事件,我们可以直接注销掉,比如:

$(document).off('.bs'); // 所有bootstrap监控事件
$(document).off('click.bs.button'); // 只对button插件有效

另一方面将参数 data-* 化,核心其实就是调用 jQuery.data() 他会自动处理这一方面,比如:

  Button.prototype.setState = function (state) {
    var d    = 'disabled'
    var $el  = this.$element
    var val  = $el.is('input') ? 'val' : 'html'
    var data = $el.data()

    state = state + 'Text'

    if (!data.resetText) $el.data('resetText', $el[val]())

    $el[val](data[state] || this.options[state])

    // push to event loop to allow forms to submit
    setTimeout(function () {
      state == 'loadingText' ?
        $el.addClass(d).attr(d, d) :
        $el.removeClass(d).removeAttr(d);
    }, 0)
  }

其中 var data = $el.data() 就是自动将当前元素的所有 data-* 转化为对象。关于这个详见 jQuery.Data()

五、API接口

bootstrap插件都是单一入口、可链接式,比如我们可以这么调用一个 button 插件。

$('button').button('loading').addClass('active');

关于单一入口,比如:

$('button').button('loading');
$('button').button('reset');

是不是觉得记忆成本非常低,有没有?

六、关于事件

bootstrap 每个插件都有属于自己的一序列自定义事件,比如 modelhidden.bs.modal,而所有这一切都是通过 jQuery.trigger 来触发,关于 jQuery.trigger 事件的一些细节,比如:版本不同事件是否冒泡也不一样等等。

当我们需要监听这些事件时,我们可以这么做:

$('#model').on('hidden.bs.modal', function(e) {});

其中 $('#model') 是当前模态框的元素,这样有效的限定同一页面多个模态分别监听。

当然也可以用 $(document) 监听,这归于 jQuery.trigger 事件冒泡,但如果有多个模态框时那么任何一个关闭都将引时该监听。

一个题外话,在查bootstrap源代码时,发现大量的使用 $.proxy(this.hide, this),作用是保持在事件回调函数内上下文依然是插件本身。

七、CSS3兼容

每个插件都有做如下处理:

      var callbackRemove = function () {
        that.removeBackdrop()
        callback && callback()
      }
      $.support.transition && this.$element.hasClass('fade') ?
        this.$backdrop
          .one('bsTransitionEnd', callbackRemove)
          .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) :
        callbackRemove()

    } else if (callback) {
      callback()
    }

这里会监测是否支持CSS3中的 transition,当不支持时直接回调用,否则当动画结束后再回调。

参考资料

  1. Grunt 快速入门
  2. bootstrap github

分析插入DOM效率问题:reflow

在富客户端时代,通过JavaScript来改变元素结构是必须的。这是通过DOM或document对象模型来完成操作,而对于当大量操作或者你想写一个比别人更快的应用,那么了解一些对DOM的原理变得尤为重要。

当元素被真正插入到document时会引发浏览器reflow(即重绘元素样式并计算,这是浏览器的事我们不讨论)。比如我们插入一个新元素、改变元素的CSS样式、调整浏览器窗口、访问offsetHeight等等都会引发若干次 reflow。

说了好几次 reflow,那么提高应用效率,减少浏览器触发 reflow 的次数是一个正比关系。

常见几种容易引发 reflow

reflow-chart

从图上看,可以看得出关于一些会引发 reflow 的CSS属性。

一、CSS样式操作

我们应该尽可能的减少对 element.style.* 的操作次数,因为每一行脚本代码都将可能引发 reflow(为什么说可能,因为对于像现代浏览器会对这种操作做适当的优化)。

function setLinkStyle() {
    element.style.fontWeight = 'bold';
    element.style.color = '#000';
    // 更多类似
}

更适合的方式应该是

.link { font-weight: bold; color: #000; }
function setLinkStyle() {
    element.className = 'link';
}

二、尽可能处理好DOM后再插入

这种创建或修改一个也会引发一个 reflow。假如我们有个函数用来创建新元素,链接文本为 Text;并提供 classlink

function addLink(parentElement) {
    var element = document.createElement('a');
    parentElement.appendChild(element);
    element.innerHTML = 'Text';
    element.className = 'link';
}

这将会引发三次 reflow。因此,我们更快的写法应该是:

function addLink(parentElement) {
    var element = document.createElement('a');
    element.innerHTML = 'Text';
    element.className = 'link';
    parentElement.appendChild(element);
}

三、DocumentFragment 方法

DocumentFragment 他是一个轻量级且不需要父代的DOM对象,他对浏览器或手机浏览器的兼容支持非常完美。

比如我们连续创建30个链接,比如:

function addBatchLink(parentElement) {
    for (var i = 0; i < 30; i++) {
        var element = document.createElement('a');
        element.innerHTML = 'Text';
        element.className = 'link';
        parentElement.appendChild(element);
    }
}

这里会引发30次 reflow。而通过 DocumentFragment 的方式只会触发一次 reflow

function addBatchLink(parentElement) {
    var fragment = document.createDocumentFragment();
    for (var i = 0; i < 30; i++) {
        var element = document.createElement('a');
        element.innerHTML = 'Text';
        element.className = 'link';
        fragment.appendChild(element);
    }
    parentElement.appendChild(fragment);
}

四、offsetHeight 等属性

appendChild 一个元素后接着再访问元素的 offsetHeight 样式属性值时也会立刻触发一个 reflow,且脚本会等待 reflow 完成后才会继续,虽然计算会非常快。

jQuery的DOM操作

jQuery对DOM操作的核心文件是 manipulation.js,这里扩展了一些像 appendafterbefore 等关于DOM操作接口。而这里的核心是 domManip() 方法,他会使用 createDocumentFragment 创建一个全新元素对象,最后才执行一次 appendChild 插入元素。

可见jQuery已经做了很多优化。

重绘与reflow

浏览器会引起重绘,可能会是当添加元素、改变元素可见性、改变背景颜色等时。

而reflow是当DOM被改变风格(我们大可指CSS)时会执行,当改变className、浏览器窗口大小等会进行若干次 reflow,当父元素执行 reflow时,也一并会考虑到子元素的变化,甚至前后代元素,最后变成所有都需要 reflow

重绘与reflow,他们就像是相互关联着,都是影响 DOM 操作效率最主要原因。

影响 reflow 时间

  • DOM结构深度越深,修改元素后代越多所需要时间越长。
  • 最小化CSS规则、删除未使用的CSS,尽可能使用className。

总结

关于 reflow 的话已经说了那么多,如果我们抛开UI阻塞、重绘等问题外,对于SPA应用而言操作DOM的效率影响者整个应用的效率。了解为什么 reflow 是一个至关重要问题,这样才能写出更高效的前端代码。

参考资料

IE6/IE7/IE8/Firefox/Chrome/Safari的CSS hack兼容一览表

各浏览器CSS hack兼容表:

IE6 IE7 IE8 Firefox Chrome Safari
!important Y Y
_ Y
* Y Y
*+ Y
\9 Y Y Y
\0 Y
nth-of-type(1) Y Y

代码示例:

#test{

color:red; /* 所有浏览器都支持 */

color:red !important;/* Firefox、IE7支持 */

_color:red; /* IE6支持 */

*color:red; /* IE6、IE7支持 */

*+color:red; /* IE7支持 */

color:red\9; /* IE6、IE7、IE8支持 */

color:red\0; /* IE8支持 */

}

body:nth-of-type(1) p{color:red;} /* Chrome、Safari支持 */

整体测试代码示例:

.test{

color:#000000;

color:#0000FF\0;

[color:#00FF00;

*color:#FFFF00;

_color:#FF0000;

}

其他说明:

1、如果你的页面对IE7兼容没有问题,又不想大量修改现有代码,同时又能在IE8中正常使用,微软声称,开发商仅需要在目前兼容IE7的网站上添加一行代码即可解决问题,此代码如下:

<meta http-equiv=”x-ua-compatible” content=”ie=7″ />

2、body:nth-of-type(1) 如果这样写,表示全局查找body,将会对应第一个<body>。

3、还有其他写法,比如:

*html #test{}或者 *+html #test{}

4、*+html 对IE7的hack 必须保证HTML顶部有如下声明:

http://www.w3.org/TR/html4/loose.dtd

5、顺序:Firefox、IE8、IE7、IE6依次排列。

来源:http://www.wangzhengdong.com/blog/ie6-ie7-ie8-firefox-chrome-safari-css-hack.html

IE下list-style-type:decimal显示全部为1

先不责备IE,而导致这个问题存在就是IE特有的所谓:hasLayout。

解决办法:

1、不指定li高。

2、_height:1%,很萎缩的办法。

如果都是单行列表,建议使用第1种。至少可以通过设置line-height来展示。

CSS中的background

这里我并不想讨论有关于他的写法,我讨论的话题是有关图像格式等一些细节上的话题。

一、关于PNG和GIF格式图像的总结

PNG是目前保证最不失真的格式,我非常喜欢在一些ICO图标、LOGO、菜单背景等等使用PNG图像,她总能帮我显示得非常精细。常用PNG格式分为PNG-8、PNG-24两种:

1、PNG-8和GIF的差别

PNG-8和GIF其实差别并不是非常大,但是针对PNG有两点非常好的地方:

a、相比GIF存储空间略小,但是PNG无法制作成动画。

b、PNG只需要下载1/64的图像信息就可以显示出低分辨率的预览图像。(这一点同样适用PNG-24)

2、PNG-8不支持半透明

PNG-8和PNG-24其实并不是颜色位的区别而是彼此之间的存储格式不同,有关这方面的知识并不宜讨论过多,我们只要知道PNG-8不支持透明,而PNG-24可以。

3、IE6下的PNG

关于这个话题主要是由于PNG-24在IE6下用灰色的底来代替,如果你的图像并没有半透明的我建议你使用PNG-8至少她比GIF有两点非常不错的好处。而唯一可以解决这个问题是在IE6下使用IE特有的滤镜,然而像iepngfixjquery.pngFix.js都是用来解决这个问题的插件。

二、引用地址的方式

这分为绝对地址和相对地址,绝对地址类似于:http://www.google.com/img/logo.png,相对地址类似于:../img/logo.png,两者在正常情况下都没有差别的。因为不管你用哪一种方式引用都是重新一个新的HTTP,然后下载相应的图像信息。

我比较喜欢用绝对地址,也许看起来会比较长一点,但是我认为这种方式比较直接。而针对相对地址在做防盗链你就小心了,因为在不同的浏览器他们彼此之间的referer来源是不一样的。

三、CSS Sprite,中文叫:CSS图像拼合

她并不是什么新技术,只不过是将一些图片拼合在一起,可以大大的减少HTTP请求次数,纵观所有大型网站都采用这种办法,比如yahoo是最为经典的,将所有小图标统一放在一张图像上。这也包括我非常喜欢的淘宝、苹果等等,都有非常经典的设计方案。

好吧,暂时先整理到这吧。

让DW开发更高效

做ASP开发时我的首选是Dreamweaver(简称:DW)编辑器,其主要在HTML、CSS、JavaScript编写都非常优秀,而其中代码着色、代码提示高效开发帮助非常大。说点我经常使用的工具,估计在高手面前也是小巫见大巫,因为我也只是用用几个最常用小工具:(PS:大部分选项都可以通过在“编辑”》“首先参数”找得到。)

一、站点

  1. 像资源管理器一样的文件树型结构,文件分布一目了然。
  2. 文件重命名后自动重新引用。当我要对某个CSS进行重命名时,所有引用该CSS的页面也相应跟着变,我就不需要一个个改CSS引用。
  3. 链接检查,帮助你检查所有失效的链接,非常有帮助的一个功能喔。
  4. 当然还可以设置测试服务器,轻松按下F12查看实际效果。

二、源代码格式

我向来对代码的缩进要求很严格,因此使用“应用源代码格式”功能完全满足我的要求,当然了,每个人的风格不一样,那么你也可以通过“标签库编辑器”修改相应标签的格式。

三、代码着色

DW代码着色真的无可挑剔。当然如果你不喜欢她默认的配置方案,你也可以通过“代码颜色”选项修改你的风格。

四、代码编辑区

默认是按9px来显示代码大小的,但是对于长期工作在电脑面前来说大一点的字体对眼镜是有益无害的。通过“字体”选项卡上修改。

五、代码提示

这部分我得重点提,HTML、CSS已经很优秀,响应速度快、丰富。

对于JavaScript脚本在CS4新版本已经做了非常多改进,特别是对一些JavaScript框架的支持,例如:JQuery

对于已经做开发很久,我相信很多人都已经有自己的一些库了,那么如果能够让这些库也能出现在代码提示列表当中的话,那么提高开发效率、失误度会大大降低。

DW是通过configuration\CodeHints的XML文件实现代码提示,而且在DW帮助文档里面是有提到CodeHints.xml文件的。至于相应标签的意思我不详解,通过观察自带的可以揣摩出相应标签的意思。

那么既然DW是通过一个叫CodeHints.xml来实现代码提示,当然咱们也可以通过自己写个类似的并实现我们所需要的提示内容。但注意它并不是放在configuration\CodeHints目录下面,而是放于:

win7之前系统:
C:\Documents and Settings\用户\Application Data\Adobe\Dreamweaver CS4\zh_CN\Configuration
或者
C:\Documents and Settings\All Users\Application Data\Adobe\Dreamweaver CS4\zh_CN\Configuration
win7系统:
C:\Users\Administrator\AppData\Roaming\Adobe\Dreamweaver CS4\zh_CN\Configuration

自行新建一个叫:codehints文件夹并把你编写的XML放入该文件夹,文件名无限制。另外需要注意的是每次修改XML文件后,需要重新启动DW才会生效喔。

简单列出一些相应的XML标签解释:

标签名 描述
function 按下Ctrl+Space可直接在列表中找到
pattern 提示字符,也是关键字
doctypes 文档类型,注意:点号要用-来代替,例如:ASP-VB、ASP-JS
icon 提示列表中的小图标
source 代码类型,比如:ASP、CSS、JS
menu 相当于一个菜单的集合,可以添加相应的method子节点。
classpattern 和pattern一样
additionaldismisschars 设置以什么字符显示该菜单的方法列表。例如:Request.Get其中的点号

参考资料

© 2017 卡片机色彩 沪ICP备13032872号-3

Theme by cipchk

to top