DOM.style

by Yan Sheng

继续上回的 KISSY DOM 源码学习..今天看了 style, attr, 和 data相关.

dom-style.js

http://github.com/kissyteam/kissy/blob/master/src/dom/dom-style.js http://github.com/kissyteam/kissy/blob/master/src/dom/dom-style-ie.js

DOM.css() 先看setter

// setter
else {
    // normalize unsetting
    if (val === null || val === EMPTY) {
        val = EMPTY;
    }
    // number values may need a unit
    else if (!isNaN(new Number(val)) && RE_NEED_UNIT.test(name)) {
        val += DEFAULT_UNIT;
    }

    // ignore negative width and height values
    if ((name === WIDTH || name === HEIGHT) && parseFloat(val) < 0) {
        return;
    }

    S.each(S.query(selector), function(elem) {
        if (elem && elem[STYLE]) {
            name.set ? name.set(elem, val) : (elem[STYLE][name] = val);
            if (val === EMPTY) {
                if (!elem[STYLE].cssText)
                    elem.removeAttribute(STYLE);
            }
        }
    });
}

核心代码 name.set ? name.set(elem, val) : (elem[STYLE][name] = val); , 也就是直接内敛设置元素的style, 前面的name.set判断 是针对 IE下, 一些特殊属性, 如 opacity, 需要特殊的处理, 所以在 style-ie.js 中增加 opacity 的 get/set 函数;

PS: IE下的 opacity 通过 elem.filters.DXImageTransform.Microsoft.Alpha.opacity 或者 elem.filters.alpha.opacity 获取, 而设置时, 利用 elem.currentStyle.filter 中有关 opacity 的值进行设置. PS: 如果以后还有其他需要特殊处理的 css 属性, 就可以直接通过 类似于opacity 的方式 添加 属性的set和get, 而不用再次修改 style.js 中的代码;

再看getter

// getter
if (val === undefined) {
    // supports css selector/Node/NodeList
    var elem = S.get(selector), ret = '';

    if (elem && elem[STYLE]) {
        ret = name.get ? name.get(elem) : elem[STYLE][name];

        // 有 get 的直接用自定义函数的返回值
        if (ret === '' && !name.get) {
            ret = fixComputedStyle(elem, name, DOM._getComputedStyle(elem, name));
        }
    }

    return ret === undefined ? '' : ret;
}
  • ret = name.get ? name.get(elem) : elem[STYLE][name]; name.get 同样是针对需要特殊处理的 属性,
  • 先尝试 取元素内敛的 style, 如果没有, 则使用 getComputedStyle 里计算当前的 CSS 属性值;
  • fixComputedStyle, 对 getComputedStyle 返回的值再次处理, 主要针对 css 属性 left/top 的返回值为 auto 时处理, elem 的 position 为 absolute 时, kissy 取 left/top 值为 offsetLeft/offsetTop 减去 margin-left/margin-top 的值; elem 的 position 为 relative 时, 直接取 0;

非 IE 下 获取元素 css 值, 使用的是 document.defaultView.getComputedStyle, IE下则用 elem.currentStyle, 所以 KISSY 针对 IE , 覆盖了 DOM._getComputedStyle , 使用 currentStyle.

还有一些其他的注意点:

  • 不同浏览器对 css 属性的命名认识也不一样, webkit 认识 camel-case(有-)的, 其他的只认识camelCase;
  • CSS 中 float 和 js 的 float 冲突, 所以浏览器将 CSS 的 float 替代命名, IE 用 styleFloat, 标准浏览器用 cssFloat;
  • color获取, 情况也很多, 详见 secrets of the javascrpt p172.

DOM.width()/height()核心函数

function getWH(selector, name) {
    var elem = S.get(selector),
        which = name === WIDTH ? ['Left', 'Right'] : ['Top', 'Bottom'],
        val = name === WIDTH ? elem.offsetWidth : elem.offsetHeight;

    S.each(which, function(direction) {
        val -= parseFloat(DOM._getComputedStyle(elem, 'padding' + direction)) || 0;
        val -= parseFloat(DOM._getComputedStyle(elem, 'border' + direction + 'Width')) || 0;
    });

    return val;
}
  • 取的是 元素内容宽度, 而 clientWidth()/clientHeight() 是包含 padding 的.
  • 取的是offsetWidth/offsetHeight, 减去 padding和border 得到;

DOM.show()/hide()/toggle() 元素显示/隐藏/切换.

  • 这里有一个注意的地方, 就是, 元素的 display 值, 再显示的时候, 不是固定设置 block, 而是设置 元素被隐藏之前的值, 当然在隐藏这个元素前会把原来的 display 值保存起来, 这里用到了 DOM.data.

dom-data.js

http://github.com/kissyteam/kissy/blob/master/src/dom/dom-data.js

DOM.data()/DOM.removeData()

分为 winDataCache 和节点上的 dataCache ,

  • 如果是设置在 win 上的 data, key 为 expando(dom-data加载时随机生成的字串), cache为 winDataCache, 即 winDataCache[expando][name] = data;
  • 如果是设置在 某个节点上 的 data, key 为 node[expando](不存在时生成一个全局唯一的id, S.guid() 方法), cache为dataCache, 即dataCache[node[expando]][name] = data;

dom-attr.js

http://github.com/kissyteam/kissy/blob/master/src/dom/dom-attr.js

// attr getter

  • ie7-, css与js冲突的属性, 如, for/class 名字分别为 htmlFor/className;
  • mapping 属性, 如 readonly, checked, selected, 直接使用 elem.name 获取;
  • 其他用 getAttribute, 但还有一些属性, 在 ie7- 下, 得通过 getAttribute(name, 2) , 就是指定 第2个参数来获取 http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx;
  • style属性, ie7- 下用 elem.style.cssText;

// attr setter

  • style, 通过elem.style.cssText;
  • checked 需要 elem.checked = xxx, 通过setAttribute(checked, xxx) 不行;
  • 其他用 setAttribute(name, val);

// removeAttr:

  • elem.removeAttribute(name);// 为何先得置空, DOM.attr(elem, name, EMPTY)??

// val getter

  • 如果是 options 元素, 当没有设定 value 时,标准浏览器 option.value === option.text, ie7- 下,没有设定 value 时,option.value === '', 需要用 el.attributes.value 来判断是否有设定 value;
  • 如果是 select 元素, 如果是单选框, 就去 selectedIndex 那个 option 的val, 没有选中返回 null ; 如果是多选框, 则便利判断 option 是否被 selected, 是则返回, 最终返回以 val 数组, 没有选择返回[];
  • 如果 radiobox, 如果是webkit浏览器, 没有设置 value时 默认返回 'on';
  • 剩余的元素, 统一用 elm.value;

// val setter

if (nodeNameIs(SELECT, el)) {
    // 强制转换数值为字符串,以保证下面的 inArray 正常工作
    if (S.isNumber(value)) {
        value += EMPTY;
    }

    var vals = S.makeArray(value),
        opts = el.options, opt;

    for (i = 0,len = opts.length; i < len; ++i) {
        opt = opts[i];
        opt.selected = S.inArray(DOM.val(opt), vals);
    }

    if (!vals.length) {
        el.selectedIndex = -1;
    }
}
else if (isElementNode(el)) {
    el.value = value;
}
  • select文本框时, 处理比较麻烦, 在设置 val 时, 需要将对应的 options 选中, 即对应的 options 的selected 为 True , selectedIndex 更新, 如果是没有选中(? 单选框的话,,应该不会吧), 设置为-1 .
  • 如果是普通 HTMLELement, 直接设置 elem.value = val 即可;

// text setter / getter

  • 如果是 HTMLElement, 使用 elem.text = val / 或 getter 是为 '';
  • 如果是 TextNode, 使用 elem.nodeValue = val;

attr 和 expando

  • elem 上的属性 , elem.id 等价于 elem.getAttribute('id');
  • elem 上的 dataname, 给elem.dataname = data, 但不同通过 elem.getAttribute(dataname) 获取;

不过, 上面的data.js中, 并没有把dataname直接加在elem上, 而是另外开辟 dataCache 放置, 或许是为了方便管理这些额外的数据; PS: secret of javascript 说, elem.id 要比elem.getAttribute('id') 快很多, 尤其是在IE下. 不知道 直接放在 dataCache 里, 性能如何, 字典应该也挺快的吧..

参考资源

JavascriptKISSY