[js插件開發教程]原生js仿jquery架構擴展開發選項卡插件

来源:http://www.cnblogs.com/ghostwu/archive/2017/10/20/7700288.html
-Advertisement-
Play Games

jquery插件一般是這麼乾的: $.fn.插件名稱 = function(){}, 把插件的名稱加在.fn上,在源碼裡面實際上是擴展到構造函數的原型對象上,如果你沒看過jquery的源代碼,或者你曾經看過,但是不知道為什麼把插件擴展到fn上,那麼本篇文章就能解答你的疑惑。關於jquery插件開發方 ...


jquery插件一般是這麼乾的: $.fn.插件名稱 = function(){}, 把插件的名稱加在.fn上,在源碼裡面實際上是擴展到構造函數的原型對象上,如果你沒看過jquery的源代碼,或者你曾經看過,但是不知道為什麼把插件擴展到fn上,那麼本篇文章就能解答你的疑惑。關於jquery插件開發方式,可以參考我的這篇文章:[js高手之路]jquery插件開發實戰-選項卡詳解

關於選項卡這個功能具體怎麼做,不在這裡詳解,這個是入門級的功能,本文重在討論插件開發的架構,擴展,以及參數設置。

如果你使用過jquery的選項卡插件,或者其他類型的插件,他們一般都是這麼調用的:

$( ".tab" ).tabs( {} )

$(".tab").tabs( function(){} );

一種是傳遞參數定製插件行為

一種是傳遞函數定製插件行為

$(".tab") 選擇到元素,然後返回的是jquery對象

tabs方法擴展在fn上就是擴展都jquery構造函數的原型對象上, 那麼對象( $(".tab") )調用原型對象上的方法( tabs )當然就順利成章了。

所以jquery插件擴展本質就是: 構造函數 + 原型對象擴展方法

定義一個構造+原型的方式,下麵代碼的原理,我在這篇文章有詳細論述:[js高手之路] 設計模式系列課程 - jQuery的鏈式調用與靈活的構造函數

 1 var G = function( selectors, context ){
 2         return new G.fn.init( selectors, context );
 3     }
 4     G.fn = G.prototype = {
 5         length : 0,
 6         constructor : G,
 7         size : function(){
 8             return this.length;
 9         },
10         init : function( selector, context ){
11             this.length = 0;
12             context = context || document;
13             if ( selector.indexOf( '#' ) == 0 ){
14                 this[0] = document.getElementById( selector.substring( 1 ) );
15                 this.length = 1;
16             }else {
17                 var aNode = context.querySelectorAll( selector );
18                 for( var i = 0, len = aNode.length; i < len; i++ ) {
19                     this[i] = aNode[i];
20                 }
21                 this.length = len;
22             }
23             this.selector = selector;
24             this.context = context;
25             return this;
26         }
27     }
28 
29     G.fn.init.prototype = G.fn;
View Code

接下來,我們還要添加一個插件擴展機制:

 1 G.extend = G.fn.extend = function () {
 2         var i = 1,
 3             len = arguments.length,
 4             dst = arguments[0],
 5             j;
 6         if (dst.length === undefined) {
 7             dst.length = 0;
 8         }
 9         if (i == len) {
10             dst = this;
11             i--;
12         }
13         for (; i < len; i++) {
14             for (j in arguments[i]) {
15                 dst[j] = arguments[i][j];
16                 dst.length++;
17             }
18         }
19         return dst;
20     };
View Code

在這篇文章:[js高手之路] 設計模式系列課程 - jQuery的extend插件機制 有詳細的論述,extend插件擴展機制

像使用jquery一樣暴露介面:

var $ = function( selectors, context ){
return G( selectors, context );
}
window.$ = $;

這個插件的擴展機制和元素選擇機制就完成了,如果要擴展插件,只要在

G.fn上擴展插件的名稱即可,如:

 1 G.fn.tabs = function( options ){
 2     options = options || {};
 3     var defaults = {
 4         contentClass : 'tab-content',
 5         navClass : 'tab-nav',
 6         activeClass : 'active',
 7         triggerElements : '*',
 8         activeIndex : 0,
 9         evType : 'click',
10         effect : 'none'
11     };
12 
13     var opt = G.extend( {}, defaults, options );
14     return this;
15 }

這樣,我們就在G的原型對象上擴展了一個tabs( 選項卡 )插件

options可以定製插件的行為:

contentClass : 'tab-content',     選項卡內容區域的class名稱
navClass : 'tab-nav', 標簽卡區域的class名稱
activeClass : 'active', 標簽卡預設選擇的class名稱:active
triggerElements : '*', 標簽卡預設觸發元素
activeIndex : 0, 預設選中第幾個標簽卡
evType : 'click', 選項卡觸發的事件類型
effect : 'none' 是否有過渡特效:如透明度


var opt = G.extend( {}, defaults, options );

這一段是把定製的配置和預設配置合成到一個對象opt裡面,後面的插件行為,就可以根據opt的配置進行定製,這是插件開發參數定製中,常用的一招。

這樣做的好處,可以防止污染預設配置defaults

1  var tabContent = this[0].querySelector( "." + opt.contentClass );
2         var tabContentEle = tabContent.children;
3         var tabNavEle = this[0].querySelectorAll( "." + opt.navClass + '>' + opt.triggerElements );
4 
5         var _contentLen = tabContentEle.length;
6         var _index = opt.activeIndex;

獲取對應的元素。

有了選項卡的元素和配置,我們就開始做業務處理(為所有選項卡添加處理的事件,進行選項卡切換)

定義一個專門的對象_api = {}, 擴展業務api

 1 G.fn.tabs = function( options ){
 2         options = options || {};
 3         var defaults = {
 4             contentClass : 'tab-content',
 5             navClass : 'tab-nav',
 6             activeClass : 'active',
 7             triggerElements : '*',
 8             activeIndex : 0,
 9             evType : 'click',
10             effect : 'none'
11         };
12 
13         var opt = G.extend( {}, defaults, options );
14 
15         var tabContent = this[0].querySelector( "." + opt.contentClass );
16         var tabContentEle = tabContent.children;
17         var tabNavEle = this[0].querySelectorAll( "." + opt.navClass + '>' + opt.triggerElements );
18 
19         var _contentLen = tabContentEle.length;
20         var _index = opt.activeIndex;
21 
22         var _api = {};
23 
24         _api.setIndex = function( index ){
25             //當前標簽加上active樣式,其餘標簽刪除active樣式
26             for ( var i = 0; i < _contentLen; i++ ) {
27                 if ( tabNavEle[i].classList.contains( 'active' ) ) {
28                     tabNavEle[i].classList.remove('active');
29                 }
30             }
31             tabNavEle[index].classList.add( 'active' );
32             switch ( opt.effect ){
33                 case 'fade':
34                     break;
35                 default:
36                     for ( var i = 0; i < _contentLen; i++ ) {
37                         tabContentEle[i].style.display = 'none';
38                     }
39                     tabContentEle[index].style.display = 'block';
40                     _index = index;
41             }
42         }
43 
44         _api.setIndex( _index ); //預設的選項卡
45 
46         //所有的標簽綁定事件
47         for( var i = 0, len = tabNavEle.length; i < len; i++ ) {
48             tabNavEle[i].index = i;
49             tabNavEle[i].addEventListener( opt.evType, function(){
50                 var i = this.index;
51                 _api.setIndex( i );
52             }, false );
53         }
54 
55         return this;
56     }
View Code

完整的插件代碼:

  1 /**
  2  * Created by ghostwu(吳華).
  3  */
  4 (function(){
  5     var G = function( selectors, context ){
  6         return new G.fn.init( selectors, context );
  7     }
  8     G.fn = G.prototype = {
  9         length : 0,
 10         constructor : G,
 11         size : function(){
 12             return this.length;
 13         },
 14         init : function( selector, context ){
 15             this.length = 0;
 16             context = context || document;
 17             if ( selector.indexOf( '#' ) == 0 ){
 18                 this[0] = document.getElementById( selector.substring( 1 ) );
 19                 this.length = 1;
 20             }else {
 21                 var aNode = context.querySelectorAll( selector );
 22                 for( var i = 0, len = aNode.length; i < len; i++ ) {
 23                     this[i] = aNode[i];
 24                 }
 25                 this.length = len;
 26             }
 27             this.selector = selector;
 28             this.context = context;
 29             return this;
 30         }
 31     }
 32 
 33     G.fn.init.prototype = G.fn;
 34     G.extend = G.fn.extend = function () {
 35         var i = 1,
 36             len = arguments.length,
 37             dst = arguments[0],
 38             j;
 39         if (dst.length === undefined) {
 40             dst.length = 0;
 41         }
 42         if (i == len) {
 43             dst = this;
 44             i--;
 45         }
 46         for (; i < len; i++) {
 47             for (j in arguments[i]) {
 48                 dst[j] = arguments[i][j];
 49                 dst.length++;
 50             }
 51         }
 52         return dst;
 53     };
 54 
 55     G.fn.tabs = function( options ){
 56         options = options || {};
 57         var defaults = {
 58             contentClass : 'tab-content',
 59             navClass : 'tab-nav',
 60             activeClass : 'active',
 61             triggerElements : '*',
 62             activeIndex : 0,
 63             evType : 'click',
 64             effect : 'none'
 65         };
 66 
 67         var opt = G.extend( {}, defaults, options );
 68 
 69         var tabContent = this[0].querySelector( "." + opt.contentClass );
 70         var tabContentEle = tabContent.children;
 71         var tabNavEle = this[0].querySelectorAll( "." + opt.navClass + '>' + opt.triggerElements );
 72 
 73         var _contentLen = tabContentEle.length;
 74         var _index = opt.activeIndex;
 75 
 76         var _api = {};
 77 
 78         _api.setIndex = function( index ){
 79             //當前標簽加上active樣式,其餘標簽刪除active樣式
 80             for ( var i = 0; i < _contentLen; i++ ) {
 81                 if ( tabNavEle[i].classList.contains( 'active' ) ) {
 82                     tabNavEle[i].classList.remove('active');
 83                 }
 84             }
 85             tabNavEle[index].classList.add( 'active' );
 86             switch ( opt.effect ){
 87                 case 'fade':
 88                     break;
 89                 default:
 90                     for ( var i = 0; i < _contentLen; i++ ) {
 91                         tabContentEle[i].style.display = 'none';
 92                     }
 93                     tabContentEle[index].style.display = 'block';
 94                     _index = index;
 95             }
 96         }
 97 
 98         _api.setIndex( _index ); //預設的選項卡
 99 
100         //所有的標簽綁定事件
101         for( var i = 0, len = tabNavEle.length; i < len; i++ ) {
102             tabNavEle[i].index = i;
103             tabNavEle[i].addEventListener( opt.evType, function(){
104                 var i = this.index;
105                 _api.setIndex( i );
106             }, false );
107         }
108 
109         return this;
110     }
111 
112     var $ = function( selectors, context ){
113         return G( selectors, context );
114     }
115     window.$ = $;
116 })();
View Code

選項卡佈局:

 1 <!DOCTYPE html>
 2 <html>
 3 <head lang="en">
 4     <!--作者:ghostwu(吳華)-->
 5     <meta charset="UTF-8">
 6     <title>選項卡插件 - by ghostwu</title>
 7     <link rel="stylesheet" href="css/tab.css"/>
 8     <script src="./js/tab.js"></script>
 9     <script>
10         window.onload = function () {
11 //            console.log( $(".tab1 .tab-nav li") );
12 //            $( ".tab1" ).tabs( { 'evType' : 'mouseenter' } );
13             $( ".tab1" ).tabs();
14         }
15     </script>
16 </head>
17 <body>
18 <div class="main">
19     <div class="tab tab1">
20         <ul class="tab-nav">
21             <li class="active"><a href="javascript:;">標簽1</a></li>
22             <li><a href="javascript:;">標簽2</a></li>
23             <li><a href="javascript:;">標簽3</a></li>
24             <li><a href="javascript:;">標簽4</a></li>
25         </ul>
26         <div class="tab-content">
27             <p>內容1</p>
28             <p>內容2</p>
29             <p>內容3</p>
30             <p>內容4</p>
31         </div>
32     </div>
33 </div>
34 </body>
35 </html>
View Code

選項卡插件樣式:

 1 * {
 2     margin: 0;
 3     padding: 0;
 4 }
 5 body {
 6     font-size: 14px;
 7     font-family: Tahoma, Verdana,"Microsoft Yahei";
 8 }
 9 a{
10     text-decoration: none;
11     color:#000;
12 }
13 ul,li{
14     list-style-type: none;
15 }
16 img {
17     border:none;
18 }
19 .main {
20     width:960px;
21     margin:20px auto;
22 }
23 .tab{
24     margin: 0 auto 20px;
25 }
26 .tab1 .tab-nav{
27     margin-bottom:8px;
28 }
29 .tab .tab-nav {
30     overflow:hidden;
31 }
32 .tab1 .tab-nav .active{
33     border-bottom:1px solid #000;
34 }
35 .tab1 .tab-nav li {
36     float:left;
37     margin:0 10px;
38 }
39 .tab1 .tab-nav li a {
40     line-height:40px;
41     display:block;
42 }
43 .tab1 .tab-content {
44     height:250px;
45     overflow:hidden;
46 }
47 .tab1 .tab-content p {
48     height:250px;
49     background:#eee;
50 }
View Code

最終效果:

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 什麼是狀態者模式? 每個對象都有其對應的狀態,而每個狀態又對應一些相應的行為,如果某個對象有多個狀態時,那麼就會對應很多的行為。那麼對這些狀態的判斷和根據狀態完成的行為,就會導致多重條件語句,並且如果添加一種新的狀態時,需要更改之前現有的代碼。這樣的設計顯然違背了開閉原則。狀態模式正是用來解決這樣的 ...
  • mybatis實際上是一個更多關註sql語句的框架,他的出現是想讓開發者更簡單的去操作資料庫。 與hibernate相比較,hibernate更多的是去sql化,雖然hibernate也可以本地sql執行,hibernate更多的是關註與hql的編寫和對象映射的配置, 只要配置完了,hibernat ...
  • { let list = new Set(); list.add(5); list.add(7); console.log('size', list, list.size); //{5, 7} 2 } { let arr = [1, 2, 3, 4, 5]; let list = new Set(a... ...
  • 這個問題是我在公司需求的時候遇到的,QQ推廣工具網站獲取的鏈接在蘋果自帶瀏覽器沒法打開到聊天界面,是因為safair在打開到app store的時候把參數給丟了,app store再打開到QQ的時候就無法打開到鏈接所屬人的聊天界面。 在瀏覽器中可以通過JS代碼打開QQ並彈出聊天界面,一般作為客服QQ ...
  • 移動端前端開發較PC端開發最大的不同之一就是需要適配各種大小不同的屏幕尺寸,如何實現辛苦編寫的 H5 頁面在各個機型屏幕上都能得到最佳展示?設計師設計的視覺稿為什麼都是2倍稿? ...
  • 此次的亮點不是three.js的3d部分,而是通過調用攝像頭然後通過攝像頭的圖像變化進行簡單的判斷後進行一些操作。上篇中我通過簡單的示例分析來學習three.js,這次是通過上一篇的一些代碼來與攝像頭判斷部分的代碼相互結合,弄一個新的東西,可以看下圖 說明 這次的示例是我們可以通過一個攝像頭隔空控制 ...
  • 項目中之前需要分頁插件,以前用的都是單純叫做pagenation.js的插件,但是這次集成的時候,項目組一個孩紙用了這個插件,結合網上的例子琢磨了一把。其實大致流程都是相同的,主要將我在用這個分頁插件的一些心得分享出來: 1、分頁插件引入html中需要: bootstrap.css 分頁插件js 自 ...
  • Ionic--構建APP側邊欄 底部選項卡 輪播圖 載入動畫;最近正在學習手機移動端,本文就Ionic的使用,簡單示例了一些樣式。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...