上層建築——DOM元素的特性與屬性(dojo/dom-prop)

  上一篇講解dojo/dom-attr的文章中咱們知道在某些狀況下,attr模塊中會交給prop模塊來處理。好比:html

  • textContent、innerHTML、className、htmlFor、value
  • disabled、checked等無狀態特性對應於屬性中的布爾變量
  • 事件的處理

  那這一節,咱們便來看看prop對於屬性的處理。node

 

  首先是一個標準名稱字典,將要設置的屬性名從新命名,避免與保留字的衝突:算法

exports.names = {
        // properties renamed to avoid clashes with reserved words
        "class": "className",
        "for": "htmlFor",
        // properties written as camelCase
        tabindex: "tabIndex",
        readonly: "readOnly",
        colspan: "colSpan",
        frameborder: "frameBorder",
        rowspan: "rowSpan",
        textcontent: "textContent",
        valuetype: "valueType"
    };

  相比dom-attr來講,dom-prop模塊只有兩個公共函數:prop.get與prop.set瀏覽器

  prop.get方法的函數簽名爲:app

// Dojo 1.7+ (AMD)
require(["dojo/dom-prop"], function(domProp){
  result = domProp.get("myNode", "someAttr");
});

  除了textContent屬性外,其餘直接以方括號語法從node中取值:node[prop];對於textContent屬性,若是元素不支持textContent,便以深度優先算法去獲取元素下全部文本節點的nodevalue:dom

function getText(/*DOMNode*/node){
        var text = "", ch = node.childNodes;
        for(var i = 0, n; n = ch[i]; i++){
            //Skip comments.
            if(n.nodeType != 8){
                if(n.nodeType == 1){
                    text += getText(n);
                }else{
                    text += n.nodeValue;
                }
            }
        }
        return text;
    }

  由於innerText並非標準屬性,因此這裏棄之不用;如下即是get方法的源碼:函數

exports.get = function getProp(/*DOMNode|String*/ node, /*String*/ name){
        node = dom.byId(node);
        //轉化成標準屬性
        var lc = name.toLowerCase(), propName = exports.names[lc] || name;
    //處理textContent這種特殊屬性
        if(propName == "textContent" && !has("dom-textContent")){
            return getText(node);
        }
        
        return node[propName];    // Anything
    };

 

  prop.set方法的函數簽名爲:ui

require(["dojo/dom-prop"], function(domProp){
  result = domProp.set("myNode", "someAttr", "value");
});

  在attr.set方法中,不少狀況都交給prop來處理,下面咱們就要看看prop中set方法的實現。spa

  set方法用來爲元素的屬性賦值,在實際應用中須要處理如下幾種狀況:code

  • 參數分解,若是一次設置多個屬性,爲每一個屬性分別設置
  • 若是someAttr是「style」,則交給dom-style模塊處理
  • 若是someAttr是innerHTML,由於有的元素(tbody、thead、tfoot、tr、td、th、caption、colgroup、col)不支持innerHTML屬性,因此須要曲線救國,這裏首先將元素的子節點清除掉,而後利用dom-construct的toDom方法,將html片斷轉化成dom節點,做爲子節點插入元素中
  • 若是someAttr是textContent,一樣由於有的瀏覽器並不支持該屬性,曲線救國的方式是先清除元素的子節點,而後建立文本節點做爲子節點插入元素中
  • 若是value是function,則看作添加事件;對於event的處理最好不要使用prop模塊仍是推薦使用on模塊來綁定事件
    exports.set = function setProp(/*DOMNode|String*/ node, /*String|Object*/ name, /*String?*/ value){
        node = dom.byId(node);
        var l = arguments.length;
        //分解參數
        if(l == 2 && typeof name != "string"){ // inline'd type check
            for(var x in name){
                exports.set(node, x, name[x]);
            }
            return node; // DomNode
        }
        //若是要設置style,調用dom-style來處理
        var lc = name.toLowerCase(), propName = exports.names[lc] || name;
        if(propName == "style" && typeof value != "string"){ // inline'd type check
            // special case: setting a style
            style.set(node, value);
            return node; // DomNode
        }
        //若是是innerHTML,對於不支持innerHTML的節點,採用曲線救國的方式,不然直接設置innerHTML
        if(propName == "innerHTML"){
            // special case: assigning HTML
            // the hash lists elements with read-only innerHTML on IE
            if(has("ie") && node.tagName.toLowerCase() in {col: 1, colgroup: 1,
                        table: 1, tbody: 1, tfoot: 1, thead: 1, tr: 1, title: 1}){
                ctr.empty(node);
                node.appendChild(ctr.toDom(value, node.ownerDocument));
            }else{
                node[propName] = value;
            }
            return node; // DomNode
        }
        //若是不支持textContent,清除元素子節點後,添加文本節點
        if(propName == "textContent" && !has("dom-textContent")) {
            ctr.empty(node);
            node.appendChild(node.ownerDocument.createTextNode(value));
            return node;
        }
        //這一部分是經過prop來綁定事件,但並不建議用這種方式
        if(lang.isFunction(value)){
            // special case: assigning an event handler
            // clobber if we can
            var attrId = node[_attrId];
            if(!attrId){
                attrId = _ctr++;
                node[_attrId] = attrId;
            }
            if(!_evtHdlrMap[attrId]){
                _evtHdlrMap[attrId] = {};
            }
            var h = _evtHdlrMap[attrId][propName];
            if(h){
                //h.remove(); 若是曾經以相似的方式綁定過事件,則移除事件
                conn.disconnect(h);
            }else{
                try{
                    delete node[propName];
                }catch(e){}
            }
            // ensure that event objects are normalized, etc.
            if(value){//prop.get函數返回node,因此把handle放到_evtHdlrMap中
                //_evtHdlrMap[attrId][propName] = on(node, propName, value);
                _evtHdlrMap[attrId][propName] = conn.connect(node, propName, value);
            }else{
                node[propName] = null;
            }
            return node; // DomNode
        }
        node[propName] = value; //直接爲屬性賦值
        return node;    // DomNode
    };

  若是您以爲這篇文章對您有幫助,請不吝點擊右下方「推薦」,謝謝~

相關文章
相關標籤/搜索