瀏覽器動作

1使用對話視窗

與標準瀏覽器視窗不同:1.不包含 HTML 和 CSS,所以無法設定樣式,且不同瀏覽器顯示的方式略有差別。2.對話視窗是強制回應(Modal),也就是說使用者處理對話方塊之前,無法在瀏覽器視窗進行任何事情。由於上述原因,以及更好的替代方案出現,現今網站實際上已經很少使用對話方塊。然而對話方塊容易使用,在有限的情況下是合理的選擇。
JavaScript 提供三種對話方塊:

1. 警告框


最簡單的方塊類型,只是一個帶有文字資訊的視窗,使用者點擊 OK 即關閉。
          alert('You can click OK now.');
        

2. 確認框


確認框比警告框略為複雜一些,使用者可點擊 OK(確定)或是 Cancel(取消)。
          var okay = confirm('Please comfirm this message');
          if(okay){     //點擊 OK
          	...
          }else{        //點擊 Cancel
          	...
          }
        
確認框可能的用途之一是驗證使用者是否離開目前頁面,可透過監視 unload 事件捕捉:
          window.onunload = function(){
          	return confirm('Are you sure you want to leave this page?');
          }
        
上述程式碼實際上使離開頁面需要兩個步驟,或許適用於內容管理系統(CMS),避免一離開頁面,工作成果就遺失;相對來說,不適用普通網站,而且過度使用這種技術很可能讓使用者不願意再訪網站。

3. 提示框


最複雜的對話方塊,提供回應欄位,點擊 OK 或 Cancel 按鈕關閉方塊。
          var response = prompt('What say you?');
          var response = prompt('What say you?', 'What about...');
        

自訂對話方塊──使用 \n


作為安全措施,無法自訂對話方塊,透過此限制,瀏覽器變能阻止惡意的駭客仿冒另一個網站、電腦作業系統或其他具有正式外觀的東西,以上唯一能自訂的地方就是使用換行符號(\n),讓訊息多行出現:
          alert('This message is \nappearing over two lines. Wow.');
        

            alert('Hello! \nIt\'s an alert!');                  //警告框

            var okay = confirm('Which one?');                   //確認框
            if(okay){
              this.innerHTML = "click 'Yes'";
            }else{
              this.innerHTML = "click 'Cancel'";
            }

            var user = prompt('What\'s your name?');            //提示框
            alert('Hello! ' + user + '.\nNice to meet you!');
          
參考 W3School Popup Boxes:http://www.w3schools.com/js/js_popup.asp

2操作視窗

瀏覽器視窗功能強大且不可少,並能呈現任何複雜度的豐富使用者介面,JavaScript 是透過 window 物件與瀏覽器互動,其中包括 window 物件的關鍵屬性和方法。

window 全域物件


在瀏覽器使用 JavaScript 時,視窗是最上層的物件,因此在任何其他物件外部定義的函數都會成為 window 全域物件的方法,window 物件本身往往是隱含的,不需明確引用:
          function doNothing(){
          }
          doNothing();          //呼叫 function
          window.doNothing();   //同上

          var someVar = true;
          someVar;              //true
          window.somVar;        //同上,對函數外部定義的任何變數也一樣
        

按照常規,有時需要明確使用 window 物件,大多時候不需要,上述兩個例子不常見,實際上三個對話方塊方法和 Math、Date 物件都屬於 window。
其中 window 物件的三個屬性反應瀏覽器視窗的特定部分是否可見:menubar、statubar、toolbar,此舉是將個人願望強加在使用者瀏覽器上,實際上不應該試圖改變,約十幾年前流行的概念之一是改變瀏覽器視窗的狀態列文字,但基於安全考量,許多瀏覽器禁用了這項功能,預設情況下不顯示狀態列。
還有 window.navigator 這個屬性有多個子屬性,儲存瀏覽器的相關資訊,常用來透過 window.navigator.userAgent 進行瀏覽器嗅探(browser sniffing),目的是根據瀏覽器類型和版本撰寫條件式敘述,但現今有更好的替代方案取代──物件檢測。
          if(window.menubar.visible){               //查看選單列是否可見
            ...
          }

          window.status = 'Hello. I am browser.';   //顯示狀態列的文字
        
            window.menubar.visible;       //選單列
            window.toolbar.visible;       //工具列
            window.locationbar.visible;   //網址列
            window.personalbar.visible;   //個人欄
            window.scrollbars.visible;    //捲軸
            window.statusbar.visible;     //狀態列
          

操作視窗的大小和位置


曾經由網頁改變瀏覽器視窗大小或移動視窗一度非常流行,雖技術上可行,但不建議使用。
除了 Firefox 以外,這兩個屬性無法修改視窗位置,但在絕對必要的時候,這些屬性會告訴我們是否有必要移動:
除了 Firefox 以外,這些屬性無法改變視窗大小,但若有必要,他們可以當成動態修改佈局的基礎,例如視窗縮小,可用 JavaScript 使用較小的圖片:
因某種理由必須移動瀏覽器視窗,可呼叫 window 物件兩種方法:
            myWindow = window.open('', 'myWindow', 'width=200, height=200');
            myWindow.moveTo(0, 0);                //重新定位
            myWindow.document.write('移動視窗');


            myWindow.moveTo(500, 100);            //移至座標 (500,100)
            myWindow.focus();                     //focus 視窗


            myWindow.moveBy(500, 100);            //相對移至 (500,100)
            myWindow.focus();                     //focus 視窗
          

螢幕屬性


與使用者實體螢幕的資訊,可利用這些資訊客製化瀏覽器視窗和使用者體驗,不過現代瀏覽器支援以 CSS 媒體查詢來修改這些設定。
            window.screen.height        //螢幕高度
            window.screen.width         //螢幕寬度
            window.screen.availHeight   //螢幕可用工作區高度
            window.screen.availWidth    //螢幕可用工作區寬度
            window.screen.colorDepth    //螢幕色深
          

網頁屬性(補充)


            document.body.clientHeight      //網頁可見區域寬
            document.body.clientWidth       //返回網頁可見區域高
            document.body.offsetHeight      //返回網頁可見區域寬
            document.body.offsetWidth       //返回網頁可見區域高
            document.body.scrollHeight      //返回網頁正文全文高
            document.body.scrollWidth       //返回網頁正文全文寬
            document.body.scrollTop         //返回網頁被捲去的高
            document.body.scrollLeft        //返回網頁被捲去的左
          

建立新視窗


這裡建立的新視窗,與對話方塊不同的是,是真正瀏覽器視窗,非強制回應,使用者可在必要時切換到原始視窗,現今建立新視窗的情況越來越少,但運用得當可增加正面效果。
上述語法不能在屬性字串中使用任何空格或歸位鍵,此外要注意,無法在開啟視窗之後修改這些設定
          var popup = window.open('somefile.html');       //URL 可以是絕對或相對路徑

          var popup = window.open('somepage.html', 'DefinitionsWindows', 'height=200, width=200, location=no, resizable=yes, scrollbars=yes');
        
屬性 註解 預設值
height 用於內容區域,大部份瀏覽器預設值為父視窗或最近建立視窗的高度,必須至少為 100px
width 用於內容區域,大部份瀏覽器預設值為父視窗或最近建立視窗的寬度,必須至少為 100px
outerHeight 整個視窗高度,至少為 100px
outerWidth 整個視窗寬度,至少為 100px
left 預設值大約是在父視窗左側向右 20px
top 預設大約在父視窗頂端向下 20px
location 位址列是否可見 no
resizable 應始終設為 yes,在某些瀏覽器中不管該值為何,視窗大小都可以改變 no
status 在某些瀏覽器上始終顯示 no
channelmode 劇院模式 no
fullscreen 全螢幕檢視(only for IE) no
menubar Mac 上無法隱藏 no
scrollbars 應始終設為 yes no
toolbar 工具列是否可見 no
titlebar 網頁名稱標籤是否可見 yes
            window.open('browser_2_2.html');
            window.open('browser_2_2.html', 'open');
            window.open('browser_2_2.html', 'jump', 'height=500, width=500');
            window.open('browser_2_2.html', 'jump', 'outerHeight=400, outerWidth=200');
            window.open('browser_2_2.html', 'jump', 'top=200px, left=100px');
            window.open('browser_2_2.html', 'jump', 'menubar=yes');
            window.open('browser_2_2.html', 'jump', 'resizable=yes');
            window.open('browser_2_2.html', 'jump', 'scrollbars=yes');
            window.open('browser_2_2.html', 'jump', 'status=yes');
            window.open('browser_2_2.html', 'jump', 'toolbar=yes');
            window.open('browser_2_2.html', 'jump', 'location=yes');
          

因曾大量用於彈出式廣告,為此現代瀏覽器擁有內建彈出視窗攔截功能,在此情況下常常返回 null 值:
          if(popup === null){		//如果敘述為真,將整個瀏覽器視窗重新導向到彈出式廣告顯示頁面。
            ...
          }
        

關閉新視窗


          //檢查 closed 屬性可確定尚未關閉 JavaScript 建立的視窗
          if((popup !== null) && !popup.closed){
            ...
          }
        

改變焦點


          if((popup !== null) && !popup.closed){    //可以在執行前檢查現有視窗
            popup.focus();
          }
        

可存取的解決方案


這些輔助視窗透過 JavaScript 建立,沒有可存取的替代方案,而且搜尋引擎無法看到輔助視窗的內容,另外也可能被彈出視窗攔截軟體阻止視窗發生,從整體考量上,應始終採用標準方法提供輔助視窗存取──在連結上增加一個視窗名稱:
          另開新視窗
        
            U.addEvent(U.$('close'), 'click', closeFunc);
            function closeFunc(){
              window.close();
            }
          

視窗之間的溝通


當開啟多個視窗時,這時透過 JavaScript 可以讓視窗之間互相溝通:
  1. 利用視窗 A 打開新視窗 B,並指派給變數 windowB,就可以使用 windowB.window 存取視窗 B 的 window 物件。
  2. 視窗 B 使用 window.opener 取得視窗 A,就能透過 windowA.window.propertyNamewindowA.window.functionName() 引用視窗 A 的全域變數和函數。
    • window.opener: 返回目前視窗上層物件的參照,如果不是由其他視窗開啟目前的視窗,opener 屬性將返回 null
            /* 視窗 A */
            var windowB = window.open('browser_7_2.html', 'windowB');
            windowB.window.addTo(12);
            windowB.window.something;
            function getFunc(getName){
              return 'Hello! ' + getName + '. 回應視窗 A!';
            }


            /* 視窗 B */
            var windowA = window.opener;
            var something = 23;
            function addTo(what){
              something += what;
              windowA.window.getFunc('Domo');
            }
          
          
同源策略
瀏覽器內建一種安全措施─「同源策略(Same origin policy)」,根據涉及資源的來源限制可以進行的操作。
例如,http://example.com 上的兩個頁面:http://example.com/page1.html 和 http://example.com/file/page2.html,即使不同子目錄,也屬同一來源。
如果是以下狀況,那麼頁面來源就不相同:
  • HTTP 通訊協定不同,例如:https://example.com
  • 子網域之下,例如:http://blog.example.com
  • 完全不同網域,例如:http://example2.com
  • 埠號考量,此問題較不常見,也較為進階
對 JavaScript 來說,在一個視窗或框架中的程式碼只能存取相同來源、另一個視窗中的 JavaScript,而根據瀏覽器的差別,可能在載入另一個網域的資源時出錯(拋出錯誤或通知使用者),在任何一種情況下,JavaScript 與來自其他網域資源之間的溝通都將被禁止

處理瀏覽器歷史


window 物件的另一個實用屬性 history 是用來查看目前視窗的歷史,這個物件有三種方法:
當使用者點擊後退鍵時,瀏覽器將返回到快取的前一個頁面,當點擊前往一個頁面的連結時,可能會找到並載入快取的版本,如無快取,瀏覽器可能會從伺服器重新請求該頁面,因此沒有真正進行過漸進式改善。
參考 W3School The History Object:http://www.w3schools.com/jsref/obj_history.asp
          
使用框架
針對 JavaScript,主 HTML 文件及其包含的 iframe 之間的關係,類似於瀏覽器視窗和它所開啟的另一個視窗之間的關係。
iframe 頁面可以透過 parent 物件(或更正式的 window.parent)存取父文件的全域變數和方法;如果 iframe 中未嵌入文件,window.parent 屬性等同於 window.self (是視窗識別自身的一種手段):
            if(window.parent != window.self) {        // This is a child!
          
在父文件中,window.frames 屬性是一個陣列,代表文件中的每個框架和 iframe(window.length 儲存框架的數量),因此 window.frames[0] 是第一個框架,window.frames['frameName'] 是引用框架的名稱(name),也可使用 document.getElementById() 取得 iframe 元素。
根據不同瀏覽器,可透過 contentWindowcontentDocument 屬性存取框架的全域屬性和函數:
            var frame = document.getElementById('theFRame');
            var frameWindow = frame.contentWindow || frame.contentDocument;
            //可使用 frameWindow.property 和 frameWindow.function()
          
如果打算使用 HTML 和 iframe,還應該研究規範增加的新屬性:sandboxsrcdoc 屬性提供附加的安全性制定方法,seamless 屬性則能更容易將 iframe 整合到 HTML 文件的其餘部分。
參考 W3School IFrame contentWindow Property:http://www.w3schools.com/jsref/prop_frame_contentwindow.asp
參考 W3School <iframe> Attributes:http://www.w3schools.com/tags/tag_iframe.asp

重新導向瀏覽器


通常最好使用 Web 伺服器(如 Apache)進行重新導向另一頁面的動作,例如修改頁面的 URL,就希望 Web 伺服器能將瀏覽器重新導向到新目的地,而不是讓瀏覽器載入錯誤的頁面,然後才進行重新導向。有些情況下,可能無法使用 Web 伺服器(例如無權存取組態),或者此時改用 JavaScript 更有意義。
            window.location.href = 'browser_8_4.html';

            window.location.replace('browser_8_4.html');

            window.home();

            var hash = window.location.hash;
            hash = hash.slice(1);

            var getString = window.location.search;       //抓參數字串
            var delQMark = getString.slice(1);            //去掉 ?
            var perPara = delQMark.split("&");            //& 分開各參數與值

            var splitPara=[], paraName=[], paraVal=[];
            for (i = 0; i < perPara.length; i++) {
              splitPara = perPara[i].split("=");          //= 分開第 i 個參數名稱與值
              paraName.push(splitPara[0]);                //將分開的參數名稱放入 paraName
              paraVal.push(splitPara[1]);                 //將分開的參數值放入 paraVal
            }

            for(var i=0; i< paraName.length; i++){
              document.write(paraName[i] + ' => ' + paraVal[i]);
            }
          
          
建立代表性的 URL
像是分頁 tab 頁籤,這類型的動態是由 JavaScript 顯示,但是分頁本身無法被瀏覽器存取為一個頁面,固定一開始顯示會是第一個頁面,若要控制一開始進入其他分頁,可利用 # 標籤標示頁籤名稱,並在進入的 URL 中建立行為:
            var hash = window.location.hash;
            var content = hash.charAt(1);
            switch(content){
              case 'tab2':        //show tab2
                break;
              case 'tab3':        //show tab3
                break;
              case 'tab1':        //show tab1
              default:
                break;
            }
          

為了使上述方法生效,必須在頁面撞態改變時更新 URL,假設點擊頁籤呼叫 setTab() 函數,該函數將事件作為一個參數:
            function setTab(e){
              if(typeof e == 'underfined')
                var e = window.event;
            }
          
使用事件的 target 或 srcElement 屬性,該函數就能知道顯示的是哪一個頁籤,如果函數取得資訊後,將其儲存在 tab 變數中,便可相對應動態改變 URL。
瀏覽器上下頁會瀏覽這些 URL,因為瀏覽器歷史會記錄所瀏覽的任何 URL,但是不會強迫載入頁面,意思是當 URL 改變時,不會忽叫 JavaScript 更新狀態。解決方法是:建立一個計時器檢查 hash 變化。
參考 W3School The Location Object:http://www.w3schools.com/jsref/obj_location.asp

列印本頁


          //在許多行動和非桌面裝置無法使用,可以檢測確認 window 物件是否有 print() 函數
          if(typeof window.print == 'function'){
            window.print();
          }
        
            document.getElementById('printLink').onclick = function(){
              if(typeof window.print == 'function'){
                window.print();
              }
            }
          

文件物件


write()writeln() 不使用的原因如下: 然而這兩個方法適合一種情形:在某種條件下動態含括額外的資源。
例如:廣告和第三方腳本經常以這些方法動態的將程式碼插入支援 JavaScript 頁面(也常應用於 iframe)

          document.title = 'New Title';

          if (document.compatMode == "BackCompat") {
            // in Quirks mode
          }
        
參考 W3School The Window Object:http://www.w3schools.com/jsref/obj_window.asp
參考 W3School The Document Object:http://www.w3schools.com/jsref/dom_obj_document.asp

3操作 DOM

文件物件模型(DOM)最早於 1998 年由 W3C(World Wide Web Consortium)標準化,它只是呈現和處理 XML、 XHTML 和 HTML 資料的一種方法,透過 DOM,JavaScript 可以相對簡單地尋找、存取和操作 HTML 元素,進而動態改變 HTML 頁面內容和外觀。

DOM 基礎


DOM 將資料呈現為一顆樹,從單一的主幹(稱為根)開始,在 HTML 頁面中,起始點是 HTML 標記,根之上是樹枝,每根樹枝是 HTML 頁面的另一個元素,每個 HTML 元素及其屬性的值都可交由 DOM 表示。以下為範例:
            <!docutype html>
            <html lang="en">
            <head>
              <meta charset="utf-8">
              <title>This Is The Title</title>
            </head>
            <body>
              <!-- This is a comment node! -->
              <div id="one"><p>Click me!</p></div>
              <div id="two"><p>This is a paragraph <a href="#" class="popup">a link</a>.</p></div>
              <p id="demo"></p>
              <script src="js/text.js"></script>
            </body>
            </html>
          
            function myFunction() {
              var c = document.body.childNodes;
              var txt = "";
              var i;
              for (i = 0; i < c.length; i++) {
                  txt = txt + c[i].nodeName + "
"; } document.getElementById("demo").innerHTML = txt; }
元素在 DOM 的呈現為節點(Node),節點有自己的分支,這些分支也是節點。這種結構通常以家族的方式描述:父(Parents)節點有子(Child)節點,節點還有兄弟(Sibling)節點。網頁的根節點 document 沒有父節點,只有一個子節點:html。
DOM 的每個節點代表一個物件,這些物件有特殊的屬性,以便反應每個節點與其直屬家族成員之間的關係。parentNode 屬性指向目前物件的雙親,childNodes 則指向目前物件子節點的物件陣列,如上例,BODY 的 parentNode 是 HTML 節點,它有三個子節點:DIV、DIV、SCRIPT。
節點還有 firstChild 和 lastChild 屬性,以及 previousSibling 和 nextSibling 屬性,如上例,HEAD 的 firstChild 是 META 節點,該節點是 TITLE 節點的 previousSibling。
除此之外,還有文字節點,例如,TITLE 有一個內含字串「This Is The Title」的文字子節點,第二個 DIV 內的段落有一個子節點 A 和一個文字子節點(A 也有自己的文字子節點)。children 屬性代表 HTML 元素的子節點,排除了 childNode 屬性中與 HTML 節點一起返回的文字節點。

每個節點都有提供自身資訊的屬性:
理論上,可以使用這幾種樹形的組合存取 DOM 元素,針對前述 HTML 標記,在 JavaScript 中可以從 document.documentElement 開始,它會返回根元素(這裡是 HTML)。意味著 document.documentElement.lastChild 引用 BODY,document.documentElement.lastChild.children[0] 引用第一個 DIV。
繼續上述方式,就能瀏覽整個 DOM,但這個過程很快就變得乏味,而且 HTML 中的空白會導致不可預知的問題,因為在 DOM 中它代表帶有空字串的文字節點。因此 document.documentElement.lastChild.children[0] 引用第一個 DIV,但是 document.documentElement.lastChild.childNodes[0] 實際上引用了一個空的文字節點(因為 children 僅返回 HTML 元素,而 childNodes 返回所有元素)。
幸運的是,瀏覽 DOM 有多比較簡單的代替方案。 參考 W3School HTML DOM childNodes Property:http://www.w3schools.com/jsref/prop_node_childnodes.asp
參考 W3School HTML DOM nodeName Property:http://www.w3schools.com/jsref/prop_node_nodename.asp
參考 W3School HTML DOM nodeValue Property:http://www.w3schools.com/jsref/prop_node_nodevalue.asp
參考 W3School HTML DOM nodeType Property:http://www.w3schools.com/jsref/prop_node_nodetype.asp

DOM 捷徑


document 物件有一些屬性,可以作為存取重要 DOM 元素的捷徑: 例如,頁面中的第一個表單可以用 document.forms[0] 代表,也可以元素名稱代替數字索引,這樣做通常更合適:document.images['someImageName'] 或者 document.forms['theForm']
參考 W3School The Document Object:http://www.w3schools.com/jsref/dom_obj_document.asp

使用 DOM 操作


            function createPopup(e) {
                'use strict';
                if (typeof e == 'undefined') var e = window.event;

                var target = e.target || e.srcElement;
                var popup = window.open(target.href, 'PopUp', 'height=400,width=400');

                if ( (popup !== null) && !popup.closed) {
                    popup.focus();
                    return false;
                } else {
                    return true;
                }
            }


            window.onload = function() {
                'use strict';
                for (var i = 0, count = document.links.length; i < count; i++) {
                    document.links[i].onclick = createPopup;
                }
            };
          

DOM 方法


使用各種 document 屬性可能是尋找頁面元素的好方法,但內建於 document 物件的兩個卓越方法更加常用:
          var header = document.getElementById('header');
          var hLink = header.getElementsByTagName('a');

          //上述合併成一行

          var hLinks = document.getElementById('header').getElementsByTagName('a');

          //getElementsByTagName()返回的變數可視為陣列,利用陣列語法參照個別元素

          document.getElementById('header').getElementsByTagName('a')[0];
        
參考 W3School getElementById():http://www.w3schools.com/jsref/met_document_getelementbyid.asp
參考 W3School getElementsByTagName():http://www.w3schools.com/jsref/met_document_getelementsbytagname.asp
參考 W3School getElementsByClassName():http://www.w3schools.com/jsref/met_document_getelementsbyclassname.asp
參考 W3School getElementsByName():http://www.w3schools.com/jsref/met_doc_getelementsbyname.asp

CSS 選擇器


在 JavaScript 中,相對新穎的頁面元素尋找方法是透過 CSS 選擇器(Selector)引用 CSS 確定頁面元素的語法,例如依照 HTML 標記、id 或者類別,隨著 CSS 新標準的推出(目前為 CSS3),選擇器的威力越來越大。
為了在 JavaScript 中使用 CSS 選擇器,可以藉助以下兩種方法,兩者都可以在 document 物件或特定元素上呼叫,如果在特定元素執行這兩個方法,那 CSS 選擇器僅適用於該元素的子元素
          document.querySelectorAll('img.thumbnail');
          document.getElementById('nav').querySelector('a.selected');
        

這兩個方法瀏覽器支援度很好,唯獨 IE 8 (IE 8 僅支援 CSS2.1 選擇器)之前的版本不支援。
          
XPath 運算式
CSS 選擇器的替代方案 XPath 運算式更快且更進階,正如 DOM 是 XML 文件(包括 HTML)的呈現形式,XPath 則是瀏覽 XML 文件的一種手段,XPath 能夠依照標記、類別、與其他元素的關係等尋找元素。在支援 XPath 的瀏覽器上,它的性能非常優越。
但支援度不一致,主流瀏覽器目前的版本支援 XPath 1.0,更新版的 XPath 標準的支援較少,且學習新語法也需勇氣。

修改元素屬性


一旦取得 HTML 元素的參照,就可以操作 DOM 修改元素的屬性。
            window.load = init();
            function init(){
              var a1Html = '';
              var a1 = document.getElementById('a1');
              for(var i=0; i< a1.attributes.length; i++){
                a1Html += a1.attributes[i].name + ' = ' + a1.attributes[i].value + ', ';
              }
              a1.innerHTML = '.attributes => ' + a1Html;

              document.getElementById('a2').href;

              document.getElementById('checkbox1').disabled + ', ' + document.getElementById('label1').htmlFor;
              document.getElementById('checkbox2').disabled + ', ' + document.getElementById('label2').htmlFor;

              var toggle1 = 0;
              document.getElementById('b1').onclick = function(){
                if(toggle1 == 0){
                  document.getElementById('span1').classList.add('pBlock');
                  toggle1 = 1;
                }else{
                  document.getElementById('span1').classList.remove('pBlock');
                  toggle1 = 0;
                }
                document.getElementById('span1').className;
              }

                document.getElementById('div1').childElementCount;
                document.getElementById('div1').clientHeight;
                document.getElementById('div1').clientWidth;
                document.getElementById('div1').clientTop;
                document.getElementById('div1').clientLeft;
            }
          
參考 W3School The HTML DOM Element Object:http://www.w3schools.com/jsref/dom_obj_all.asp

建立元素


操作 DOM 的最後一種方法是實際建立或刪除 HTML 元素,但建立元素並新增到 DOM 的手段比使用 innerHTML 慢,不過有時候卻是必要的。

直接從無到有新增元素的方法

          var p = document.createElement('p');
        
這時我們建立了一個新的 HTML 段落元素,但它還是空的,只具備預設的屬性,還不是 DOM 的一部份,也就是最終使用者看不見。

設定不同的 HTML 和屬性,以便進行客製化:
          p.innerText = 'This is some text';
          p.className = 'enhanced';
        
最後,使用多種可能的方法,像是 insertBefore()appendChild()replaceChild() 將元素增加到 DOM,具體方法取決於所需的最終結果,任何情況下,至少需要一個元素的參照,以便容納要置放的新元素,假設新的段落出現在某個 DIV 中:
          var div = document.getElementById('someDiv');
          div.appendChild(p);       //在目標元素上呼叫 appendChild(),就可將段落新增到 DIV 中
        
            var p = document.createElement('p');
            p.innerText = 'This is some text';
            p.className = 'enhanced';
            var div = document.getElementById('someDiv');
            div.appendChild(p);
          
參考 W3School createElement():http://www.w3schools.com/jsref/met_document_createelement.asp

至少需要一個元素的參照,來新增所要置放的元素

如果打算替換成另一種同類型的元素,則直接取代元素的內容或屬性,不建議使用上述方法。
            var span1 = document.createElement('span');
            span1.innerText = 'span 1';
            var p1 = document.getElementById('p1');
            p1.appendChild(span1, document.getElementById('strong1'));


            var span2= document.createElement('span');
            span2.innerText = 'span 2';
            var p2 = document.getElementById('p2');
            p2.insertBefore(span2, document.getElementById('strong2'));


            var span3 = document.createElement('span');
            span3.innerText = 'span 3';
            var p3 = document.getElementById('p3');
            p3.replaceChild(span3, document.getElementById('strong3'));
          
參考 W3School appendChild():http://www.w3schools.com/jsref/met_node_appendchild.asp
參考 W3School insertBefore():http://www.w3schools.com/jsref/met_node_insertbefore.asp
參考 W3School replaceChild():http://www.w3schools.com/jsref/met_node_replacechild.asp

其他建立元素的用法

            var txt = document.createTextNode("->Hello World!");      //建立新文字
            //將此元素搭配上述三個插入元素的方法放置相關位置
            document.getElementById("p1").appendChild(txt);           //插入文字

            var cln = document.getElementById("p1").cloneNode(true);  //複製 p1 元素
            document.getElementById("p2").appendChild(cln);           //插入元素
          
參考 W3School createTextNode():http://www.w3schools.com/jsref/met_document_createtextnode.asp
參考 W3School cloneNode():http://www.w3schools.com/jsref/met_node_clonenode.asp

刪除元素

          var div = document.getElementById('someDiv');
          div.removeChild(p);
        
          var p = document.getElementById('someP');
          var parent = p.parentNode;    //如果知道需要刪除的元素,但不確定其父元素,可透過 parentNode 屬性搜尋
          parent.removeChild(p);

          /*上述程式碼簡化*/
          var p = document.getElementById('someP');
          p.parentNode.removeChild(p);
        
參考 W3School removeChild():http://www.w3schools.com/jsref/met_node_removechild.asp

4JavaScript 與 CSS

「主瀏覽器」和「漸進式改善」的定義都設及三個組成部分:JavaScript、DOM 操作、CSS。儘管 JavaScript 和 CSS 是兩種不同的技術,在 Web 瀏覽器扮演不同的角色,但可組合兩者,以便改進使用者體驗。

引用個別樣式


取得瀏覽器元素之後,便可透過 style 屬性拿到元素目前的 CSS 樣式。
注意針對所有屬性,必須使用「駝峰式大小寫」,包括帶有 dash 符號的屬性也是如此,其次,以 JavaScript 設定大小時,每次都必須指定大小的單位。
          var elem = document.getElementById('someElement');
          elem.style.fontSize = '10em';
        
style 屬性指派的直被當作「內嵌樣式」(inline style),它將優先於其他地方定義的樣式規則,這表示當讀取 style 屬性值時,它反應的只是內嵌樣式,而非所有適用的樣式。

          var elem = document.getElementById('someElement');

          var elementStyle = elem.currentStyle;               //只支援 IE 和 Opera

          var elementStyle = window.getComputedStyle(elem);   //針對不支援 currentStyle 的瀏覽器
          elementStyle['display'];                            //getComputedStyle()返回值作關聯陣列,回傳 inline
          elementStyle.display;                               //getComputedStyle()返回值作物件,回傳 inline
        
getComputedStyle() 方法的參數是元素的參照,返回值可以當做關聯陣列或物件來處理
注意,這兩種方法只能讀取,不能修改樣式,而且,返回的大小帶有單位:10em,而不是 10

            var p1 = document.getElementById('p1');
            p1.style.width = '100px'
            document.getElementById('span1').innerText = p1.style.width

            var p2 = document.getElementById('p2');
            if (p2.currentStyle) {
              document.getElementById('span2').innerText = p2.currentStyle.width
            }

            var p3 = document.getElementById('p3');
            if (window.getComputedStyle) {
              var p3Style = window.getComputedStyle(p3);
              document.getElementById('span3').innerText = p3Style.width
            }
          

引用樣式表


一種和使用個別樣式不同的替代方案是操作整份頁面的樣式表document.styleSheets 屬性存放頁面所用的樣式表清單,包括外部的 CSS 檔案和內部定義的樣式。如果 style HTML 元素使用 id 屬性,那麼還可以 document.getElmentById() 引用單獨樣式表:
          <link rel="stylesheet" id="mainStyleSheet" href="css/style.css">
        
          var mainCSS = document.getElementById('mainStyleSheet');
        

一旦取得樣式表,能使用以下屬性取得或改變樣式表內容:
          mainCSS.src;                    //回應 css/style.css
          mainCSS.disable = 'disabled';   //停用樣式表
        

以下方法改變樣式表規則:
因 CSS 規則的應用根據順序而有差別,所以索引位置非常重要
          mainCSS.insertRule('.hide: {visibility: none;}', 50);
          mainCSS.deleteRule(5);
          mainCSS.addRule('.hide', '{visibility: none;}', 50);
        

一般來說,建議固定 CSS 規則並保持恆定,但是對於進階、以 CSS 驅動的網站來說,使用 JavaScript 操作規則是合理的。
舉一個漸進式改善的例子,可先建立一個頁面,預設禁用樣式表,僅在支援 JavaScript 時啟用,或者可透過 createElement() 建立 style 類型的元素,動態產生一組新的樣式規則:
          var s = document.createElement('style');
          s.innerText = 'body { font-size: 90%; font-family: arial, sans-serif;}';     //建立樣式內容
          document.body.appendChild(s);        //樣式元素作為頁面主體的子元素,新增到頁面上
        

5使用 Cookie

Cookie 是 Netscape 發明的另一項技術,已經成為動態網站不可或缺的一部分。通常用於瀏覽器請求 Web 頁面的超本文傳輸協定(HyperText Transfer Protocol, HTTP),其為一種無狀態的協定,也就是說,當從頁面 A 轉到相同網站的頁面 B 時,伺服器無法分辨兩個頁面得使用者是同一人。這是一個問題,沒有維護狀態的機制,也就無法管理使用者(亦即登入和註冊地能力)以及自訂內容顯示,因此電子商務上的用途變受到限制。幸運的是,有兩種方法能夠維護狀態:使用會話(Session)或者 Cookie。會話只能透過伺服器技術完成,因為其資料儲存在伺服器上。而 Cookie 則可利用伺服端技術或瀏覽器的 JavaScript 來管理。

Cookie 就是存放在瀏覽器與特定網站相關的資訊「即時貼」。當訪客連結一個網站時,網站伺服器會發送一個 Cookie 至使用者的瀏覽器。瀏覽器儲存 Cookie 之後,後續的網頁存取都會回傳給伺服器。重要的是,Cookie 僅從原來接收它的機器上發回給相同的伺服器,即便在同一個伺服器,但不同網域,也無法讀取,因此這種限制有著安全和性能的雙重好處。

Cookie 包含多個互不相關的資訊片段: 到期日期和時間必須格式化為 UTC 字串。路徑是 Cookie 在伺服器上的有效目錄,例如,如果網址為 www.example.com,「/」路徑使 Cookie 可以從根目錄和任何子目錄讀取。倘若路徑為 /forums,則伺服器只能再使用者存取 www.example.com/forums(包括其子目錄)下的頁面時,才能讀到此 Cookie。
          
使用 Cookie 所需考慮的要點
不管用於 JavaScript 還是伺服端技術,Cookie 都非常方便,但也有其侷限性。首先,使用者電腦上很容易看到 Cookie,所以絕不能存放敏感資訊。其次,Cookie 很容易被終端使用者操作,所以伺服器 ── 網站 ── 應該始終驗證 Cookie 值,並謹慎使用。
綜合上述,建議在 JavaScript 中像對 JavaScript 本身那樣對待 Cookie:視為向使用者提供強化功能的一種方式。注重安全的應用程式應該始終使用由伺服端技術實作的會話。

建立 Cookie


JavaScript 的 Cookie 透過 document 物件的 cookie 屬性取得。下面的程式能夠建立一個 Cookie:
          document.cookie = value;
        

Cookie 值必須使用特定的語法,這種語法以 cookieName=cookieValue 開始。例如,底下的 Cookie 用來儲存使用者偏愛的字體大小:
(這種發送的 Cookie 有預設的到期時間、路徑和網域)
          document.cookie = 'fontSize=14';
        

在 Cookie 值內新增一個分號,後面加上 Cookie 屬性 expires、path、domain 及內容,便可修改這些值:
          document.cookie = 'fontSize=14;expires=' + someDate.toGMTString() + ';path=/subdirectory;domain=*.example.com';
        

可以使用不同的 Cookie 名稱,設定多個 Cookie,將其指派給相同的 document.cookie 屬性:
          document.cookie = 'fontSize=14';
          document.cookie = 'color=3C9';
        

上述做法是有效的,因為 document.cookie 是特殊的屬性,每一次的賦值都新增到 Cookie 列表中,而不會替換之前的 Cookie。
window.navigator.cookieEnabled 屬性則說明瀏覽器是否啟用 Cookie。

讀取 Cookie


一旦在 JavaScript 設定 Cookie,就可以開始讀取。無論設定幾個 Cookie,document.cookie 屬性都代表所有可用的 Cookie。
多個 Cookie 之間用分號隔開,格式為 name=value,為了讀取每個 Cookie,必須先拆解字串,接著以一個 for 迴圈遍覽 Cookie:
          var cookie = document.cookie.split(';');

          for(var i=0, count = cookie.length; i< count; i++){
            //cookie[i] 就是 name = value
          }
        
for 迴圈先根據等號,將個別的 Cookie 值(name=value 組合)分解為單獨的部分。注意,無法存取單獨的 Cookie 屬性 ── 到期時間、路徑和網域,因為存取 Cookie 本身就說明 Cookie 尚未到期,可用於目前路徑和網域中的頁面。
PHP 腳本發送的 Cookie,在載入另一個頁面或重新載入同一頁面之前無法存取,與此不同,可立即讀取 JavaScript 的 cookie。

刪除 Cookie


在 JavaScript 中刪除 Cookie 只需要設定一個同名 Cookie,但不提供 Cookie 值,並將到期時間填入過去時刻 ── 如 UNIX 時間戳記:
          document.cookie = 'fontSize=;expires=Thu, 01-Jan-1970 00:00:01 GMT';
        
為了達到最佳的可靠性,路徑和網域的內容應該與建立 Cookie 時相同。

建立 Cookie 程式庫


          var COOKIE = {     //建立名為 COOKIE 的物件,腳本中唯一的全域變數

              //定義 setCookie() 方法,三個參數分別是 Cookie 名稱、值、到期時間
              setCookie: function(name, value, expire) {
                  'use strict';
                  //首先建立一個用於完整值的字串,並指派給 document.cookie(實際上需加上驗證步驟)
                  //encodeURIComponent() 函數避開可能有問題的字元,並預防伺服器問題,用來將值安全儲存在 Cookie
                  var str =  encodeURIComponent(name) + '=' + encodeURIComponent(value);
                  str += ';expires=' + expire.toGMTString();
                  document.cookie = str;
              },

              //定義 getCookie() 方法,一個參數為 Cookie 名稱,函數返回該 Cookie 的值
              getCookie: function(name) {
                  'use strict';
                  var len = name.length;
                  //按照分號拆解 Cookie 值
                  var cookies = document.cookie.split(';');
                  //遍覽 Cookie
                  for (var i = 0, count = cookies.length; i < count; i++) {
                      //有些瀏覽器會在 document.cookie 中的兩個 Cookie 之間加上一個空格,必須避免這個空格
                      var value = (cookies[i].slice(0,1) == ' ') ? cookies[i].slice(1) : cookies[i];
                      //在發送時將會進行編碼,所以需使用對應的 decodeURIComponent() 函數解碼 value 變數
                      value = decodeURIComponent(value);
                      //如果是目前的 Cookie 是腳本所需的 Cookie 值相同,則返回 Cookie 值
                      if (value.slice(0,len) == name) {
                          return value.split('=')[1];
                      }
                  }
                  return false;
              },

              //定義 deleteCookie() 方法
              deleteCookie: function(name) {
                  'use strict';
                  //刪除 Cookie 名稱
                  document.cookie = encodeURIComponent(name) + '=;expires=Thu, 01-Jan-1970 00:00:01 GMT';
              }
          };
        
參考 MSDN encodeURIComponent 函式:https://msdn.microsoft.com/zh-tw/library/aeh9cef7(v=vs.94).aspx
參考 MSDN decodeURIComponent 函式:https://msdn.microsoft.com/zh-tw/library/91b80x6x(v=vs.94).aspx

使用 Cookie 程式庫


實作例子,建立一套系統,讓使用者挑選頁面的主題,這個實例是使用者剛進入頁面時不套用任何 CSS,而是當點擊某個連結時,不但改變目前頁面主題,還發送一個 Cookie,後續的存取才能使用相同的 CSS,結合了事件處理、阻止瀏覽器預設行為、Cookie 和 DOM 的操作:
              //定義 setTheme() 函數,一個參數代表使用者選擇的主題(樣式表)
              function setTheme(theme) {
                  'use strict';
                  var css = null;
                  if (document.getElementById('cssTheme')) {      //如果已經存在 link 元素,則直接更新
                      css = document.getElementById('cssTheme');
                      css.href = '../css/' + theme + '.css';
                  } else {                                        //如果不存在 link 元素,則自行建立
                      css = document.createElement('link');
                      css.rel = 'stylesheet';
                      css.href = '../css/' + theme + '.css';
                      css.id = 'cssTheme';
                      document.head.appendChild(css);
                  }
              }

              //定義 setThemeCookie() 函數,將於使用者點擊連結時呼叫,在 Cookie 中儲存選中的主題
              //該 Cookie 將被更新為從目前開始的一週內到期,然後更新頁面,一週內將保存使用者偏好設定
              function setThemeCookie(e) {
                  'use strict';
                  //為了解點擊的連結,需一個針對事件和事件目標的參照
                  if (typeof e == 'undefined') e = window.event;
                  if (e.preventDefault) {
                      e.preventDefault();
                  } else {
                      e.returnValue = false;
                  }
                  var target = e.target || e.srcElement;

                  //設定 Cookie
                  var expire = new Date();                    //今天
                  expire.setDate(expire.getDate() + 7);       //一週後
                  COOKIE.setCookie('theme', target.id, expire);

                  setTheme(target.id);                        //更新主題
                  return false;                               //阻止預設行為

              }

              //建立一個函數處理視窗的載入事件
              window.onload = function() {
                  'use strict';
                  //新增點擊事件處理器
                  document.getElementById('aTheme').onclick = setThemeCookie;
                  document.getElementById('bTheme').onclick = setThemeCookie;
                  //讀取 Cookie 並設定相關的主題
                  var theme = COOKIE.getCookie('theme');     //取得主題的 cookie 值
                  if (theme) {                               //如果之前已儲存一個主題,頁面必須更新為所選主題
                      setThemeCookie(theme);
                  }
              };
            
參考 W3School JavaScript Cookies:http://www.w3schools.com/js/js_cookies.asp

6使用計時器

瀏覽器提供另一種功能是「建立計時器」,此處指的是「倒數計時器」,透過它便能告知 JavaScript 在特定時間間隔之後執行某些程式
          setTimeout(function(){
            alert('It has been 2000 milliseconds');
          }, 2000);

          setInterval(function(){
            alert('It has been 2000 milliseconds');
          }, 2000);
        

任何時候都應該有最終停止計時器的程式碼,首先需要將 setInterval()setTimeout() 執行結果指派給一個變數: setTimeout() 建立的計時器,也可以透過將呼叫動作賦值給一個變數,然後以該變數作為參數執行 clearInterval() 函數來停止。
            var interval = setInterval(doThis, 3000);

            var n = 0;
            function doThis(){
              document.write('This is alert #' + n + '
'); n++; if(n == 5) clearInterval(interval); //總共會跑 5 次,共 15 秒 }
參考 W3School setTimeout():http://www.w3schools.com/jsref/met_win_settimeout.asp
參考 W3School setInterval():http://www.w3schools.com/jsref/met_win_setinterval.asp
參考 W3School clearTimeout():http://www.w3schools.com/jsref/met_win_cleartimeout.asp
參考 W3School clearInterval():http://www.w3schools.com/jsref/met_win_clearinterval.asp

舉例:淡出效果


設定計時器時有一個問題:函數不能保證在精確的時間間隔中執行,原因是 JavaScript 使用單執行緒,也就是說一次只能做一件工作。在計時器的時間消逝的過程中,使用者或瀏覽器也可能觸發必須由其他 JavaScript 程式碼處理的事件。如果時間間隔到達時,JavaScript 正忙於處理其他事件,就必須等到空閒時才能呼叫相關的函數。任何情況下,函數都不會在時間間隔抵達之前執行,而在之後也可能無法立刻執行(意指所有事件(包括計時器事件)都進入一個佇列,並儘快得以處理)。

計時器可用於多種不同目的,例如動畫和特效,但是複雜的動畫不應該由計時器來完成,以下為將 DIV 的不透明度從 100% 下降到 0%,以便產生淡出效果:
            var div = document.getElementById('someDiv');
            var opacity = 1;
            var fader = setInterval(function(){             //建立計時器
              opacity -= .1;                                //每隔 0.1 秒,透明度降低 0.1
              if(opacity >= 0){                             //當透明度不低於 0 時
                if(typeof div.style.opacity == 'string'){   //確認是否有 opacity 屬性
                  div.style.opacity = opacity;              //改變透明度
                }else{
                  div.style.filters = 'alpha(opacity=' + (opacity * 100) + ')'; //for IE
                }
              }else{                                        //當透明度等於 0 時
                clearInterval(fader);                       //停止計時器
              }
            }, 100);
          

舉例:變更 URL 不再次執行 JavaScript


計時器的另一個用處是自動更新內容,例如目前時間、天氣或股票價格等,此例為:後退和前進按鈕會改變 URL,但不會導致 JavaScript 再次執行。解決方案就是採用一個計時器,再監控 hash 值的改變
          var hash = window.location.hash;
          var hashWatcher = setInterval(function(){
            if(window.location.hash != hash){
              updatePage();
            }
          }, 1000);
        
          
(不)使用 eval()
eval() 函數實際上用來執行一段編寫腳本時未知的程式碼字串,只有一個字串參數,並將 JavaScript 程式碼執行,以下兩行執行結果相同:
            alert('This is an alert.');
            eval("alert('This is an alert.')");
          
絕不應該像上例一般將 eval() 用在文字形式的字串上,因直接使用 JavaScript 程式碼的效能更好,而這也是不應該使用 eval() 的原因:盲目地執行未知的程式碼極其危險。這種做法使腳本暴露在注入攻擊的危險之下:在這種攻擊中,惡意的使用者有意提供「壞」資料,希望了解某些細節或者導致損害。歷史經驗告訴我們,透過 eval() 執行的程式碼性能太差,而且難以偵錯。
不過 eval() 也有合理的用途,傳統的用法之一是將 Ajax 返回的資料轉換為 JavaScript 物件,但現今的 JavaScript 中,幾乎任何方法都比 eval() 更好,應該對呼叫它的程式碼保持懷疑態度。 參考 MSDN eval 函式:https://msdn.microsoft.com/zh-tw/library/12k71sw7(v=vs.94).aspx
DEMO MEMO