[IE8] Internet Explorer 8 中的JavaScript

本文主題涵蓋 開發者工具-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 SliceAcceleratorSearch 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.logconsole.info等函式所輸出的資訊。
中斷點頁籤則可以觀察到目前所設定的所有中斷點資訊。
 
3
 
區域變數頁籤可觀察到目前此函式所使用的區域變數及值。
 
4
 
  
  這些變數的值會在F10F11時即時更新,所以如果你想觀察的變數是區域變數的話,就直接於此頁籤中觀察即可,不需設定監看式。特別注意的是,
開發者工具允許你在除錯時期改變特定變數的值。
  例如在此例中,我們原本選擇的是【簡體中文】語系,但在中斷點停下時,可以透過區域變數頁籤將變eventTarget的值改變(簡體中文是ctl10結尾,
此例改為ctl11結尾),那麼實際執行時,切換的語系也會因為eventTarget的變數值改變而跟著改變,這是開發者工具除錯JavaScript時相當犀利的優點,
可以運用此技巧來測試後端網頁的安全防堵是否完善
 
監看式頁籤中允許我們手動鍵入變數名稱,與區域變數頁籤內一樣,這些變數值會在F10F11時即時更新。
5
 
當監看的變數為物件型態時,可展開查看內部結構。
 
6
 
監看式通常用來觀察全域變數值,有些情況下我們也會在監看式中監看區域變數值,這常發生在不想在區域變數、監看式間切來切去的時候。
呼叫堆疊頁籤則是開發者工具另一讓人讚賞的功能,於此頁籤會列示到達目前中斷點前的呼叫歷程,以本例而言如圖7
 
7
 
 
點擊第二個項目就會跳到該程式碼處,這是呼叫__doPostBack函式的程式碼。
 
8
 
眼尖的話會發現,【停止偵錯】右方的按紐內容有所改變,是的!開發者工具不僅可以除錯網頁中的
inline JavaScript,同時連帶的也可除錯以<script src..>引入的JavaScript程式檔。
 
9
 
附帶一提,如果要略過此中斷點往後執行,按下F5即可,假如後面還有其它中斷點被執行到、或是此中斷點再次被執行到時,開發者工具會再次停下。
開發者工具所提供的JavaScript支援很棒,但其它功能也不錯,之後的文章會再針對其CSSHTML解部份介紹。
 
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>
 
  此例運用了IEifdef判斷式完成,請注意,以往在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函式,外面的頁面可以呼叫此
函式將訊息送入IFrameIFrame內則可以藉由掛載事件至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
 
 本文範例下載