虚度人生

导航

笑问客从何处来--浏览器检测vs.特性检测

之所以说浏览器类型检测比较尴尬,是因为有了一个打对台的东东,但是又不可能完全被替代,就是粒度更小的浏览器特性检测。

所以现在就成了两者共存的情况,虽然根据特性来判断更为准确,但是浏览器类型往往又是开发者判断的首选,而且较为简单明了。

还是和以前一样,站在巨人的肩膀上,利用各个js的框架源码,逐一分析比较一下。

注:采用的框架版本:prototype-1.6.1, mootools-1.2.4, jquery-1.4.2, ext-3.2.0, yui-3.1.0, dojo-1.4.2

prototype:

Browser         : (function() {
    var ua = navigator.userAgent;
    var isOpera = Object.prototype.toString.call(window.opera) == '[object Opera]';
    return {
        IE           : !!window.attachEvent && !isOpera,
        Opera        : isOpera,
        WebKit       : ua.indexOf('AppleWebKit/') > -1,
        Gecko        : ua.indexOf('Gecko') > -1 && ua.indexOf('KHTML') === -1,
        MobileSafari : /Apple.*Mobile.*Safari/.test(ua)
    }
})(),
BrowserFeatures : {
    XPath                     : !!document.evaluate,
    SelectorsAPI              : !!document.querySelector,
    ElementExtensions         : (function() {
        var constructor = window.Element || window.HTMLElement;
        return !!(constructor && constructor.prototype);
    })(),
    SpecificElementExtensions : (function() {
        if (typeof window.HTMLDivElement !== 'undefined')
            return true;
        var div = document.createElement('div');
        var form = document.createElement('form');
        var isSupported = false;
        if (div['__proto__'] && (div['__proto__'] !== form['__proto__'])) {
            isSupported = true;
        }
        div = form = null;
        return isSupported;
    })()
}
  • 浏览器检测
    1. var isOpera = Object.prototype.toString.call(window.opera) == '[object Opera]',较常见的是var isOpera == !!window.opera,当然这个不够准确,因为我们可以在window下定义一个opera变量,所以采用前一种写法更加准确。
    2. window.attachEvent这个绑定事件的方法只有在ie及opera中支持,其余的都是采用addEventListener,因此可以用来判别是否是ie,不过稍稍有点奇怪的是,这里为什么就不怕用户自己加个attachEvent变量,如果采用约定俗成的话,那其实!!window.opera也足够了。
    3. webkit, gecko这些都是采用最常见的useragent字符串检测。
  • 特性检测
    1. XPath,document.evaluate会根据传入的path返回XPathResult对象,XPath的语法详见这里,除了ie外的主流浏览器基本都支持。
    2. SelectorsAPI,众多js selector,sizzlepeppy之类的终结者,新一代的浏览器,ff3.5,saf4,chrome,op10,嗯,还有ie8,都已经支持。
    3. ElementExtensions,大家对Object.prototype应该很熟悉了,这就是在DOM中是否允许扩展prototype,毫不意外,出局的又是ie。

mootools:

var Browser = $merge({
    Engine   : {
        name    : 'unknown',
        version : 0
    },
    Platform : {
        name : (window.orientation != undefined) ? 'ipod'
            : (navigator.platform.match(/mac|win|linux/i) || ['other'])[0].toLowerCase()
    },
    Features : {
        xpath : !!(document.evaluate),
        air   : !!(window.runtime),
        query : !!(document.querySelector)
    },
    Plugins  : {},
    Engines  : {
        presto  : function() {
            return (!window.opera) ? false : ((arguments.callee.caller) ? 960 : ((document.getElementsByClassName)
                ? 950 : 925));
        },
        trident : function() {
            return (!window.ActiveXObject) ? false : ((window.XMLHttpRequest) ? ((document.querySelectorAll) ? 6 : 5)
                : 4);
        },
        webkit  : function() {
            return (navigator.taintEnabled) ? false : ((Browser.Features.xpath)
                ? ((Browser.Features.query) ? 525 : 420) : 419);
        },
        gecko   : function() {
            return (!document.getBoxObjectFor && window.mozInnerScreenX == null) ? false
                : ((document.getElementsByClassName) ? 19 : 18);
        }
    }
}, Browser || {});
Browser.Platform[Browser.Platform.name] = true;
Browser.detect = function() {
    for (var engine in this.Engines) {
        var version = this.Engines[engine]();
        if (version) {
            this.Engine = {
                name    : engine,
                version : version
            };
            this.Engine[engine] = this.Engine[engine + version] = true;
            break;
        }
    }
    return {
        name    : engine,
        version : version
    };
};
Browser.detect();
Browser.Request = function() {
    return $try(function() {
        return new XMLHttpRequest();
    }, function() {
        return new ActiveXObject('MSXML2.XMLHTTP');
    }, function() {
        return new ActiveXObject('Microsoft.XMLHTTP');
    });
};
Browser.Features.xhr = !!(Browser.Request());
Browser.Plugins.Flash = (function() {
    var version = ($try(function() {
        return navigator.plugins['Shockwave Flash'].description;
    }, function() {
        return new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version');
    }) || '0 r0').match(/\d+/g);
    return {
        version : parseInt(version[0] || 0 + '.' + version[1], 10) || 0,
        build   : parseInt(version[2], 10) || 0
    };
})();
  • 浏览器检测
    1. 多了平台的判断,这在某些情况下也还是有必要,因为不同平台的同一浏览器可能表现不一样,特别是手持终端平台。
    2. 这里与常见的浏览器类型版本不同的是判断的浏览器渲染引擎,presto是opera的专用,同样trident是ie家族的,使用webkit的就有很多,著名的如safari,chrome,gecko著名的是ff。但是用特征判断引擎的版本就不是很准确,因为版本在不断更新,如果需要判断引擎版本,还是推荐useragent分析,当然,实际上我们只关心引擎的大版本,如presto2,presto3,具体的版本号其实没有什么意义。
  • 特性检测
    1. 特性里加了是否支持xhr,目前绝大多数浏览器,包括手机平台的,都支持xhr,所以个人觉得可有可无。
    2. 插件判断加入是否安装flash,两个不同函数分别对应非ie和ie,但是要注意的是ie下有navigator.plugins这个对象,只是是个空对象。

jquery:

jquery = {
    // ...
    uaMatch : function(ua) {
        var ret = {
            browser : ""
        };
        ua = ua.toLowerCase();
        if (/webkit/.test(ua)) {
            ret = {
                browser : "webkit",
                version : /webkit[\/ ]([\w.]+)/
            };
        } else if (/opera/.test(ua)) {
            ret = {
                browser : "opera",
                version : /version/.test(ua) ? /version[\/ ]([\w.]+)/ : /opera[\/ ]([\w.]+)/
            };
        } else if (/msie/.test(ua)) {
            ret = {
                browser : "msie",
                version : /msie ([\w.]+)/
            };
        } else if (/mozilla/.test(ua) && !/compatible/.test(ua)) {
            ret = {
                browser : "mozilla",
                version : /rv:([\w.]+)/
            };
        }
        ret.version = (ret.version && ret.version.exec(ua) || [0, "0"])[1];
        return ret;
    },
    browser : {}
};
browserMatch = jQuery.uaMatch(userAgent);
if (browserMatch.browser) {
    jQuery.browser[browserMatch.browser] = true;
    jQuery.browser.version = browserMatch.version;
}
// Deprecated, use jQuery.browser.webkit instead
if (jQuery.browser.webkit) {
    jQuery.browser.safari = true;
}
// ...
(function() {
    jQuery.support = {};
    var root = document.documentElement, script = document.createElement("script"), div = document.createElement("div"), id =
        "script" + now();
    div.style.display = "none";
    div.innerHTML =
        ' 
a'; var all = div.getElementsByTagName("*"), a = div.getElementsByTagName("a")[0]; if (!all || !all.length || !a) { return; } jQuery.support = { leadingWhitespace : div.firstChild.nodeType === 3, tbody : !div.getElementsByTagName("tbody").length, htmlSerialize : !!div.getElementsByTagName("link").length, style : /red/.test(a.getAttribute("style")), hrefNormalized : a.getAttribute("href") === "/a", opacity : /^0.55$/.test(a.style.opacity), cssFloat : !!a.style.cssFloat, checkOn : div.getElementsByTagName("input")[0].value === "on", optSelected : document.createElement("select").appendChild(document.createElement("option")).selected, parentNode : div.removeChild(div.appendChild(document.createElement("div"))).parentNode === null, deleteExpando : true, checkClone : false, scriptEval : false, noCloneEvent : true, boxModel : null }; script.type = "text/javascript"; try { script.appendChild(document.createTextNode("window." + id + "=1;")); } catch (e) { } root.insertBefore(script, root.firstChild); if (window[id]) { jQuery.support.scriptEval = true; delete window[id]; } try { delete script.test; } catch (e) { jQuery.support.deleteExpando = false; } root.removeChild(script); if (div.attachEvent && div.fireEvent) { div.attachEvent("onclick", function click() { jQuery.support.noCloneEvent = false; div.detachEvent("onclick", click); }); div.cloneNode(true).fireEvent("onclick"); } div = document.createElement("div"); div.innerHTML = ''; var fragment = document.createDocumentFragment(); fragment.appendChild(div.firstChild); jQuery.support.checkClone = fragment.cloneNode(true).cloneNode(true).lastChild.checked; jQuery(function() { var div = document.createElement("div"); div.style.width = div.style.paddingLeft = "1px"; document.body.appendChild(div); jQuery.boxModel = jQuery.support.boxModel = div.offsetWidth === 2; document.body.removeChild(div).style.display = 'none'; div = null; }); var eventSupported = function(eventName) { var el = document.createElement("div"); eventName = "on" + eventName; var isSupported = (eventName in el); if (!isSupported) { el.setAttribute(eventName, "return;"); isSupported = typeof el[eventName] === "function"; } el = null; return isSupported; }; jQuery.support.submitBubbles = eventSupported("submit"); jQuery.support.changeBubbles = eventSupported("change"); // release memory in IE root = script = div = all = a = null; })();
  • 浏览器检测
    1. 很传统的useragent字符串检测。
    2. 原来的safari被webkit代替,估计是因为chrome的强势出现。
  • 特性检测
    1. leadingWhitespace,ie中使用innnerHTML会将头部的空格自动去除,注意:尾部不会。
    2. tbody,如果table中没有tbody,则自动插入。这本来是ie的特性,但现在ie8的出现让情况复杂了,ie8不会自动插入,所以这时候就体现出特性检测的优势了,因为拥有更细的粒度,也就更为准确。
    3. htmlSerialize,源码中的注释有些错误,其实不是包装元素的问题,而是ie下将link等同于头部空格处理了,只需要将link挪后面或者在前面加上点什么就可以。
    4. style,getAttribute("style")返回style的字符串,但ie下是返回一个object,要取字符串的话用style.cssText。ie8又额外跳了出来,大家已经适应了ie的特殊性,这时候ie开始慢慢向标准靠拢,反而有点不习惯。
    5. hrefNormalized,getAttribute("href"),ie会在前面加入访问的url。同时,没错,你猜对了,ie8又是例外,我们应该渐渐感受到微软的诚意,尽管这一天来得太晚。
    6. opacity,这个应该比较熟悉,ie下可用滤镜实现,其余的也可以用特定样式,如-webkit-opacity,-moz-opacity。
    7. cssFloat,有一些css属性在css中与js中的名称不完全一样,这个就是一例,css中的float,ie中用styleFloat对应,其余的用cssFloat对应,具体这一类的情况我们在以后的获取样式中还会讲到。
    8. checkOn,除webkit引擎之外的浏览器,checkbox的默认值为"on"。
    9. optSelected,除webkit及ie外的浏览器,option的selected默认值为true,目前测试mac下的chrome例外,估计新版的532.9的webkit修复了这个bug已经。
    10. parentNode,除ie外的浏览器,removeNode的parentNode为空。
    11. deleteExpando,除ie外的浏览器,delete一个未定义的属性返回true。
    12. checkClone,除webkit外的浏览器,调用fragment的cloneNode时,checkbox的状态并未克隆,目前测试mac下的chrome例外,估计新版的532.9的webkit修复了这个bug已经。
    13. scriptEval,除ie外的浏览器可以像操纵普通dom元素一样对script元素使用appendChild,ie用script.text代替。
    14. noCloneEvent,ie调用cloneNode会将事件响应函数也复制过去,不是很合理。
    15. boxModel,盒模型就不解释了。
    16. submitBubbles,changeBubbles,ff用添加onevent属性,检查是否函数,其余的用简单的in来检测元素是否支持该事件响应,这一妙招来自于Detecting event support without browser sniffing

ext:

var ua = navigator.userAgent.toLowerCase(),
    check = function(r){
        return r.test(ua);
    },
    DOC = document,
    isStrict = DOC.compatMode == "CSS1Compat",
    isOpera = check(/opera/),
    isChrome = check(/\bchrome\b/),
    isWebKit = check(/webkit/),
    isSafari = !isChrome && check(/safari/),
    isSafari2 = isSafari && check(/applewebkit\/4/), // unique to Safari 2
    isSafari3 = isSafari && check(/version\/3/),
    isSafari4 = isSafari && check(/version\/4/),
    isIE = !isOpera && check(/msie/),
    isIE7 = isIE && check(/msie 7/),
    isIE8 = isIE && check(/msie 8/),
    isIE6 = isIE && !isIE7 && !isIE8,
    isGecko = !isWebKit && check(/gecko/),
    isGecko2 = isGecko && check(/rv:1\.8/),
    isGecko3 = isGecko && check(/rv:1\.9/),
    isBorderBox = isIE && !isStrict,
    isWindows = check(/windows|win32/),
    isMac = check(/macintosh|mac os x/),
    isAir = check(/adobeair/),
    isLinux = check(/linux/),
    isSecure = /^https/i.test(window.location.protocol);
  • 浏览器检测
    1. 代码只用变量,但比jquery,并无甚不同,yui亦如是。(浪花只开一时,但比千年石,并无甚不同,流云亦如此,庆余年中的句子,个人非常喜欢,借用一下)
  • 特性检测
    1. isStrict,document.compatMode是否为CSS1Compat来判断是否是严格模式,但我在一篇文章中看到说这个判断并不准确,具体记不起来,留待日后找到再补。
    2. isSecure,采用http还是https访问。

yui:

Y.UA = function() {
    var numberify = function(s) {
        var c = 0;
        return parseFloat(s.replace(/\./g, function() {
            return (c++ == 1) ? '' : '.';
        }));
    }, win = Y.config.win, nav = win && win.navigator, o = {
        ie     : 0,
        opera  : 0,
        gecko  : 0,
        webkit : 0,
        mobile : null,
        air    : 0,
        caja   : nav && nav.cajaVersion,
        secure : false,
        os     : null
    }, ua = nav && nav.userAgent, loc = win && win.location, href = loc && loc.href, m;
    o.secure = href && (href.toLowerCase().indexOf("https") === 0);
    if (ua) {
        if ((/windows|win32/i).test(ua)) {
            o.os = 'windows';
        } else if ((/macintosh/i).test(ua)) {
            o.os = 'macintosh';
        } else if ((/rhino/i).test(ua)) {
            o.os = 'rhino';
        }
        if ((/KHTML/).test(ua)) {
            o.webkit = 1;
        }
        m = ua.match(/AppleWebKit\/([^\s]*)/);
        if (m && m[1]) {
            o.webkit = numberify(m[1]);
            if (/ Mobile\//.test(ua)) {
                o.mobile = "Apple";
            } else {
                m = ua.match(/NokiaN[^\/]*|Android \d\.\d|webOS\/\d\.\d/);
                if (m) {
                    o.mobile = m[0];
                }
            }
            m=ua.match(/Chrome\/([^\s]*)/);
            if (m && m[1]) {
                o.chrome = numberify(m[1]); // Chrome
            } else {
                m = ua.match(/AdobeAIR\/([^\s]*)/);
                if (m) {
                    o.air = m[0];
                }
            }
        }
        if (!o.webkit) {
            m = ua.match(/Opera[\s\/]([^\s]*)/);
            if (m && m[1]) {
                o.opera = numberify(m[1]);
                m = ua.match(/Opera Mini[^;]*/);
                if (m) {
                    o.mobile = m[0];
                }
            } else { // not opera or webkit
                m = ua.match(/MSIE\s([^;]*)/);
                if (m && m[1]) {
                    o.ie = numberify(m[1]);
                } else { // not opera, webkit, or ie
                    m = ua.match(/Gecko\/([^\s]*)/);
                    if (m) {
                        o.gecko = 1; // Gecko detected, look for revision
                        m = ua.match(/rv:([^\s\)]*)/);
                        if (m && m[1]) {
                            o.gecko = numberify(m[1]);
                        }
                    }
                }
            }
        }
    }
    return o;
}();
  • 浏览器检测
    1. 也是useragent字符串检测,不过变量的值即表示该浏览器的版本号,如果取不到版本号则默认为1,这样避免了如ie6,ie7,ie8之类多个变量,减少变量使用,更为清晰。
  • 特性检测
    1. secure

dojo:

var d = dojo;
var n = navigator;
var dua = n.userAgent, dav = n.appVersion, tv = parseFloat(dav);
if (dua.indexOf("Opera") >= 0) {
    d.isOpera = tv;
}
if (dua.indexOf("AdobeAIR") >= 0) {
    d.isAIR = 1;
}
d.isKhtml = (dav.indexOf("Konqueror") >= 0) ? tv : 0;
d.isWebKit = parseFloat(dua.split("WebKit/")[1]) || undefined;
d.isChrome = parseFloat(dua.split("Chrome/")[1]) || undefined;
d.isMac = dav.indexOf("Macintosh") >= 0;
var index = Math.max(dav.indexOf("WebKit"), dav.indexOf("Safari"), 0);
if (index && !dojo.isChrome) {
    d.isSafari = parseFloat(dav.split("Version/")[1]);
    if (!d.isSafari || parseFloat(dav.substr(index + 7)) <= 419.3) {
        d.isSafari = 2;
    }
}
if (dua.indexOf("Gecko") >= 0 && !d.isKhtml && !d.isWebKit) {
    d.isMozilla = d.isMoz = tv;
}
if (d.isMoz) {
    d.isFF = parseFloat(dua.split("Firefox/")[1] || dua.split("Minefield/")[1]) || undefined;
}
if (document.all && !d.isOpera) {
    d.isIE = parseFloat(dav.split("MSIE ")[1]) || undefined;
    var mode = document.documentMode;
    if (mode && mode != 5 && Math.floor(d.isIE) != mode) {
        d.isIE = mode;
    }
}
if (dojo.isIE && window.location.protocol === "file:") {
    dojo.config.ieForceActiveXXhr = true;
}
d.isQuirks = document.compatMode == "BackCompat";
d.locale = dojo.config.locale || (d.isIE ? n.userLanguage : n.language).toLowerCase();
  • 浏览器检测
    1. useragent字符串检测。
    2. ie8的X-UA-Compatible引入带来了一定的麻烦,需要综合document.documentMode来考虑。
  • 特性检测
    1. isQuirks。
    2. locale,在1.4.2中新加入了语言。

通过以上的代码,我们可以总结出以下几点:

  1. 浏览器与特性检测并不矛盾,可以同时存在。
  2. 有些框架着重于引擎版本,有些着重于浏览器版本,基本上每个引擎都对应于一个主要浏览器,webkit除外,有safari和chrome,我们就单独处理一下。
  3. 为了判断兼容性,一般都有平台信息,但缺少语言信息。
  4. 有各种特性检测,有对各种特性的支持判断,也有基于某些特殊浏览器bug的兼容判断。
  5. 对新的HTML5的一些特性检测好像都没有,可以考虑加入。

综合网上的一些代码得出的个人版本

var win = window, doc = win.document, nav = win.navigator, root = doc.documentElement, div = doc.createElement('div'), bs, frag, id, ua =
    navigator.userAgent, ots = Object.prototype.toString;
function has(p, o) {
    o = o || win;
    if (p in o) {
        try {
            delete o[p];
        } catch (e) {
        }
        return p in o;
    }
};
function ver(split) {
    var s = ua.split(split)[1];
    return s && (s = s.split('.')) && parseFloat(s.shift() + '.' + s.join('')) || 1;
};
function hasEvent(e) {
    e = "on" + e;
    var has = (e in div);
    if (!has) {
        div.setAttribute(e, "return;");
        has = typeof div[e] === "function";
    }
    return has;
};
// 为了保证特性检测的独立性,所以不依靠浏览器判断
div.innerHTML =
    ' 
'; bs = { trident : -[1,] ? 0 : ScriptEngineMinorVersion(), // 考虑像mootools那样用window.ActiveXObject来判断,但实质上这和判断userAgent一样的,都是可以由用户更改的,所以取了这个最短判断,至少目前可用 webkit : !has('taintEnabled', nav) ? ver('WebKit/') : 0, gecko : win.crypto && ots.call(win.crypto) === '[object Crypto]' ? ver('rv:') : 0, presto : win.opera && ots.call(win.opera) === '[object Opera]' ? ver('Presto/') : 0, gchrome : has('chrome') ? ver('Chrome/') : 0, safari : /a/.__proto__ == '//' ? ver('Version/') : 0, // mac,win下ok ie : document.documentMode, platform : (nav.platform.toLowerCase().match(/mac|win|linux/) || [''])[0], lang : (nav.language || nav.browserLanguage).toLowerCase(), // feature detect strict : doc.compatMode === 'CSS1Compat', https : /^https/i.test(win.location.protocol), // xpath: !!doc.evaluate // query: !!doc.querySelector, // style 没有太大意义,应该使用通用的a.style.cssText querySelector : has('querySelector', doc), // in opera HTMLElement.prototype will lead an error domExtensible : win.HTMLDivElement && ('prototype' in win.HTMLDivElement) ? 2 : win.HTMLElement && ('prototype' in win.HTMLElement) ? 1 : 0, msging : has('postMessage'), storage : win.sessionStorage && ots.call(win.sessionStorage) === '[object Storage]', db : has('openDatabase'), appCache : has('applicationCache'), worker : win.Worker && ots.call(win.Worker) === '[object Worker]', geo : nav.geolocation && ots.call(nav.geolocation) === '[object Geolocation]', dragdrop : hasEvent('drag'), offline : has('onLine', nav), cssTable : 0, rgba : 0, blankTrimmed : div.firstChild.nodeName === 'A', hrefNormalized : div.getElementsByTagName('a')[0].getAttribute('href') === '/a', autoTbody : div.getElementsByTagName('tbody').length, scriptChild : 0, parentRemoved : !div.removeChild(div.firstChild).parentNode, optSeleted : div.getElementsByTagName('select')[0].appendChild(document.createElement('option')).selected, chkOn : div.lastChild.value === 'on', chkCloned : 1, eventCloned : 0 // deleteExpando // boxModel:, // submitBubbles, changeBubbles }; if (div.attachEvent && div.fireEvent) { div.attachEvent("onclick", function click() { bs.eventCloned = 1; div.detachEvent("onclick", click); }); div.cloneNode(true).fireEvent("onclick"); } frag = doc.createDocumentFragment(); div.innerHTML = ''; frag.appendChild(div.lastChild); bs.chkClone = frag.cloneNode(true).lastChild.checked; frag = doc.createElement('script'); frag.type = "text/javascript"; id = '_' + new Date().getTime(); try { frag.appendChild(doc.createTextNode('window.' + id + '=1;')); } catch (e) { } root.insertBefore(frag, root.firstChild); if (win[id]) { bs.scriptEval = 1; delete win[id]; } root.removeChild(frag); try { div.style.display = 'table'; bs.cssTable = div.style.display === 'table'; } catch(e) { } try { div.style.color = 'rgba(0,0,0,1)'; bs.rgba = !div.style.color.indexOf('rgba'); } catch(e) { } div = flag = null;
  • 浏览器检测
    1. 检测trident, webkit, gecko和presto这四大浏览器引擎及版本
    2. 检测chrome, safari等常用浏览器及版本
    3. trident属性和ie属性的区别,主要为了兼容ie8引入的X-UA-Compatible,trident指的是浏览器引擎版本,ie指的是当前浏览器渲染版本,通常以ie为比较标准。
    4. 检测win, mac, linux平台
    5. 加入当前浏览器使用语言
  • 特性检测
    1. strict, 是否使用严格模式。
    2. https, 是否使用https协议。
    3. querySelector, 是否提供querySelector函数。
    4. domExtensible, dom元素是否允许扩展,0不允许,1只允许HTMLElement,2可扩展各个HTMLElement子类。
    5. msging, 是否支持跨文档通信。
    6. storage, 是否支持本地存储。
    7. db, 是否支持本地数据库。
    8. worker, 是否支持worker。
    9. geo, 是否支持geolocation。
    10. dragdrop, 是否支持原生拖放。
    11. offline, 是否支持离线检测。
    12. cssTable, 是否支持css的table布局。
    13. rgba, 是否支持rgba。
    14. blankTrimmed, innerHTML是否会去掉空格。
    15. hrefNormalized, href值是否保持不变。
    16. autoTbody, 是否自动插入tbody。
    17. scriptChild,除ie外的浏览器可以像操纵普通dom元素一样对script元素使用appendChild,ie用script.text代替。
    18. parentRemoved,除ie外的浏览器,removeNode的parentNode为空。
    19. optSelected,除webkit及ie外的浏览器,option的selected默认值为true,目前测试mac下的chrome例外,估计新版的532.9的webkit修复了这个bug已经。
    20. chkOn,除webkit引擎之外的浏览器,checkbox的默认值为"on"。
    21. chkCloned,除webkit外的浏览器,调用fragment的cloneNode时,checkbox的状态并未克隆,目前测试mac下的chrome例外,估计新版的532.9的webkit修复了这个bug已经。
    22. eventCloned,ie调用cloneNode会将事件响应函数也复制过去,不是很合理。

通过对高手的代码学习解析,能从中提高一星半点对js的掌握。

个人评价:

ext:★

yui,dojo:★☆

prototype:★★

mootools:★★☆

jquery:★★★★

posted on 2010-03-24 09:31  nozer0  阅读(1274)  评论(1编辑  收藏  举报