笑问客从何处来--浏览器检测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:★★★★

你可能感兴趣的:(浏览器)