dojo/dom-style樣式操作學習筆記


基礎總結

  一個元素的樣式信息由三個來源根據層疊規則確定。三個來源分別是:

  • 由DOM元素style特性設置的內聯樣式
  • 由style元素中嵌入的樣式規則
  • 由link元素引入的外部樣式表

  元素的樣式

  任何支持style特性的HTML元素在JavaScript中都有一個對應的style屬性。這個style對象是CSSStyleDeclaration的實例,包含着通過HTML的style特性指定的所有樣式信息,但不包含與外部樣式表或嵌入樣式表經層疊而來的樣式。在style特性中指定的任何css屬性都可以通過這個style對象相應的屬性來訪問(對於短划線的css屬性必須轉化成駝峰形式:background-color=》backgroundColor)。除“float”外大部分css屬性都可以通過簡單轉化為屬性形式訪問,因為float來JavaScript中屬於保留字,所以在firefox、chrome、opera、safari等瀏覽器中對應JavaScript屬性為cssFloat,而在Ie中對應為styleFLoat。

  “DOM2級樣式”規范中為CSSStyleDeclaration對象定義了一系列的屬性和方法(IE9+):

  • cssText:讀取模式下返回瀏覽器對style特性中css代碼的內部表示;寫模式下,賦給cssText的值會重寫整個style特性的值。設置cssText是為元素應用多項變化最快捷的方式。
  • length:應用給元素的css屬性的數量
  • parentRule:表示css信息的CSSRule對象。
  • getPropertyCSSValue(propertyName):返回包含給定屬性值的CSSValue對象。(CSSValue對象包含兩個屬性:cssText和cssValueType;cssText與getPropertyValue()返回的值相同)
  • getPropertyPriority(propertyName):如果給定的css屬性使用了!important設置,則返回"!important";否則返回空字符串
  • getPropertyValue(propertyName):返回給定屬性的字符串值
  • item(index):返回給定位置的CSS屬性的名稱
  • removeProperty(propertyName):從樣式中刪除給定屬性
  • setProperty(propertyName, value, priorit):將給定屬性設置為相應值,並加上優先權標識("!important"或者一個空字符串)

  元素的樣式

  "DOM2級樣式"增強了document.defaultView,defaultView指向當前文檔所在的window對象。該對象增加了一個getComputedStyle(node, pseudo)方法,該方法返回一個CSSStyleDeclaration對象,包含當前元素的所有計算樣式。而IE中並不支持該方法,與其對應的是node.style.currentStyle屬性,該屬性也是CSSStyleDeclaration的實例,包含當前元素所有計算后的樣式。無論在哪個瀏覽器中,所有計算后的樣式都是只讀的

  樣式表

  CSSStyleSheet類型代表樣式表,僅僅是樣式表,無論是通過<link>元素包含的樣式表和在<style>元素中定義的樣式表。CSSStyleSheet對象是一套只讀的接口(除了disabled屬性)。CSSStyleSheet接口的屬性如下:

  • disabled:布爾值,表示樣式表是否被禁用。該屬性是可讀寫的,設為true可以禁用樣式表。
  • href:如果是通啊過<link>設置的則返回樣式表的URL否則返回null
  • media:當前樣式表支持的所有媒體類型的集合。
  • ownerNode:指向當前樣式表的節點的指針;如果當前樣式表是其他樣式表通過@import導入的,該屬性為null。IE不支持該屬性。
  • parentStyleSheet:如果當前樣式表是通過@import導入的情況下,這個屬性是一個指向導入它的樣式表的指針。
  • title:ownerNode的title。
  • type:表示樣式表類型的字符串。對css樣式而言,這個屬性是"type/css"
  • cssRules:表示樣式表中包含的樣式規則的集合。IE不支持這個屬性,但支持一個rules屬性。
  • ownerRule:如果樣式表是通過@import導入的,這個屬性表示導入的規則。IE不支持該屬性。
  • deleteRule(index):刪除cssRules中指定位置的規則。IE不支持該屬性,但支持類似的removeRule(index).
  • insertRule(rule, index):像cssRules集合中指定的位置插入rule字符串。IE不支持,但有一個類似的addRule()方法。

  也可以直接通過<link>或<style>元素取得CSSStyleSheet對象。element.sheet指向代表該元素樣式表的CSSStyleSheet對象。IE中不支持該屬性,但支持一個styleSheet屬性。

  CSS規則

  CSSRule對象表示樣式表中的一條規則。CSSStyleRule繼承自CSSRule類,並提供了以下屬性:

  • cssText:返回整條規則對應的文本。IE不支持這個屬性
  • parentRule:如果當前規則是導入的規則,這個屬性引用的就是導入規則;否則為null;IE不支持這個屬性
  • parentStyleSheet:當前規則所屬的樣式表。IE不支持該屬性。
  • selectorText:返回當前規則的選擇符文本。這個屬性是只讀的。
  • style:一個CSSStyleDeclaration對象,可以通過它設置和取得規則中特定的樣式值。
  • type:表示規則類型的常量。IE不支持。

  其中cssText、selectorText、style三個屬性最常用,cssText與style.cssText屬性類似,但cssText包含選擇符文本和圍繞樣式信息的花括號,而style.cssText只有樣式信息,style.cssText是可讀寫的而cssText是只讀的。

  這幾種類型的關系如下圖所示:

  dojo/dom-style

   該模塊提供兩個方法:get和set。

  對於取值,最重要的是獲取樣式層疊之后的計算值,所以要使用的原生api就是getComputedStyle。但是該函數有兼容性問題,上文提到過IE中沒有該方法,只能通過element.currentStyle來達到相同的效果;另外,webkit內核瀏覽器中如果node的display為none,這時調用getComputedStyle是無法取到正確結果的,這里dojo自己處理了一下將其diaplay設為空字符串。一下便是dojo中對getComputedStyle的處理:

var getComputedStyle, style = {
        // summary:
        //        This module defines the core dojo DOM style API.
    };
    // nodeType為1,代表元素節點
    if(has("webkit")){
        getComputedStyle = function(/*DomNode*/ node){
            var s;
            if(node.nodeType == 1){
                var dv = node.ownerDocument.defaultView;
                s = dv.getComputedStyle(node, null);
                // node不可見時,無法得到有效結果
                if(!s && node.style){
                    node.style.display = "";
                    s = dv.getComputedStyle(node, null);
                }
            }
            return s || {};
        };
    }else if(has("ie") && (has("ie") < 9 || has("quirks"))){
        getComputedStyle = function(node){
            return node.nodeType == 1 && node.currentStyle ? node.currentStyle : {};
        };
    }else{
        getComputedStyle = function(node){
            return node.nodeType == 1 ?
                node.ownerDocument.defaultView.getComputedStyle(node, null) : {};
        };
    }
    style.getComputedStyle = getComputedStyle;

  到這里我們僅僅能得到計算后的樣式值,但getComputedStyle方法的樣式值是帶有單位的,而實際應用中我們往往需要的是數字值。所以接下來的步驟就是將計算值轉化成數字:

var toPixel;
    if(!has("ie")){
        toPixel = function(element, value){
            //非ie瀏覽器直接轉化成數字
            return parseFloat(value) || 0;
        };
    }else{
        // ie瀏覽器下則要進行各種兼容處理
        toPixel = function(element, avalue){
            if(!avalue){ return 0; }
            // 對於border-width 可能設置為medium,這時我們認為寬度為4
            if(avalue == "medium"){ return 4; }
            // 帶px單位的,去掉px即可;slice函數參數為負數的相當於length+負數
            if(avalue.slice && avalue.slice(-2) == 'px'){ return parseFloat(avalue); }
            // 下面這段沒看懂。。。。。
            var s = element.style, rs = element.runtimeStyle, cs = element.currentStyle,
                sLeft = s.left, rsLeft = rs.left;
            rs.left = cs.left;
            try{
                // 'avalue' may be incompatible with style.left, which can cause IE to throw
                // this has been observed for border widths using "thin", "medium", "thick" constants
                // those particular constants could be trapped by a lookup
                // but perhaps there are more
                s.left = avalue;
                avalue = s.pixelLeft;
            }catch(e){
                avalue = 0;
            }
            s.left = sLeft;
            rs.left = rsLeft;
            return avalue;
        };
    }
    style.toPixelValue = toPixel;

  處理完這些之后,讓我們先看一下dojo/dom-style.get方法:

style.get = function getStyle(/*DOMNode|String*/ node, /*String?*/ name){
        var n = dom.byId(node), l = arguments.length, op = (name == "opacity");
        // 對於opacity做兼容處理,非ie直接使用css中的opacity屬性; ie8以下瀏覽器並不支持,這時就需要使用濾鏡,所以取值時候也要特殊處理
        if(l == 2 && op){ 
            return _getOpacity(n);
        }
        // 對於float保留字的處理,非ie用的是CSSFloat而ie使用的是styleFloat
        name = _floatAliases[name] ? "cssFloat" in n.style ? "cssFloat" : "styleFloat" : name;
        var s = style.getComputedStyle(n);
        // 如果指定的屬性名稱,調用_toStyleValue方法。
        return (l == 1) ? s : _toStyleValue(n, name, s[name] || n.style[name]); /* CSS2Properties||String||Number */
    };

  dojo中對於opacity做了兼容性處理,這里我們只要知道原理即可,想詳細了解的同學可以自己查看dojo源碼:

var astr = "DXImageTransform.Microsoft.Alpha";
    var af = function(n, f){
        try{
            return n.filters.item(astr);
        }catch(e){
            return f ? {} : null;
        }
    };

    var _getOpacity =
        has("ie") < 9 || (has("ie") < 10 && has("quirks")) ? function(node){
            try{
                return af(node).Opacity / 100; // Number
            }catch(e){
                return 1; // Number
            }
        } :
        function(node){
            return getComputedStyle(node).opacity;
        };

    var _setOpacity =
        has("ie") < 9 || (has("ie") < 10 && has("quirks")) ? function(/*DomNode*/ node, /*Number*/ opacity){
            if(opacity === ""){ opacity = 1; }
            var ov = opacity * 100, fullyOpaque = opacity === 1;

            // on IE7 Alpha(Filter opacity=100) makes text look fuzzy so disable it altogether (bug #2661),
            // but still update the opacity value so we can get a correct reading if it is read later:
            // af(node, 1).Enabled = !fullyOpaque;

            if(fullyOpaque){
                node.style.zoom = "";
                if(af(node)){
                    node.style.filter = node.style.filter.replace(
                        new RegExp("\\s*progid:" + astr + "\\([^\\)]+?\\)", "i"), "");
                }
            }else{
                node.style.zoom = 1;
                if(af(node)){
                    af(node, 1).Opacity = ov;
                }else{
                    node.style.filter += " progid:" + astr + "(Opacity=" + ov + ")";
                }
                af(node, 1).Enabled = true;
            }

            if(node.tagName.toLowerCase() == "tr"){
                for(var td = node.firstChild; td; td = td.nextSibling){
                    if(td.tagName.toLowerCase() == "td"){
                        _setOpacity(td, opacity);
                    }
                }
            }
            return opacity;
        } :
        function(node, opacity){
            return node.style.opacity = opacity;
        };
View Code

  通過上面代碼我們可以看到,如果沒有獲取特定屬性直接返回getComputedStyle方法得到的結果,如果指明特定屬性則調用_toStyleValue方法, 該方法決定將哪些值轉化成數字:

var _pixelNamesCache = {
        left: true, top: true
    };
    // 如果獲取的屬性符合這個正則表達式,將他們放到_pixelNamesCache中,將得到的結果轉化成數字
    var _pixelRegExp = /margin|padding|width|height|max|min|offset/; // |border
    function _toStyleValue(node, type, value){
        //TODO: should we really be doing string case conversion here? Should we cache it? Need to profile!
        type = type.toLowerCase();
        if(has("ie") || has("trident")){
            //ie瀏覽器中如果width或height得到的是auto,使用offsetWidth或者offsetHeight
            if(value == "auto"){
                if(type == "height"){ return node.offsetHeight; }
                if(type == "width"){ return node.offsetWidth; }
            }
            // 對fontWeight的處理
            if(type == "fontweight"){
                switch(value){
                    case 700: return "bold";
                    case 400:
                    default: return "normal";
                }
            }
        }
        //如果符合正則,則放入_pixelNamesCache中,將得到的結果轉化成數字
        if(!(type in _pixelNamesCache)){
            _pixelNamesCache[type] = _pixelRegExp.test(type);
        }
        return _pixelNamesCache[type] ? toPixel(node, value) : value;
    }

  

  有了以上基礎,設置就簡單許多:

style.set = function setStyle(/*DOMNode|String*/ node, /*String|Object*/ name, /*String?*/ value){
        var n = dom.byId(node), l = arguments.length, op = (name == "opacity");
        // 對float屬性的處理
        name = _floatAliases[name] ? "cssFloat" in n.style ? "cssFloat" : "styleFloat" : name;
        // 處理opacity屬性
        if(l == 3){
            return op ? _setOpacity(n, value) : n.style[name] = value; // Number
        }
        // 如果一次設置多個屬性,將他們分別處理;
        for(var x in name){
            style.set(node, x, name[x]);
        }
        // 返回樣式的計算值
        return style.getComputedStyle(n);
    };

   對於多個屬性處理的這部分,最好的方式是使用cssText一次設置多個值:style.cssText = style.cssText+"拼接后的樣式字符串"。這樣瀏覽器只需要重新繪制一次即可應用多種樣式。而不是每次設置style都要重繪一次(有的瀏覽器可以做優化處理)。當然使用cssText方式,一來在ie8中並不支持,而來如果要重新覆蓋一個樣式屬性我們還需要做一些處理。

  以上部分就是今天的博客內容,如果您覺得這篇文章對您有幫助,請不吝點擊下方推薦,您的鼓勵是我分享的動力!!!

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM