框架設計—選擇器模塊

来源:http://www.cnblogs.com/delicate/archive/2016/04/11/5376623.html
-Advertisement-
Play Games

一個禮拜沒動靜了,實在是懶惰。。 好了,不扯淡了,進入正題:框架封裝之選擇器模塊。 首先,我們為什麼要封裝框架? 淺顯的文字不具有良好的說服性,來做幾個題目吧: 求一個數組所有項之和 2. 求數組中最大值 3. 獲取數組中指定值 ok,題目做到這就行了,我們可以看出每道題目中都用到了for迴圈,每次 ...


一個禮拜沒動靜了,實在是懶惰。。

好了,不扯淡了,進入正題:框架封裝之選擇器模塊。

首先,我們為什麼要封裝框架?

淺顯的文字不具有良好的說服性,來做幾個題目吧:

  1. 求一個數組所有項之和

//求和
 var sum=0;                      
 for(var i=0; i<arr.length;i++){ 
     sum+=arr[i];                
 }                               
  console.log(sum);             

  2. 求數組中最大值

//求最大值
var
max=arr[0]; for(var i=0; i<arr.length;i++){ if(max<arr[i]){ max=arr[i]; } } console.log(max)

 3. 獲取數組中指定值

//獲取指定值                 
var index=0;              
function get(v){          
    for(var i in arr){    
        if(arr[i]===v){   
            index=i;      
            break;        
        }                 
    }                     
}                         

ok,題目做到這就行了,我們可以看出每道題目中都用到了for迴圈,每次都寫一遍是不是很麻煩,這裡代碼不多,可能感覺還好,如果工作量大,冗餘度是很高的,所以我們就可以封裝這個for迴圈:

//  arr: 目標數組                                                          
//  fn: 自定義方法                                                         
    var each= function ( arr , fn ) {                                   
       for(var i=0; i<arr.length;i++){                                  
           // 使用call改變作用域,後面調用可直接使用this                                 
           if( fn.call(arr[i],i,arr[i])===false) break;                 
           //做搜索操作,break必須寫到迴圈中,此處使用返回值檢索是否執行break                      
       }                                                                
   }                                                                    

封裝後重寫求和方法

  //1.封裝後求和                 
  var sum=0;                
  each(arr,function(i,v){   
     sum+=v;                
 })                         

好了,其他就不寫了,比較簡單,所以框架主要是為了降低程式之間的依賴性和耦合性,使重用性達到最高,下麵說說我們的重點,如何封裝選擇器框架。

目標:實現常見選擇器框架的封裝,即id選擇器,類選擇器,標簽選擇器。

//傳統做法
function getId(id){
        return document.getElementById(id);
    }
function getTagName(name){
        return document.getElementsByTagName(name);
    }
//獲取id為box的節點,然後操作
var dom = getId('box');
'red';
dom.style.background = 'red';
//獲取屬性名為div 的節點,然後操作
var tags = getTagName('div');
for(var i=0; i<tags.length;i++){
  tags[i].style.background = 'red';
}

傳統做法如上,可以通過該方法實現普通需求,但是要是繼續想通過類名,標簽名獲得屬性,然後分別加樣式,豈不是很累,所以我們可以通過下麵方式取代原始方法:

//id 選擇器
    function getId(id,node,arr){
         arr.push(node.getElementById(id));
        return arr;
    }
// 屬性選擇器
    function getName(name,node,arr){
        [].push.apply(arr,node.getElementsByTagName(name));
        return arr;
    }
// 類選擇器
    function getClass(clas,node,arr){
        [].push.apply(arr,node.getElementsByClassName(classname));
        return arr;
    }

 緊接著我們再實現一個介面, 根據輸入,調用不同的選擇器

//多選擇器介面
//    getType( tag , node , arr )
//                類型   父節點  數組容器

    function getType(tag,node,arr){
        node= node || document;
        arr= arr || [];
              rag(1) rag(2) rag(3) rag(4)
var rag=/^(?:#([\w\-]+)|\.([\w\-]+)|([\w\-]+)|(\*))$/; var type=rag.exec(tag); if(type){ if(type[1]) arr=getId(type[1],node,arr); else if(type[2]) arr=getClass(type[2],node,arr); else arr=getName(type[3]||'*',node,arr); } return arr; }

上文中用到了正則表達式,簡要解釋下,/^(?:#([\w\-]+)|\.([\w\-]+)|([\w\-]+)|(\*))$/

^表示已xx開頭,$表示已xx結尾; 

(?:)其中'( )'表示分組,此處使用只是提升中間部分優先順序,使用 ?: 取消分組; 

匹配 id:#([\w\-]+)  可通過tag(1)獲取除#外的id字元

匹配 類:\.([\w\-]+)    tag(2)

匹配 標簽名:([\w\-]+)        tag(3)

通配符:(\*)        tag(4)

PS:
var arr1=[1,3,4];
var arr2=[3,4,5];

如果我們要把 arr2展開,然後一個一個追加到arr1中去,最後讓arr1=[1,3,4,3,4,5]
arr1.push(arr2)顯然是不行的。 因為這樣做會得到[1,3,4,[3,4,5]]

但是我們可以用 Array.prototype.push.apply(arr1,arr2) 實現需求
因此我們這樣做 [].push.apply(arr,node.getElementsByTagName(name));

但是,在IE8 及低於IE8以下的瀏覽器需要註意幾個問題.

1、 apply 傳參不接受類似 {0:'a',1:'b',length:2} 的對象,可以是 數組、arguments、  HTMLCollection 對象 和 Nodelist 對象等節點集合.

在這種情況下你也許想要把傳參對象轉換成數組.

2、節點集合無法調用數組的原型方法,但是 類似 {0:'a',1:'b',length:2} 的對象可以。

 看似我們實現了所有需求,但是別忘記另我們頭疼的IE8瀏覽器,它不支持 getElementsByClassName 為此我們需要解決相容問題。
您也許會想到以下方法:
 if(document.getElementsByClassName){
        var results= document.getElementsByClassName('box');
    }
    else {
        //自己實現的方法
    }

 

但是存在兩個問題 1.性能問題 2.安全問題
先解決性能問題 :我們都知道原型鏈吧,當函數或對象執行某方法或使用屬性時,會從其自身開始搜尋,然後一直沿著原型鏈往上找,直至找到為止才停止尋找,每往上一層,都會增加搜尋時間,降低性能,getElementsByClassName是
document的屬性方法,因此最好將該屬性放在當前對象中,而且每次執行時都會判斷是否相容,也降低性能,因此我們可以用support對象緩存能力檢測結果,如下
1.提升性能
 var support={};
//!! 裝換成boolean類型
support.getElementsByClassName =!!document.getElementsByClassName;
   if(support.getElementsByClassName){
       var results= document.getElementsByClassName('box');
   }
   else {
       //自己實現的方法
   }

 

減少了性能問題,但是存在安全問題 ,看下麵代碼會輸出什麼?
document.getElementsByClassName=123;  //註入代碼攻擊
    var support={};
    support.getElementsByClassName = !!document.getElementsByClassName;
    if(support.getElementsByClassName){
        console.log('支持ByClassName');
    }
    else {
        console.log('不支持ByClassName');
    }

 

無論在什麼瀏覽器中都會輸出: 支持ByClassName.
因為document.getElementsByClassName=123;這句代碼,此時執行判斷是true,具體傳送門==>http://blog.csdn.net/writehappy/article/details/8970491
告訴你們一個大牛們使用註入代碼攻擊的做的事:在chrome上為自己的微博刷贊!!!
好了,轉回整體,如何解決這個問題呢?
繼續看操作:
2.提升安全性
//    在全局作用域(最終要變成沙箱模式),提供一個support對象,存儲屬性是否支持,不用每次檢測
        var support={};
        support.getElementsByClassName = (function () {
            istrue= !!document.getElementsByClassName;
            if(istrue&& (typeof document.getElementsByClassName==="function")){
                //不僅判斷是否支持該方法,還要執行該方法檢驗
                var div=document.createElement('div'),
                        testDiv=document.createElement('div');
                testDiv.className='test';
                div.appendChild(testDiv);
                //檢驗
                return div.getElementsByClassName('test')[0]===testDiv;
            }
            else return false;
        })();

 

很棒吧,jquery就是這麼實現的,以下
jquery實現
/**
 * Support testing using an element
 * @param {Function} fn Passed the created div and expects a boolean result
 */
function assert( fn ) {
    var div = document.createElement("div");

    try {
        return !!fn( div );
    } catch (e) {
        return false;
    } finally {
        // Remove from its parent by default
        if ( div.parentNode ) {
            div.parentNode.removeChild( div );
        }
        // release memory in IE
        div = null;
    }
}

    // Support: IE<8
    // Verify that getAttribute really returns attributes and not properties
    // (excepting IE8 booleans)
    support.attributes = assert(function( div ) {
        div.className = "i";
        return !div.getAttribute("className");
    });

    /* getElement(s)By*
    ---------------------------------------------------------------------- */

    // Check if getElementsByTagName("*") returns only elements
    support.getElementsByTagName = assert(function( div ) {
        div.appendChild( doc.createComment("") );
        return !div.getElementsByTagName("*").length;
    });

    // Support: IE<9
    support.getElementsByClassName = rnative.test( doc.getElementsByClassName );

 

那麼我們的類選擇器可以重新寫了
類選擇器重寫
 function getClass(clas,arr,node){
        arr=arr||[];;
        [].push.apply(arr,getclass(clas,node));
        return arr;
    }
        //getClas相容方法
        function getclass(classname,node){
            node=node || document;
            if(support.getElementsByClassName){
                return node.getElementsByClassName(classname);
            }
            else {
//      2.自己實現的代碼
                var arr=document.getElementsByTagName('*');
                var results=[];
                each(arr, function (k,v) {
                    if(this.className===classname){
                        results.push(this);
                    }
                });
                return results;
            }
        }

 

 實現效果:


ok,這次就講到這兒吧,啰啰嗦嗦,希望各位不要嫌棄我的這點拙見,還希望多多指點,共同進步,未完待續。
2016-04-10

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

-Advertisement-
Play Games
更多相關文章
  • PC的早期階段,也是傳統的C/S模式居多,後進化到B/S模式,並產生了SaaS、雲計算等概念和應用。從客戶端進化到瀏覽器最大好處是客戶端無需更新,減少了大量的更新成本,只需伺服器端進行更新。這也是為什麼現在流行webQQ, google docs, photoshop網頁版的原因。現在同時很多軟體廠 ...
  • 瀏覽器的事件模型 DOM第0級事件模型 他的屬性提供了關於當前正被處理的已觸發事件的大量信息。這包括一些細節,比如在哪個元素上觸發的事件、滑鼠事件的坐標以及鍵盤事件中單擊了哪個鍵。 當觸發 dom 樹中一個元素上的事件時,事件模型會檢查這個元素是否已經創建了特定的事件處理器。如果是,就會調用已創建的 ...
  • 序言 在今天,JavaScript已經成為了網頁編輯的核心。尤其是過去的幾年,互聯網見證了在SPA開發、圖形處理、交互等方面大量JS庫的出現。 如果初次打交道,很多人會覺得js很簡單。確實,對於很多有經驗的工程師,或者甚至是初學者而言,實現基本的js功能幾乎毫無障礙。但是JS的真實功能卻比很多人想象 ...
  • 選擇器的優先順序關係到元素應用哪個樣式。在CSS2.1的規範(http://www.w3.org/TR/2009/CR-CSS2-20090908/cascade.html#specificity)中是這樣描述的: 如果聲明來自於“style”屬性,而不是帶有選擇器的規則,則記為 1,否則記為 0 ( ...
  • 摘要: 最近閑來無事就把以前做的cordova項目整理了下,發現網上很少有詳細完整的配置教程,所以自己就總結了下分享給大家。 項目地址:https://github.com/baixuexiyang/hybrid 環境搭建: 1.安裝node.js下載安裝node.js,https://nodejs ...
  • 雖然jquery的較新的api已經很好用了, 但是在實際工作還是有做二次封裝的必要,好處有:1,二次封裝後的API更加簡潔,更符合個人的使用習慣;2,可以對ajax操作做一些統一處理,比如追加隨機數或其它參數。同時在工作中,我們還會發現,有一些ajax請求的數據,對實時性要求不高,即使我們把第一次請... ...
  • 一、HTML 元素 HTML 元素以開始標簽起始 HTML 元素以結束標簽終止 元素的內容是開始標簽與結束標簽之間的內容 某些 HTML 元素具有空內容(empty content) 空元素在開始標簽中進行關閉(以開始標簽的結束而結束) 大多數 HTML 元素可擁有屬性 嵌套的 HTML 元素 HT ...
  • 我這裡說的提示框,就是當用戶將滑鼠移動到需要提示的圖標時,就會在這圖標的位置出現一個提示框了。 咦,那這有什麼好說的呢? 如果你來實現這一效果,你會怎麼做呢? 初步的做法嘛,就是利用PS製作一張提示框內容區域的png圖片和一張指向位置的箭頭png圖片,然後利用這張圖片作為提示背景,裡面輸入指定內容唄 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...