本文主題涵蓋 開發者工具-JavaScript Debugger , 開發者工具-JavaScript Profiler , JavaScript Language Changes of IE 8 , 用JavaScript偵測IE 版本 , 用CSS偵測IE版本 , Query Selector API, postMessage API
Internet Explorer 8 中的JavaScript
文/黃忠成
本文主題涵蓋
1. 開發者工具-JavaScript Debugger
2. 開發者工具-JavaScript Profiler
3. JavaScript Language Changes of IE 8
4. 用JavaScript偵測IE 版本
5. 用CSS偵測IE版本
6. Query Selector API
7. postMessage API
開發者工具及JavaScript Debugger
在Internet Explorer 8中,最具亮點的幾個特色就是開發者工具、三大新功能(Web Slice、Accelerator、Search Provider)兩個,其中三大新功能的部份,
在Microsoft網站及許多Blog都已著墨許多,因此本文就不談這個,未來在此系列文章內,預計將由實際案例應用來介紹她們。
對於許多程式設計師及網頁設計師而言,開發者工具的誕生是令人興奮的,其對JavaScript的除錯支援,對CSS的解析能力,皆是大家夢寐以求的。
要測試IE 8中對JavaScript除錯能力很簡單,只要開啟任一使用JavaScript的網頁,按下F12啟動開發者工具,即可在指令碼頁籤中於想要停下的JavaScript
程式碼行設定中斷點(F9是設定中斷點的快速鍵),然後按下【開始偵錯】開始進行除錯。
圖1

開始時,由於某些JavaScript需要特定的動作觸發才會執行,而有些則是已經執行過了,所以大多數情況,在你按下【開始偵錯】按紐後不會有任何反應,
這時你可以回到IE 8的瀏覽頁面,按下【F5】來重新載入網頁,此時如果中斷點所在的位置是載入網頁時會執行到的行,那麼開發者工具就會跳出來。
以MSDN首頁來說,我們所設定的中斷點處__doPostBack函式會在切換語言時觸發,因此這裡我們只要切換一下語言,開發者工具就會在__doPostBack
函式處停下。
圖2

當停下後,你可以用F10來【逐程序執行】,也就是Step Over模式,在這個模式下不會追入函式內部,也就是說當呼叫函式時,直接執行並往下一行走,
按下F11則是【逐步執行】,也就是Step Into模式,這種模式會追入函式內部,也就是說當呼叫函式時,直接跳入該函式的程式碼逐步執行。
在右方區塊中,你可以觀察主控台頁籤,這裡通常會出現一些訊息,例如【程式碼錯誤】、或由程式以console.log、console.info等函式所輸出的資訊。
中斷點頁籤則可以觀察到目前所設定的所有中斷點資訊。
圖3

區域變數頁籤可觀察到目前此函式所使用的區域變數及值。
圖4

這些變數的值會在F10、F11時即時更新,所以如果你想觀察的變數是區域變數的話,就直接於此頁籤中觀察即可,不需設定監看式。特別注意的是,
開發者工具允許你在除錯時期改變特定變數的值。
例如在此例中,我們原本選擇的是【簡體中文】語系,但在中斷點停下時,可以透過區域變數頁籤將變數eventTarget的值改變(簡體中文是ctl10結尾,
此例改為ctl11結尾),那麼實際執行時,切換的語系也會因為eventTarget的變數值改變而跟著改變,這是開發者工具除錯JavaScript時相當犀利的優點,
你可以運用此技巧來測試後端網頁的安全防堵是否完善。
監看式頁籤中允許我們手動鍵入變數名稱,與區域變數頁籤內一樣,這些變數值會在F10、F11時即時更新。
圖5

當監看的變數為物件型態時,可展開查看內部結構。
圖6

監看式通常用來觀察全域變數值,有些情況下我們也會在監看式中監看區域變數值,這常發生在不想在區域變數、監看式間切來切去的時候。
呼叫堆疊頁籤則是開發者工具另一讓人讚賞的功能,於此頁籤會列示到達目前中斷點前的呼叫歷程,以本例而言如圖7。
圖7

點擊第二個項目就會跳到該程式碼處,這是呼叫__doPostBack函式的程式碼。
圖8

眼尖的話會發現,【停止偵錯】右方的按紐內容有所改變,是的!開發者工具不僅可以除錯網頁中的
inline JavaScript,同時連帶的也可除錯以<script src..>引入的JavaScript程式檔。
圖9

附帶一提,如果要略過此中斷點往後執行,按下F5即可,假如後面還有其它中斷點被執行到、或是此中斷點再次被執行到時,開發者工具會再次停下。
開發者工具所提供的JavaScript支援很棒,但其它功能也不錯,之後的文章會再針對其CSS、HTML解部份介紹。
JavaScript Profiler
開發者工具還提供了JavaScript Profiler的能力,也就是由特定點開始到特定點結束期間,所有JavaScript函式的執行時間,與偵錯能力相同,
不僅可偵測網頁中的inline JavaScript,連帶著引入的JavaScript程式檔都一併偵測,程式1是用來測試此功能的範例網頁。
程式1
TestProfiler.htm |
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title></title> <script language="javascript" type="text/javascript"> function DoFirst() { for (val = 0; val < 10000; val++) { document.getElementById("test"); } } function DoSecond() { for (val = 0; val < 30000; val++) { document.getElementById("test"); } } </script> </head> <body> <input id="test" type="button" value="Run" onclick="DoFirst();DoSecond();alert('ok');" /> </body> </html> |
開啟此網頁後,以F12啟動開發者工具,切到【分析者工具】頁籤,按下【開始設定資料】按紐,告知開發者工具由現在這個點開始偵測。
圖10

在按下【開始設定資料】按紐後,該按紐會變成【結束設定資料】,稍後進行完觸發JavaScript動作後,即可再按下此按紐來結束偵測,此時會列出所有的偵測所得資訊,
現在請按下網頁上的【Run】按紐,待跳出訊息框後再按下【結束設定資料】按紐。
圖11

圖12

JavaScript Language Changes of IE 8
在Internet Explorer 8中,添加了許多新的JavaScript函式,這些會在本文後面進行介紹,在這之前,我們先看看Internet Explorer 8中,一個關於getElementById函式的變動,
這個函式在IE 7前對於傳入的id參數值是不分大小寫的,在IE 8時,各位要注意到此函式開始區分大小寫了,以程式2的例子來說。
程式2
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title></title> </head> <body> <form id="form1" runat="server"> <div> <div id="T1">TEST</div> <input type="button" id="btn1" value="test" onclick="document.getElementById('t1').innerHTML='<br>TEST HTML</b><div><p>Test1</p></div>';" /> </div> </form> </body> </html> |
在IE 7時,此網頁可正常執行無誤,那是因為getElementById不分大小寫的緣故,所以不管用t1或是T1都可以正確執行。但在IE 8時,就得注意了,t1 != T1,
分辨大小寫的特性會讓此網頁產生錯誤。
透過JavaScript判定IE版本
這很簡單,程式3是一個例子。
程式3
DetectIEVersion_Short.aspx |
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="DetectIEVersion_Short.aspx.cs" Inherits="DetectIEVersion_Short" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title></title> </head> <body> <form id="form1" runat="server"> <div> <script language="javascript"> var isIE8 = navigator.userAgent.indexOf("MSIE 8"); if (isIE8 > 0) alert("in IE8"); else alert("not in IE8"); </script> </div> </form> </body> </html> |
不過這是偷懶版,官方希望我們使用程式4的較長、較精確的寫法。
程式4
DetectIEVersion.aspx |
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="DetectIEVersion.aspx.cs" Inherits="DetectIEVersion" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title></title> </head> <body> <form id="form1" runat="server"> <div> <script language="javascript"> function getInternetExplorerVersion() { var rv = -1; // Return value assumes failure. if (navigator.appName == 'Microsoft Internet Explorer') { var ua = navigator.userAgent; var re = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})"); if (re.exec(ua) != null) rv = parseFloat(RegExp.$1); } return rv; } function checkVersion() { var msg = "You're not using Windows Internet Explorer."; var ver = getInternetExplorerVersion(); if (ver > -1) { if (ver >= 8.0) msg = "You're using a recent copy of Windows Internet Explorer." else msg = "You should upgrade your copy of Windows Internet Explorer."; } alert(msg); } checkVersion(); </script> </div> </form> </body> </html> |
透過CSS判定IE 版本
除了透過JavaScript判別IE版本外,網頁設計師也常用CSS來判定IE版本並選取對應的CSS,這可以透過程式5的方式完成。
程式5
Default_IE8_IE7.aspx |
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default_IE8_IE7.aspx.cs" Inherits="Default_IE8_IE7" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head id="Head2" runat="server"> <title></title> <!--[if gte IE 8]> <style type="text/css"> div { border: medium dashed #00FF00; border: 2px solid #0033CC; } .container { min-width:400px; } </style> <![endif]--> <!--[if lt IE 8]> <style type="text/css"> div { border: medium dashed #00FF00; border: 2px solid #009966; /*firefox*/ *border: 2px solid #0033CC; /*ie7*/ _border: 2px solid #FF3366; /*ie6或更舊的版本*/ } .container { min-width:400px; } </style> <![endif]--> </head> <body> <form id="form2" runat="server"> <div> <div id="t1">TEST</div> <input type="button" id="Button1" value="test" onclick="document.getElementById('t1').innerHTML='<br>TEST HTML</b><div><p>Test1</p></div>';" /> </div> </form> </body> </html> |
此例運用了IE的ifdef判斷式完成,請注意,以往在style前加【*】號的方式不適用於IE 8,也就是說網路上常見的程式6寫法僅適用於判定IE 7,
當使用者使用IE 8時,會出現不同結果。
程式6
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title></title> <style type="text/css"> div { border: medium dashed #00FF00; border: 2px solid #009966; /*firefox*/ *border: 2px solid #0033CC; /*ie7*/ _border: 2px solid #FF3366; /*ie6或更舊的版本*/ } .container { min-width:400px; } </style> </head> <body> <form id="form1" runat="server"> <div> <div id="t1">TEST</div> <input type="button" id="btn1" value="test" onclick="document.getElementById('t1').innerHTML='<br>TEST HTML</b><div><p>Test1</p></div>';" /> </div> </form> </body> </html> |
Query Selector API
相信大家都有體驗過JQuery的強大選取HTML元素能力,W3C在之前就有製訂了一個關於選取HTML元素的規格,名為【Query Selector API】,
IE 8中正式的支援此規格,程式7是運用這些API的範例。
程式7
UsingQuerySelector.aspx |
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <!-- http://www.w3.org/TR/css3-selectors/ --> <head> <title></title> <script language="javascript" type="text/javascript"> function GetElement() { //選取id為div1的Element,只回傳第一個找到的HTML元素 var div = document.querySelector("#div1"); div.innerHTML = "Selected"; } function GetSingleElement() { //選取id為div1或是id為s1的Element,只回傳第一個找到的HTML元素, //也就是說找到div1的話就回傳div1,如沒找到,就繼續找id為s1, //如找到就回傳s1,否則就回傳null. var div = document.querySelector("#div1, #s1"); div.innerHTML = "Selected"; } function GetInnerElement() { //選取id為s1內的子div Element,僅回傳第一個找到的子div Element. var div = document.querySelector("#s1 div"); div.innerHTML = "Selected"; } function GetElements() { //選取id為div1及id為s1的Elements,回傳所有找到的HTML元素, //此回傳值為一陣列。 var divs = document.querySelectorAll("#div1,#s1"); for (val = 0; val < divs.length; val++) divs[val].innerHTML = "Selected"; } function GetAllElement() { //選取所有span Elements,回傳所有找到的HTML元素, //此回傳值為一陣列。 var spans = document.querySelectorAll("span"); for (val = 0; val < spans.length; val++) spans[val].innerHTML = "Selected"; } function GetAllElementWithClass() { //選取所有套用.c1及.c2 style(class)的的Elements,回傳所有找到的HTML元素, //此回傳值為一陣列。 var spans = document.querySelectorAll(".c1, .c2"); for (val = 0; val < spans.length; val++) spans[val].innerHTML = "Selected"; } </script> </head> <body> <input type="button" value="Select Div" onclick="GetElement()" /> <input type="button" value="Select First Match Div" onclick="GetSingleElement()" /> <input type="button" value="Select Match Divs" onclick="GetElements()" /> <input type="button" value="Select InnerDiv" onclick="GetInnerElement()" /> <input type="button" value="Select Spans" onclick="GetAllElement()" /> <input type="button" value="Select Styles" onclick="GetAllElementWithClass()" /> <div id="div1"></div> <div id="s1"> S1 Content <div>TEST</div> </div> <span></span> <span></span> <span></span> <p class="c1"></p> <p class="c1"></p> <p class="c1"></p> <p class="c2"></p> <p class="c2"></p> <p class="c2"></p> </body> </html> |
更多的Query Selector語法可參考http://www.w3.org/TR/css3-selectors/ 。
post Message API
以往,如果我們在網頁中內嵌許多IFrame時,常會遇到不知如何讓這些IFrame互通訊息的情況,IE 8提供了一個postMessage函式,外面的頁面可以呼叫此
函式將訊息送入IFrame,IFrame內則可以藉由掛載事件至onmessage來接收訊息,程式8是一個這樣的例子。
程式8
InnerFrame.aspx |
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="InnerFrame.aspx.cs" Inherits="InnerFrame" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title></title> </head> <body> <form id="form1" runat="server"> <div> <script language="javascript" type="text/javascript"> window.attachEvent('onmessage', function(e) { if (e.origin.indexOf('http://localhost') >= 0) { if (e.data == 'Hello World') { document.getElementById("t1").innerHTML = "Got Message"; } } }); </script> <div id="t1"></div> </div> </form> </body> </html> |
OuterFrame.aspx |
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="OuterFrame.aspx.cs" Inherits="OuterFrame" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title></title> </head> <body> <form id="form1" runat="server"> <div> <script language="javascript" type="text/javascript"> function sendMessageToFrame(msg) { var w = document.getElementById("Frame1").contentWindow; w.postMessage(msg,"http://localhost"); } </script> <a href="#" onclick="sendMessageToFrame('Hello World');">Test Link</a> </div> <iframe src="InnerFrame.aspx" id="Frame1"></iframe> </form> </body> </html> |
postMessage的第二個參數是描述呼叫的Domain(網域),技術上IE 8並不硬性規定postMessage僅能送訊息到同一Domain,不過就安全性而言,
我們依然要遵守此原則,於onmessage事件中判斷訊息來源,避免不必要的安全漏洞產生。
圖13為執行OuterFrame.aspx的情況。
圖13

按下Test Link連結時,sendMessageToFrame函式會以postMessage送出訊息到frm1這個IFrame,此時frm1內的InnerFrame.aspx中的onmessage會被觸發,
而後將此訊息顯示在t1這個div中,圖14是此流程示意圖。
圖14

本文範例下載