成人精品一区二区三区中文字幕-成人精品一区二区三区-成人精品一级毛片-成人精品亚洲-日本在线视频一区二区-日本在线视频免费

導航首頁 ? 技術教程 ? jQuery彈簧插件編寫基礎之“又見彈窗”
全站頭部文字 我要出現在這里
jQuery彈簧插件編寫基礎之“又見彈窗” 629 2024-03-17   

本文將通過一個實例來引出jQuery插件開發中的一些細節,首先介紹下jQuery插件開發的一些基礎知識。

jQuery的插件開發主要分為兩類:

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

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

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

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

類級別的插件開發

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

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

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

對象級別的插件開發

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

原來是原型鏈啊。。。

接收配置參數

在編寫一個插件時,我們可以讓使用插件的人能按自己的意愿設置插件的一些屬性,這就需要插件有接收參數的功能,同時當使用插件的人不傳入參數時,插件內部也有一套自己默認的配置參數。

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

這里的關鍵就是 $.extend 方法,它能夠將對象進行合并。對于相同的屬性,后面的對象會覆蓋前面的對象。為什么extend方法第一個參數是一個空對象呢?因為該方法會將后者合并到前者上,為了不讓 defaults 被改變所以第一個參數設為空對象。

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

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

這樣就可以在外部對插件的默認參數進行修改了。

適當的暴露一些方法

$.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);
}

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

保持函數的私有性

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

;(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官方給出的插件開發方式,好處包括:1.沒有全局依賴 2.避免其他人破壞 3.兼容 '$' 與 'jQuery' 操作符。

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

合并

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

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

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

從上圖我們看出包括三個部分,標題、內容、以及按鈕組。這里需要申明一點,我們不想只做成瀏覽器里默認的只包含一個按鈕的alert框,而是使用者可以自定義按鈕數量,這樣該彈出框也能完成類似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. 我們創建了基于對象且名為 popWin 方法,并將 defaults 默認配置參數暴露出去以便使用的人進行修改;

2. 這里使用面向對象的方法來管理我們的私有函數,createPopWin 方法就是我們私有的用來創建彈窗的函數。

3. 在插件被調用時將jq對象與自定義的參數傳入構造函數中并實例化。

調用

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

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

調用 popWin 的同時傳入自定義的配置參數,之后被選中的 div 元素就被神奇的轉化成一個彈窗了!當然,這只是我們的設想,下面開始碼代碼。

確定默認配置

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

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

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

彈窗DOM創建

var popWinDom,titleAreaDom,descAreaDom,btnAreaDom;
SubType.prototype = {
  createPopWin: function() {
    var _this = this;
    //首次創建彈窗
    //背景填充整個窗口
    this.$ele.css({
      position: 'fixed',
      top: 0,
      left: 0,
      right: 0,
      bottom: 0,
      backgroundColor: 'rgba(0,0,0,0.4)',
      overflow: 'hidden'
    });
    
    //窗口區域
    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);
    
    //標題區域
    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'));
        }));
      });
    
    this.$ele.append(popWinDom);
  }
}

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

2. 創建窗口DOM,根據傳入的高、寬來設置尺寸并居中,之后另上傳入的窗口CSS類名;

3. 創建標題、描述、按鈕組區域,并將傳入的標題、描述內容配置上去;

4. 動態加入按鈕,并為按鈕加上data-index的索引值。注冊點擊事件,點擊后調用傳入的回調函數,將索引值傳回。

好了,我們先看下效果。調用如下:

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

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

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

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

要注意的當然是不能直接改變defaults的引用,以免露掉必須的參數。 這樣以后的調用都無需傳入尺寸了。

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

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

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

顯示&隱藏

接下來要進行打開、關閉彈窗功能的開發?;叵肷厦娼榻B的概念,我們想讓使用該插件的人能夠對這兩個方法進行擴展或者重寫,所以將這兩個方法暴露出去:

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

之后在createPopWin方法中需要的地方調用這兩個方法。

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

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

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();
});

二次打開

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

if (popWinDom) { //彈窗已創建
  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) { //彈窗已創建
        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;
      }
      //首次創建彈窗
      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);

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

說下這個標紅的 return this 是干什么用的,前面已說過 this 在這里是被選中的jQuery對象。將其return就可以在調用完我們的插件方法后可以繼續調用jQ對象上的其他方法,也就是jQuery的鏈式操作,說玄乎點就叫級聯函數。

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

想象個情景,你用了這個插件后覺得簡單的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();
  });
}

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

當然你也可以讓它豎著進、橫著進、翻著跟頭進,這就看你自己了。

最后貼上默認的樣式表,為了急著想粘回去試試的同學們。

.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;
}

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



主站蜘蛛池模板: 美姐妹| 张子枫的全部作品| 监狱风云美剧| 意外的春天| 大奉打更人电视剧免费在线观看| 正宗辅星水法九星断语| 性视频播放| 每天一分钱每天翻一倍连续30天| 黄瓜在线| 夜半2点钟| free xxxx japan| 性感瑜伽| 周秀娜三级大尺度视频| 湖南金鹰卡通节目表| 溜冰圆舞曲音乐教案| 美女热吻| 电影白上之黑| cctv体育频道5| 电影《斯大林格勒》| 婚前婚后电影高清完整版| 小丑与小丑女| 巴霍巴利王2国语版免费中文版| 哈基米| b超怎么看是男孩女孩| 电影交换| 挠丝袜| 李慧珍演的电影有哪些| 比基尼诱惑| 黄秋生的电影| www.56.com| 娄际成| 无耻之徒英文| 护工电影| 黑势力| 电子版租房合同免费下载| 38在线电影| 特殊的精油| 《棋魂》电视剧| 哑妻| 真爱到永远电影在线看| 房事性生活|

?。。≌鹃L長期在線接!!!

網站、小程序:定制開發/二次開發/仿制開發等

各種疑難雜癥解決/定制接口/定制采集等

站長微信:lxwl520520

站長QQ:1737366103