自己偵測瀏覽器對於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的稍微差一些。