欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

jQuery彈簧插件編寫基礎(chǔ)之“又見彈窗”

 更新時間:2015年12月11日 11:08:33   投稿:mrr  
本文通過具體實例給大家介紹jquery彈窗插件編寫基礎(chǔ)之又見彈簧的相關(guān)資料,對jquery彈簧插件編寫相關(guān)知識感興趣的朋友一起學(xué)習(xí)吧

本文將通過一個實例來引出jQuery插件開發(fā)中的一些細(xì)節(jié),首先介紹下jQuery插件開發(fā)的一些基礎(chǔ)知識。

jQuery的插件開發(fā)主要分為兩類:

1. 類級別,即在jQuery類本身上擴(kuò)展方法,類似與 $.ajax,$.get 等。

2. 對象級別,這里所謂的對象是指通過jQuery選擇器選中的jQuery對象,在該對象上添加方法。例如:$('div').css(), $('div').show() 等。

在實際開發(fā)中,我們通常選用對象級別的方法來開發(fā)插件,jQuery強大的選擇器及對象操作是我們選擇這種方式很大的一個原因。

接下來我們看看兩種方式的具體寫法是什么:

類級別的插件開發(fā)

$.extend({
  foo: function() {
    //...
  },
  bar: function() {
    //...
  }
})
//調(diào)用
$.foo();

在這里,對擴(kuò)展方法的命名需要考究一些,以免與jQuery類中的原有方法重名。即便如此,當(dāng)我們需要在類上擴(kuò)展多個方法時仍有可能會出現(xiàn)命名沖突的情況,為此我們可以創(chuàng)建自定義的命名空間:

$.myPlugin = {
  foo: function() {
    //...
  },
  bar: function() {
    //...
  }
}
 //調(diào)用
$.myPulgin.foo();

對象級別的插件開發(fā)

$.fn.foo = function() {
  //doSomething...
}
//調(diào)用(假設(shè)擁有一個id為obj的元素)
$('#obj').foo();
有個會問 fn 是什么東東?粘一段別人截取的jQuery源碼就明白了:
jQuery.fn = jQuery.prototype = {
  init: function(selector, context) {
    //....
  }
}

原來是原型鏈啊。。。

接收配置參數(shù)

在編寫一個插件時,我們可以讓使用插件的人能按自己的意愿設(shè)置插件的一些屬性,這就需要插件有接收參數(shù)的功能,同時當(dāng)使用插件的人不傳入?yún)?shù)時,插件內(nèi)部也有一套自己默認(rèn)的配置參數(shù)。

$.fn.foo = function(options) {
  var defaults = {
    color: '#000',
    backgroundColor: 'red'
  };
  var opts = $.extend({}, defaults, options); 
  alert(opts.backgroundColor); //yellow
}
$('#obj').foo({
  backgroundColor: 'yellow'  
})

這里的關(guān)鍵就是 $.extend 方法,它能夠?qū)ο筮M(jìn)行合并。對于相同的屬性,后面的對象會覆蓋前面的對象。為什么extend方法第一個參數(shù)是一個空對象呢?因為該方法會將后者合并到前者上,為了不讓 defaults 被改變所以第一個參數(shù)設(shè)為空對象。

如果我們允許使用插件的人能夠設(shè)置默認(rèn)參數(shù),就需要將其暴露出來:

$.fn.foo = function(options) {
  var opts = $.extend({}, $.fn.foo.defaults, options); 
  alert(opts.backgroundColor);
}
$.fn.foo.defaults = {
  color: '#000',
  backgroundColor: 'red'
}

這樣就可以在外部對插件的默認(rèn)參數(shù)進(jìn)行修改了。

適當(dāng)?shù)谋┞兑恍┓椒?/strong>

$.fn.foo = function(options) {
  var opts = $.extend({}, $.fn.foo.defaults, options); 
  $.fn.foo.sayColor(opts.backgroundColor);
}
$.fn.foo.sayColor = function(bgColor) {
  alert(bgColor);
}
$.fn.foo.defaults = {
  color: '#000',
  backgroundColor: 'red'
}

改寫:

$.fn.foo.sayColor = function(bgColor) {
  alert('background color is ' + bgColor);
}

暴露插件中的一部分方法是很牛逼的,它使得別人可以對你的方法進(jìn)行擴(kuò)展、覆蓋。但是當(dāng)別人對你的參數(shù)或方法進(jìn)行修改時,很可能會影響其他很多東西。所以在考慮要不要暴露方法時候要頭腦清楚,不確定的就不要暴露了。

保持函數(shù)的私有性

說到保持私有性,首先想到什么?沒錯,就是閉包:

;(function($) {
  $.fn.foo = function(options) {
    var opts = $.extend({}, $.fn.foo.defaults, options); 
    debug(opts.backgroundColor);
  }
  function debug(bgColors) {
    console.log(bgColors);
  }
  $.fn.foo.defaults = {
    color: '#000',
    backgroundColor: 'red'
  }
})(jQuery)

這是jQuery官方給出的插件開發(fā)方式,好處包括:1.沒有全局依賴 2.避免其他人破壞 3.兼容 '$' 與 'jQuery' 操作符。

如上,debug 方法就成了插件內(nèi)部的私有方法,外部無法對其進(jìn)行修改。在閉包前面加 ; 是防止進(jìn)行代碼合并時,如果閉包前的代碼缺少分號從而導(dǎo)致后面報錯的情況。

合并

;(function($) {
  //定義插件
  $.fn.foo = function(options) {
    //doSomething...
  }
  //私有函數(shù)
  function debug() {
    //doSomething...
  }
  //定義暴露函數(shù)
  $.fn.foo.sayColor = function() {
    //doSomething...
  }
  //插件默認(rèn)參數(shù)
  $.fn.foo.default = {
    color: '#000',
    backgroundColor: 'red'
  }
})(jQuery);

以上的代碼就創(chuàng)建了一個完整且規(guī)范的插件骨架,看起來雖然很簡單但在實際開發(fā)中還是有很多技巧與注意事項,接下來我們通過一個實例來看看。

想了半天,覺得將彈窗做成插件當(dāng)作示例是比較合適的。在開發(fā)之前我們先構(gòu)想一下這個彈窗插件的結(jié)構(gòu)與功能等:

從上圖我們看出包括三個部分,標(biāo)題、內(nèi)容、以及按鈕組。這里需要申明一點,我們不想只做成瀏覽器里默認(rèn)的只包含一個按鈕的alert框,而是使用者可以自定義按鈕數(shù)量,這樣該彈出框也能完成類似confirm框的功能。

搭建插件骨架

function SubType($ele, options) {
  this.$ele = $ele;
  this.opts = $.extend({}, $.fn.popWin.defaults, options);
}
SubType.prototype = {
  createPopWin: function() {
  }
};
$.fn.popWin = function(options) {
  //this指向被jQuery選擇器選中的對象
  var superType = new SubType(this, options);
  superType.createPopWin();
};
$.fn.popWin.defaults = {};

1. 我們創(chuàng)建了基于對象且名為 popWin 方法,并將 defaults 默認(rèn)配置參數(shù)暴露出去以便使用的人進(jìn)行修改;

2. 這里使用面向?qū)ο蟮姆椒▉砉芾砦覀兊乃接泻瘮?shù),createPopWin 方法就是我們私有的用來創(chuàng)建彈窗的函數(shù)。

3. 在插件被調(diào)用時將jq對象與自定義的參數(shù)傳入構(gòu)造函數(shù)中并實例化。

調(diào)用

設(shè)想一下我們該怎么調(diào)用這個插件呢?我們可以在自己的文檔樹中合適的位置插入一個 div 元素,選中該 div 并調(diào)用我們定義在jQuery對象上的 popWin 方法。

$('#content').popWin({
  a: 1,
  b: 2,
  callback: function() {}
});

調(diào)用 popWin 的同時傳入自定義的配置參數(shù),之后被選中的 div 元素就被神奇的轉(zhuǎn)化成一個彈窗了!當(dāng)然,這只是我們的設(shè)想,下面開始碼代碼。

確定默認(rèn)配置

$.fn.popWin.defaults = {
  width: '600', //彈窗寬
  height: '250', //彈窗高
  title: '標(biāo)題', //標(biāo)題
  desc: '描述', //描述
  winCssName: 'pop-win', //彈窗的CSS類名
  titleCssName: 'pop-title', //標(biāo)題區(qū)域的CSS類名
  descCssName: 'pop-desc', //描述區(qū)域的CSS類名
  btnAreaCssName: 'pop-btn-box', //按鈕區(qū)域的CSS類名
  btnCssName: 'pop-btn', //單個按鈕的CSS類名
  btnArr: ['確定'], //按鈕組
  callback: function(){} //點擊按鈕之后的回調(diào)函數(shù)
}

我們定義了如上的參數(shù),為什么有要傳入這么多的CSS類名呢?1. 為了保證JS與CSS盡可能的解耦。 2. 你的樣式有很大可能別人并不適用。所以你需要配置一份樣式表文件來對應(yīng)你的默認(rèn)類名,當(dāng)別人需要更改樣式時可以傳入自己編寫的樣式。

按鈕組為一個數(shù)組,我們的彈窗需要根據(jù)其傳入的數(shù)組長度來動態(tài)的生成若干個按鈕?;卣{(diào)函數(shù)的作用是在用戶點擊了某個按鈕時返回他所點擊按鈕的索引值,方便他進(jìn)行后續(xù)的操作。

彈窗DOM創(chuàng)建

var popWinDom,titleAreaDom,descAreaDom,btnAreaDom;
SubType.prototype = {
  createPopWin: function() {
    var _this = this;
    //首次創(chuàng)建彈窗
    //背景填充整個窗口
    this.$ele.css({
      position: 'fixed',
      top: 0,
      left: 0,
      right: 0,
      bottom: 0,
      backgroundColor: 'rgba(0,0,0,0.4)',
      overflow: 'hidden'
    });
    
    //窗口區(qū)域
    popWinDom = $('<div><div></div><div></div><div></div></div>').css({
      width: this.opts.width,
      height: this.opts.height,
      position: 'absolute',
      top: '30%',
      left: '50%',
      marginLeft: '-' + (this.opts.width.split('px')[0] / 2) + 'px'
    }).attr('class',this.opts.winCssName);
    
    //標(biāo)題區(qū)域
    titleAreaDom = popWinDom.find('div:eq(0)')
              .text(this.opts.title)
              .attr('class',this.opts.titleCssName);
    
    //描述區(qū)域
    descAreaDom = popWinDom.find('div:eq(1)')
            .text(this.opts.desc)
            .attr('class',this.opts.descCssName);
    //按鈕區(qū)域   
    btnAreaDom = popWinDom.find('div:eq(2)')
            .attr('class',this.opts.btnAreaCssName);
    
    //插入按鈕
    this.opts.btnArr.map(function(item, index) {
      btnAreaDom.append($('<button></button>')
        .text(item)
        .attr({'data-index':index, 'class':_this.opts.btnCssName})
        .on('click', function() {
          _this.opts.callback($(this).attr('data-index'));
        }));
      });
    
    this.$ele.append(popWinDom);
  }
}

1. 首先命名了四個變量用來緩存我們將要創(chuàng)建的四個DOM,將傳入的jQuery對象變形成覆蓋整個窗口半透明元素;

2. 創(chuàng)建窗口DOM,根據(jù)傳入的高、寬來設(shè)置尺寸并居中,之后另上傳入的窗口CSS類名;

3. 創(chuàng)建標(biāo)題、描述、按鈕組區(qū)域,并將傳入的標(biāo)題、描述內(nèi)容配置上去;

4. 動態(tài)加入按鈕,并為按鈕加上data-index的索引值。注冊點擊事件,點擊后調(diào)用傳入的回調(diào)函數(shù),將索引值傳回。

好了,我們先看下效果。調(diào)用如下:

$('#content').popWin({
  width: '500',
  height: '200',
  title: '系統(tǒng)提示',
  desc: '注冊成功',
  btnArr: ['關(guān)閉'],
  callback: function(clickIndex) {
    console.log(clickIndex);
  }
});

可以看到一個彈窗的DOM已被渲染到頁面中了,當(dāng)點擊關(guān)閉按鈕時控制臺會打印出 "0",因為按鈕組只有一個值嘛,當(dāng)然是第0個了。

如果我們需要多次調(diào)用這個彈窗,每次都要傳入高、寬我會覺得很麻煩。這時我們可以直接在一開始修改插件內(nèi)部的默認(rèn)配置,這也是我們將默認(rèn)配置暴露的好處:

$.fn.popWin.defaults.width = '500';
$.fn.popWin.defaults.height = '200';

要注意的當(dāng)然是不能直接改變defaults的引用,以免露掉必須的參數(shù)。 這樣以后的調(diào)用都無需傳入尺寸了。

我們加一個按鈕并且傳入一個自定義的樣式看看好使不呢?

$('#content').popWin({
  title: '系統(tǒng)提示',
  desc: '是否刪除當(dāng)前內(nèi)容',
  btnArr: ['確定','取消'],
  winCssName: 'pop-win-red',
  callback: function(clickIndex) {
    console.log(clickIndex);
  }
});

可以看到都是生效了的,當(dāng)點擊“確定”按鈕時回調(diào)函數(shù)返回 0,點擊“取消”按鈕時回調(diào)函數(shù)返回 1。這樣使用插件的人就知道自己點擊的是哪一個按鈕,以完成接下來的操作。

顯示&隱藏

接下來要進(jìn)行打開、關(guān)閉彈窗功能的開發(fā)?;叵肷厦娼榻B的概念,我們想讓使用該插件的人能夠?qū)@兩個方法進(jìn)行擴(kuò)展或者重寫,所以將這兩個方法暴露出去:

$.fn.popWin.show = function($ele) {
  $ele.show();
}
$.fn.popWin.hide = function($ele) {
  $ele.hide();
}

之后在createPopWin方法中需要的地方調(diào)用這兩個方法。

這里多強調(diào)一點,也是做彈窗控件不可避免的一點:只有當(dāng)我們點擊按鈕以及灰色背景區(qū)域時允許彈窗關(guān)閉,點擊彈窗其他地方不允許關(guān)閉。由于彈窗屬于整個灰色區(qū)域的子節(jié)點,必然牽扯到的就是事件冒泡的問題。

所以在給最外層加上點擊關(guān)閉的事件時,要在彈窗區(qū)域阻止事件冒泡。

popWinDom = $('<div><div></div><div></div><div></div></div>').css({
  width: this.opts.width,
  height: this.opts.height,
  position: 'absolute',
  top: '30%',
  left: '50%',
  marginLeft: '-' + (this.opts.width.split('px')[0] / 2) + 'px'
}).attr('class',this.opts.winCssName).on('click', function(event) {
  event.stopPropagation();
});

二次打開

我們只需要在第一次調(diào)用插件時創(chuàng)建所有創(chuàng)建DOM,第二次調(diào)用時只更改其參數(shù)即可,所以在createPopWin方法最前面加入如下方法:

if (popWinDom) { //彈窗已創(chuàng)建
  popWinDom.css({
    width: this.opts.width,
    height: this.opts.height
  }).attr('class',this.opts.winCssName);
  titleAreaDom.text(this.opts.title).attr('class',this.opts.titleCssName);
  descAreaDom.text(this.opts.desc).attr('class',this.opts.descCssName);
  btnAreaDom.html('').attr('class',this.opts.btnAreaCssName);
  this.opts.btnArr.map(function(item, index) {
    btnAreaDom.append($('<button></button>')
      .text(item)
      .attr('data-index',index)
      .attr('class',_this.opts.btnCssName)
      .on('click', function() {
        _this.opts.callback($(this).attr('data-index'));
        $.fn.popWin.hide(_this.$ele);
      }));
    });
  $.fn.popWin.show(this.$ele);
  return;
}

合并整個插件代碼

;(function($) {
  function SubType(ele, options) {
    this.$ele = ele;
    this.opts = $.extend({}, $.fn.popWin.defaults, options);
  }
  var popWinDom,titleAreaDom,descAreaDom,btnAreaDom;
  SubType.prototype = {
    createPopWin: function() {
      var _this = this;
      if (popWinDom) { //彈窗已創(chuàng)建
        popWinDom.css({
          width: this.opts.width,
          height: this.opts.height
        }).attr('class',this.opts.winCssName);
        titleAreaDom.text(this.opts.title).attr('class',this.opts.titleCssName);
        descAreaDom.text(this.opts.desc).attr('class',this.opts.descCssName);
        btnAreaDom.html('').attr('class',this.opts.btnAreaCssName);
        this.opts.btnArr.map(function(item, index) {
          btnAreaDom.append($('<button></button>')
            .text(item)
            .attr('data-index',index)
            .attr('class',_this.opts.btnCssName)
            .on('click', function() {
              _this.opts.callback($(this).attr('data-index'));
              $.fn.popWin.hide(_this.$ele);
            }));
        });
        $.fn.popWin.show(this.$ele);
        return;
      }
      //首次創(chuàng)建彈窗
      this.$ele.css({
        position: 'fixed',
        top: 0,
        left: 0,
        right: 0,
        bottom: 0,
        backgroundColor: 'rgba(0,0,0,0.4)',
        overflow: 'hidden',
        display: 'none'
      }).on('click', function() {
        $.fn.popWin.hide(_this.$ele);
      });
      popWinDom = $('<div><div></div><div></div><div></div></div>').css({
        width: this.opts.width,
        height: this.opts.height,
        position: 'absolute',
        top: '30%',
        left: '50%',
        marginLeft: '-' + (this.opts.width.split('px')[0] / 2) + 'px'
      }).attr('class',this.opts.winCssName).on('click', function(event) {
        event.stopPropagation();
      });
      titleAreaDom = popWinDom.find('div:eq(0)')
              .text(this.opts.title)
              .attr('class',this.opts.titleCssName);
      descAreaDom = popWinDom.find('div:eq(1)')
              .text(this.opts.desc)
              .attr('class',this.opts.descCssName);
      btnAreaDom = popWinDom.find('div:eq(2)')
              .attr('class',this.opts.btnAreaCssName);
      this.opts.btnArr.map(function(item, index) {
        btnAreaDom.append($('<button></button>')
          .text(item)
          .attr({'data-index':index, 'class':_this.opts.btnCssName})
          .on('click', function() {
            _this.opts.callback($(this).attr('data-index'));
            $.fn.popWin.hide(_this.$ele);
          }));
      });
      this.$ele.append(popWinDom);
      $.fn.popWin.show(this.$ele);
    }
  }
  $.fn.popWin = function(options) {
    var superType = new SubType(this, options);
    superType.createPopWin();
    return this;
  }
  $.fn.popWin.show = function($ele) {
    $ele.show();
  }
  $.fn.popWin.hide = function($ele) {
    $ele.hide();
  }
  $.fn.popWin.defaults = {
    width: '600',
    height: '250',
    title: 'title',
    desc: 'description',
    winCssName: 'pop-win',
    titleCssName: 'pop-title',
    descCssName: 'pop-desc',
    btnAreaCssName: 'pop-btn-box',
    btnCssName: 'pop-btn',
    btnArr: ['確定'],
    callback: function(){}
  }
})(jQuery);

如上,一個完整的彈窗插件就在這里了。

說下這個標(biāo)紅的 return this 是干什么用的,前面已說過 this 在這里是被選中的jQuery對象。將其return就可以在調(diào)用完我們的插件方法后可以繼續(xù)調(diào)用jQ對象上的其他方法,也就是jQuery的鏈?zhǔn)讲僮鳎f玄乎點就叫級聯(lián)函數(shù)。

OK!趁熱打鐵,我們來看看暴露出去的兩個方法重寫之后效果怎么樣,畢竟對插件暴露部分的擴(kuò)展和重寫是很牛逼的一塊東西。

想象個情景,你用了這個插件后覺得簡單的show和hide效果簡直是low爆了,決定重寫這個彈出和隱藏的效果:

$.fn.popWin.show = function($ele) {
  $ele.children().first().css('top','-30%').animate({top:'30%'},500);
  $ele.show();
}
$.fn.popWin.hide = function($ele) {
  $ele.children().first().animate({top:'-30%'},500,function() {
    $ele.hide();
  });
}

你在自己的代碼里加上上面兩段,然后發(fā)現(xiàn)彈窗有了一個簡單的上下滑動進(jìn)入屏幕的效果,同時又不會影響我們彈窗的創(chuàng)建,證明我們的暴露方法還算合理。

當(dāng)然你也可以讓它豎著進(jìn)、橫著進(jìn)、翻著跟頭進(jìn),這就看你自己了。

最后貼上默認(rèn)的樣式表,為了急著想粘回去試試的同學(xué)們。

.pop-win {
  border: 1px solid #fff;
  padding: 10px;
  background-color: #fff;
  -wekbit-border-radius: 6px;
  border-radius: 6px;
  -webkit-box-shadow: 0 3px 9px rgba(0,0,0,0.3);
  box-shadow: 0 3px 9px rgba(0,0,0,0.3);
}
.pop-win-red {
  padding: 10px;
  background-color: red;
  -wekbit-border-radius: 6px;
  border-radius: 6px;
  -webkit-box-shadow: 0 3px 9px rgba(0,0,0,0.3);
  box-shadow: 0 3px 9px rgba(0,0,0,0.3);
}
.pop-title {
  width: 100%;
  height: 20%;
  line-height: 40px;
  padding-left: 10px;
  box-sizing: border-box;
  border-bottom: 1px solid #eee;
  font-size: 17px;
  font-weight: bold;
}
.pop-desc {
  width: 100%;
  height: 60%;
  box-sizing: border-box;
  padding: 10px 0 0 10px;
  border-bottom: 1px solid #eee;
}
.pop-btn-box {
  width: 100%;
  height: 20%;
  text-align: right;
}
.pop-btn {
  margin: 10px 10px 0 0;
  width: 60px;
  height: 30px;
}

當(dāng)然這只是個編寫插件的例子,如果要拿出去使用還需要仔細(xì)打磨。例子雖然簡單,旨在拋磚引玉。

相關(guān)文章

最新評論