自己偵測瀏覽器對於API的支援
Mon, 04 Oct 2010 10:31:57 +0800最近開始想要用自己寫程式的方式,透過規格文件來學習HTML5。不過看過規格,發現許多API還是繼承自DOM規格,所以還是需要花時間回頭看一下DOM3的相關文件。
記得在John Resig及PPK的Blog或是書裡面,有看過一些對於瀏覽器相容性的建議,主要是關於不要用瀏覽器來決定是否支援某項功能,而應該直接偵測某項功能在某個瀏覽器是否可以使用。基於這樣的想法,之前也寫了個小程式來偵測瀏覽器對於html5 tag的支援。其實想法很簡單,只是從標準文件上找到某個物件具備的properties,然後測試這些東西實際在瀏覽器中是否存在。
從ECMA-262 Edition3裡面物件的properties就有[DontEnum]屬性,如果有這個屬性,使用for...in就無法列舉出來。不過只是不會列舉,並不是無法使用。之前偵測IE的window物件就有碰到這樣的問題,IE8把許多東西都設為[DontEnum],所以用for..in來偵測的話,很多功能就跑不出來。這樣,就得想辦法自己列舉該有的properties,檢查他是否在物件中為undefined。
HTML5中,所有的元素都是HTMLElement,這個Interface繼承了DOM的Element,而Element又繼承了DOM的Node Interface。同樣,在其他規格中也與DOM有關,例如Drag & Drop中,事件會繼承DOM3 Event的核心Event Interface,有這些線索,就可以根據規格一路追過來。因為要處理許多Interface,在寫程式的過程中就歸納了一些自己會用到的函數:
function row(cells) {
return '<tr>' + cells.join('') + '</tr>\n';
}
function cell(text, style, col) {
return '<td' + (col? ' colspan="'+col+'"':'') + (style? ' style="'+style+'"':'') + '>' + text + '</td>';
}
function DOMInspector(title, obj, attr) {
var ret = {
'title':title,
'SupportedAttributes':[],
'UnsupportAttributes':[],
'SupportedMethods':[],
'UnsupportMethods':[]
};
for(var i=0; i<attr.attr.length; i++) {
if(obj[attr.attr[i]] !== undefined) {
ret.SupportedAttributes.push(attr.attr[i]);
} else {
ret.UnsupportAttributes.push(attr.attr[i]);
}
}
for(var i=0; i<attr.mthd.length; i++) {
if(obj[attr.mthd[i]] !== undefined) {
ret.SupportedMethods.push(attr.mthd[i]);
} else {
ret.UnsupportMethods.push(attr.mthd[i]);
}
}
return ret;
}
function resultParser(test) {
var str = '<table cellpadding="2" cellspacing="0" border="1">';
for(var i in test) {
if(i == 'title') {
str += row([cell(test[i],null,2)]);
} else {
str += row([cell(i),cell(test[i].length>0? test[i].join('<br>'):' ')]);
}
}
str += '</table>';
return str;
}
有了這幾個函數,我在需要偵測瀏覽器對於DOM3 Core中定義的Node Interface時,只要針對這個interface的規格寫好一個property的對應就可以了:
function DOM3NodeInspector(obj) {
var map = {
attr : [
'nodeName',
'nodeValue',
'nodeType',
'parentNode',
'childNodes',
'firstChild',
'lastChild',
'previousSibling',
'nextSibling',
'attributes',
'ownerDocument',
'namespaceURI',
'prefix',
'localName',
'baseURI',
'textContent'
],
mthd : [
'insertBefore',
'replaceChild',
'removeChild',
'appendChild',
'hasChildNodes',
'cloneNode',
'normalize',
'isSupported',
'compareDocumentPosition',
'isSameNode',
'lookupPrefix',
'isDefaultNamespace',
'lookupNamespaceURI',
'isEqualNode',
'getFeature',
'setUserData',
'getUserData'
]
};
return DOMInspector('DOM3 Node Interface support.', obj, map);
};
接著可以呼叫resultParser把結果用html印出:
var obj = document.getElementById('test');
document.getElementById('panel').innerHTML = resultParser(DOM3NodeInspector(obj));
這樣就可以把結果放入到某個元素的innerHTML裡面來呈現。
例如,在Firefox4 Beta6上面跑的結果:
| DOM3 Node Interface support. | |
| SupportedAttributes | nodeName nodeValue nodeType parentNode childNodes firstChild lastChild previousSibling nextSibling attributes ownerDocument namespaceURI prefix localName baseURI textContent |
| UnsupportAttributes | |
| SupportedMethods | insertBefore replaceChild removeChild appendChild hasChildNodes cloneNode normalize isSupported compareDocumentPosition isSameNode lookupPrefix isDefaultNamespace lookupNamespaceURI isEqualNode getFeature setUserData getUserData |
| UnsupportMethods | |
| DOM3 Node Interface support. | |
| SupportedAttributes | nodeName nodeValue nodeType parentNode childNodes firstChild lastChild previousSibling nextSibling attributes ownerDocument namespaceURI prefix localName baseURI textContent |
| UnsupportAttributes | |
| SupportedMethods | insertBefore replaceChild removeChild appendChild hasChildNodes cloneNode normalize isSupported compareDocumentPosition isSameNode lookupPrefix isDefaultNamespace lookupNamespaceURI isEqualNode |
| UnsupportMethods | getFeature setUserData getUserData |
嗯...看起來Chrome7對於DOM3-Core中Node的支援比Firefox4 Beta6的稍微差一些。