第八章 BOM

BOM(瀏覽器對象模型)提供了不少對象,用於訪問瀏覽器的功能。javascript

8.1 window 對象html

BOM 的核心對象是window,它表示瀏覽器的一個實例。在瀏覽器中,window 對象有雙重角色,它既是經過JavaScript 訪問瀏覽器窗口的一個接口,又是ECMAScript 規定的Global 對象。這意味着在網頁中定義的任何一個對象、變量和函數,都以window 做爲其Global 對象,所以有權訪問parseInt()等方法。java

8.1.1 全局做用域數組

因爲window 對象同時扮演着ECMAScript 中Global 對象的角色,所以全部在全局做用域中聲明的變量、函數都會變成window 對象的屬性和方法。瀏覽器

var age=29;
function sayAge(){
    console.log(this.age);
}
console.log(window.age);//29
sayAge();//29
window.sayAge();//29

咱們在全局做用域中定義了一個變量age 和一個函數sayAge(),它們被自動歸在了window 對象名下。因而,能夠經過window.age 訪問變量age,能夠經過window.sayAge()訪問函數sayAge()。因爲sayAge()存在於全局做用域中,所以this.age 被映射到window.age,最終顯示的仍然是正確的結果。緩存

拋開全局變量會成爲window 對象的屬性不談,定義全局變量與在window 對象上直接定義屬性仍是有一點差異:全局變量不能經過delete 操做符刪除,而直接在window 對象上的定義的屬性能夠。安全

var age = 29;
window.color = "red";

//在IE < 9 時拋出錯誤,在其餘全部瀏覽器中都返回false
delete window.age;

//在IE < 9 時拋出錯誤,在其餘全部瀏覽器中都返回true
delete window.color; //returns true

alert(window.age); //29
alert(window.color); //undefined

嘗試訪問未聲明的變量會拋出錯誤,可是經過查詢window 對象,能夠知道某個可能未聲明的變量是否存在。服務器

//這裏會拋出錯誤,由於oldValue 未定義
var newValue = oldValue;

//這裏不會拋出錯誤,由於這是一次屬性查詢
//newValue 的值是undefined
var newValue = window.oldValue;

8.1.2 窗口關係及框架cookie

若是頁面中包含框架,則每一個框架都擁有本身的window 對象,而且保存在frames 集合中。在frames集合中,能夠經過數值索引(從0 開始,從左至右,從上到下)或者框架名稱來訪問相應的window 對象。每一個window 對象都有一個name 屬性,其中包含框架的名稱。下面是一個包含框架的頁面:網絡

<html>
    <head>
        <title>Frameset Example</title>
    </head>
    <frameset rows="160,*">
        <frame src="frame.htm" name="topFrame">
        <frameset cols="50%,50%">
            <frame src="anotherframe.htm" name="leftFrame">
            <frame src="yetanotherframe.htm" name="rightFrame">
        </frameset>
    </frameset>
</html>

top 對象始終指向最高(最外)層的框架,也就是瀏覽器窗口。使用它能夠確保在一個框架中正確地訪問另外一個框架。由於對於在一個框架中編寫的任何代碼來講,其中的window 對象指向的都是那個框架的特定實例,而非最高層的框架。

與top 相對的另外一個window 對象是parent。顧名思義,parent(父)對象始終指向當前框架的直接上層框架。在某些狀況下,parent 有可能等於top;但在沒有框架的狀況下,parent 必定等於top(此時它們都等於window)。 

與框架有關的最後一個對象是self,它始終指向window;實際上,self 和window 對象能夠互換使用。引入self 對象的目的只是爲了與top 和parent 對象對應起來,所以它不格外包含其餘值。

8.1.3 窗口位置

用來肯定和修改window 對象位置的屬性和方法有不少。

IE、Safari、Opera 和Chrome 都提供了screenLeft 和screenTop 屬性,分別用於表示窗口相對於屏幕左邊和上邊的位置。

Firefox 則在screenX 和screenY 屬性中提供相同的窗口位置信息,Safari 和Chrome 也同時支持這兩個屬性。

Opera雖然也支持screenX 和screenY 屬性,但與screenLeft 和screenTop 屬性並不對應,所以建議你們不要在Opera 中使用它們。

使用下列代碼能夠跨瀏覽器取得窗口左邊和上邊的位置。 

var leftPos = (typeof window.screenLeft == "number") ? window.screenLeft : window.screenX;
var topPos = (typeof window.screenTop == "number") ? window.screenTop : window.screenY;

這個例子運用二元操做符首先肯定screenLeft 和screenTop 屬性是否存在,若是是(在IE、Safari、Opera 和Chrome 中),則取得這兩個屬性的值。若是不存在(在Firefox 中),則取得screenX和screenY 的值。 

在IE、Opera 中,screenLeft 和screenTop 中保存的是從屏幕左邊和上邊到由window 對象表示的頁面可見區域的距離。換句話說,若是window 對象是最外層對象,並且瀏覽器窗口緊貼屏幕最上端——即y 軸座標爲0,那麼screenTop 的值就是位於頁面可見區域上方的瀏覽器工具欄的像素高度。可是,在Chrome、Firefox 和Safari 中,screenY 或screenTop中保存的是整個瀏覽器窗口相對於屏幕的座標值,即在窗口的y 軸座標爲0 時返回0。

使用moveTo()和moveBy()方法卻是有可能將窗口精確地移動到一個新位置。這兩個方法都接收兩個參數,其中moveTo()接收的是新位置的x 和y 座標值,而moveBy()接收的是在水平和垂直方向上移動的像素數。

//將窗口移動到屏幕左上角
window.moveTo(0,0);

//將窗向下移動100 像素
window.moveBy(0,100);

//將窗口移動到(200,300)
window.moveTo(200,300);

//將窗口向左移動50 像素
window.moveBy(-50,0);

8.1.4 窗口大小

IE9+、Firefox、Safari、Opera 和Chrome 均爲此提供了4 個屬性:innerWidth、innerHeight、outerWidth 和outerHeight。

在IE9+、Safari 和Firefox中,outerWidth 和outerHeight 返回瀏覽器窗口自己的尺寸(不管是從最外層的window 對象仍是從
某個框架訪問)。在Opera 中,這兩個屬性的值表示頁面視圖容器的大小。而innerWidth 和innerHeight則表示該容器中頁面視圖區的大小(減去邊框寬度)。

在Chrome 中,outerWidth、outerHeight 與innerWidth、innerHeight 返回相同的值,即視口(viewport)大小而非瀏覽器窗口大小。

IE8 及更早版本沒有提供取得當前瀏覽器窗口尺寸的屬性;不過,它經過DOM 提供了頁面可見區域的相關信息。

在IE、Firefox、Safari、Opera 和Chrome 中,document.documentElement.clientWidth 和document.documentElement.clientHeight 中保存了頁面視口的信息。在IE6 中,這些屬性必須在標準模式下才有效;若是是混雜模式,就必須經過document.body.clientWidth 和document.body.clientHeight 取得相同信息。而對於混雜模式下的Chrome,則不管經過document.documentElement仍是document.body 中的clientWidth 和clientHeight 屬性,均可以取得視口的大小。

雖然最終沒法肯定瀏覽器窗口自己的大小,但卻能夠取得頁面視口的大小,以下

var pageWidth = window.innerWidth,
    pageHeight = window.innerHeight;
if (typeof pageWidth != "number"){
    if (document.compatMode == "CSS1Compat"){
        pageWidth = document.documentElement.clientWidth;
        pageHeight = document.documentElement.clientHeight;
    } else {
        pageWidth = document.body.clientWidth;
        pageHeight = document.body.clientHeight;
    }
}

對於移動設備,window.innerWidth 和window.innerHeight 保存着可見視口,也就是屏幕上可見頁面區域的大小。移動IE 瀏覽器不支持這些屬性,但經過document.documentElement.client-Width 和document.documentElement.clientHeihgt 提供了相同的信息。隨着頁面的縮放,這些值也會相應變化。

使用resizeTo()和resizeBy()方法能夠調整瀏覽器窗口的大小。這兩個方法都接收兩個參數,其中resizeTo()接收瀏覽器窗口的新寬度和新高度,而resizeBy()接收新窗口與原窗口的寬度和高度之差。

//調整到100×100
window.resizeTo(100, 100);

//調整到200×150
window.resizeBy(100, 50);

//調整到 300×300
window.resizeTo(300, 300);

8.1.5 導航和打開窗口

使用window.open()方法既能夠導航到一個特定的URL,也能夠打開一個新的瀏覽器窗口。這個方法能夠接收4 個參數:要加載的URL、窗口目標、一個特性字符串以及一個表示新頁面是否取代瀏覽器歷史記錄中當前加載頁面的布爾值。一般只須傳遞第一個參數,最後一個參數只在不打開新窗口的狀況下使用。

若是爲window.open()傳遞了第二個參數,並且該參數是已有窗口或框架的名稱,那麼就會在具備該名稱的窗口或框架中加載第一個參數指定的URL。

//等同於< a href="http://www.wrox.com" target="topFrame"></a>
window.open("http://www.wrox.com/", "topFrame");

調用這行代碼,就如同用戶單擊了href 屬性爲http://www.wrox.com/,target 屬性爲"topFrame"的連接。若是有一個名叫"topFrame"的窗口或者框架,就會在該窗口或框架加載這個URL;不然,就會建立一個新窗口並將其命名爲"topFrame"。此外,第二個參數也能夠是下列任何一個特殊的窗口名稱:_self、_parent、_top 或_blank。

一、彈出窗口

若是給window.open()傳遞的第二個參數並非一個已經存在的窗口或框架,那麼該方法就會根據在第三個參數位置上傳入的字符串建立一個新窗口或新標籤頁。若是沒有傳入第三個參數,那麼就會打開一個帶有所有默認設置(工具欄、地址欄和狀態欄等)的新瀏覽器窗口(或者打開一個新標籤頁——根據瀏覽器設置)。在不打開新窗口的狀況下,會忽略第三個參數。
第三個參數是一個逗號分隔的設置字符串,表示在新窗口中都顯示哪些特性。下表列出了能夠出如今這個字符串中的設置選項。

設置 說明
fullscreen yes或no 表示瀏覽器窗口是否最大化。僅限IE
height 數值 表示新窗口的高度。不能小於100
left 數值 表示新窗口的左座標。不能是負值
location yes或no

表示是否在瀏覽器窗口中顯示地址欄。不一樣瀏覽器的默認值不一樣。若是設置爲no,地址欄可能

會隱藏,也可能會被禁用(取決於瀏覽器)

menubar yes或no 表示是否在瀏覽器窗口中顯示菜單欄。默認值爲no
resizable yes或no 表示是否能夠經過拖動瀏覽器窗口的邊框改變其大小。默認值爲no
scrollbars yes或no 表示若是內容在視口中顯示不下,是否容許滾動。默認值爲no
status yes或no 表示是否在瀏覽器窗口中顯示狀態欄。默認值爲no
toolbar yes或no 表示是否在瀏覽器窗口中顯示工具欄。默認值爲no
top 數值 表示新窗口的上座標。不能是負值
width 數值 表示新窗口的寬度。不能小於100

表中所列的部分或所有設置選項,均可以經過逗號分隔的名值對列表來指定。其中,名值對以等號表示

window.open("http://www.wrox.com/","wroxWindow","height=400,width=400,top=10,left=10,resizable=yes");

window.open()方法會返回一個指向新窗口的引用。引用的對象與其餘window 對象大體類似,但咱們能夠對其進行更多控制。例如,有些瀏覽器在默認狀況下可能不容許咱們針對主瀏覽器窗口調整大小或移動位置,但卻容許咱們針對經過window.open()建立的窗口調整大小或移動位置。經過這個返回的對象,能夠像操做其餘窗口同樣操做新打開的窗口,以下所示。

var wroxWin = window.open("http://www.wrox.com/","wroxWindow","height=400,width=400,top=10,left=10,resizable=yes");
//調整大小 wroxWin.resizeTo(500,500);
//移動位置 wroxWin.moveTo(100,100);

調用close()方法還能夠關閉新打開的窗口。

wroxWin.close();

對於瀏覽器的主窗口,若是沒有獲得用戶的容許是不能關閉它的。不過,彈出窗口卻是能夠調用top.close()在不經用戶容許的狀況下關閉本身。彈出窗口關閉以後,窗口的引用仍然還在,但除了像下面這樣檢測其closed 屬性以外,已經沒有其餘用處了。

wroxWin.close();
alert(wroxWin.closed); //true

新建立的window 對象有一個opener 屬性,其中保存着打開它的原始窗口對象。這個屬性只在彈出窗口中的最外層window 對象(top)中有定義,並且指向調用window.open()的窗口或框架。例如:

var wroxWin = window.open("http://www.wrox.com/","wroxWindow","height=400,width=400,top=10,left=10,resizable=yes");
alert(wroxWin.opener == window); //true

在Chrome中,將新建立的標籤頁的opener 屬性設置爲null,即表示在單獨的進程中運行新標籤頁

var wroxWin = window.open("http://www.wrox.com/","wroxWindow","height=400,width=400,top=10,left=10,resizable=yes");
wroxWin.opener = null;

將opener 屬性設置爲null 就是告訴瀏覽器新建立的標籤頁不須要與打開它的標籤頁通訊,所以能夠在獨立的進程中運行。標籤頁之間的聯繫一旦切斷,將沒有辦法恢復。

2. 安全限制

Windows XP SP2 中的IE6 對彈出窗口施加了多方面的安全限制,包括不容許在屏幕以外建立彈出窗口、不容許將彈出窗口移動到屏幕之外、不容許關閉狀態欄等。IE7 則增長了更多的安全限制,如不容許關閉地址欄、默認狀況下不容許移動彈出窗口或調整其大小。Firefox 1 從一開始就不支持修改狀態欄,所以不管給window.open()傳入什麼樣的特性字符串,彈出窗口中都會無一例外地顯示狀態欄。後來的Firefox 3 又強制始終在彈出窗口中顯示地址欄。Opera 只會在主瀏覽器窗口中打開彈出窗口,但不容許它們出如今可能與系統對話框混淆的地方。

此外,有的瀏覽器只根據用戶操做來建立彈出窗口。這樣一來,在頁面還沒有加載完成時調用window.open()的語句根本不會執行,並且還可能會將錯誤消息顯示給用戶。換句話說,只能經過單擊或者擊鍵來打開彈出窗口。

對於那些不是用戶有意打開的彈出窗口,Chrome 採起了不一樣的處理方式。它不會像其餘瀏覽器那樣簡單地屏蔽這些彈出窗口,而是隻顯示它們的標題欄,並把它們放在瀏覽器窗口的右下角。

3. 彈出窗口屏蔽程序

大多數瀏覽器都內置有彈出窗口屏蔽程序,而沒有內置此類程序的瀏覽器,也能夠安裝Yahoo!Toolbar 等帶有內置屏蔽程序的實用工具。結果就是用戶能夠將絕大多數不想看到彈出窗口屏蔽掉。因而,在彈出窗口被屏蔽時,就應該考慮兩種可能性。若是是瀏覽器內置的屏蔽程序阻止的彈出窗口,那麼window.open()極可能會返回null。此時,只要檢測這個返回的值就能夠肯定彈出窗口是否被屏蔽了,以下面的例子所示。

var wroxWin = window.open("http://www.wrox.com", "_blank");
if (wroxWin == null){
    alert("The popup was blocked!");
}

若是是瀏覽器擴展或其餘程序阻止的彈出窗口,那麼window.open()一般會拋出一個錯誤。所以,要想準確地檢測出彈出窗口是否被屏蔽,必須在檢測返回值的同時,將對window.open()的調用封裝在一個try-catch 塊中,以下所示。

var blocked = false;
try {
    var wroxWin = window.open("http://www.wrox.com", "_blank");
    if (wroxWin == null){
        blocked = true;
    }
} catch (ex){
    blocked = true;
}
if (blocked){
    alert("The popup was blocked!");
}

在任何狀況下,以上代碼均可以檢測出調用window.open()打開的彈出窗口是否是被屏蔽了。但要注意的是,檢測彈出窗口是否被屏蔽只是一方面,它並不會阻止瀏覽器顯示與被屏蔽的彈出窗口有關的消息。

8.1.6 間歇調用和超時調用

JavaScript 是單線程語言,但它容許經過設置超時值和間歇時間值來調度代碼在特定的時刻執行。前者是在指定的時間事後執行代碼,然後者則是每隔指定的時間就執行一次代碼。

超時調用須要使用window 對象的setTimeout()方法,它接受兩個參數:要執行的代碼和以毫秒錶示的時間(即在執行代碼前須要等待多少毫秒)。其中,第一個參數能夠是一個包含JavaScript 代碼的字符串(就和在eval()函數中使用的字符串同樣),也能夠是一個函數。

例如,下面對setTimeout()的兩次調用都會在一秒鐘後顯示一個警告框。

//不建議傳遞字符串!
setTimeout("alert('Hello world!') ", 1000);

//推薦的調用方式
setTimeout(function() {
    alert("Hello world!");
}, 1000);

雖然這兩種調用方式都沒有問題,但因爲傳遞字符串可能致使性能損失,所以不建議以字符串做爲第一個參數。

第二個參數是一個表示等待多長時間的毫秒數,但通過該時間後指定的代碼不必定會執行。JavaScript 是一個單線程序的解釋器,所以必定時間內只能執行一段代碼。爲了控制要執行的代碼,就有一個JavaScript 任務隊列。這些任務會按照將它們添加到隊列的順序執行。setTimeout()的第二個參數告訴JavaScript 再過多長時間把當前任務添加到隊列中。若是隊列是空的,那麼添加的代碼會當即執行;若是隊列不是空的,那麼它就要等前面的代碼執行完了之後再執行。

調用setTimeout()以後,該方法會返回一個數值ID,表示超時調用。這個超時調用ID 是計劃執行代碼的惟一標識符,能夠經過它來取消超時調用。要取消還沒有執行的超時調用計劃,能夠調用clearTimeout()方法並將相應的超時調用ID 做爲參數傳遞給它,以下所示。

//設置超時調用
var timeoutId = setTimeout(function() {
    alert("Hello world!");
}, 1000);

//注意:把它取消
clearTimeout(timeoutId);

只要是在指定的時間還沒有過去以前調用clearTimeout(),就能夠徹底取消超時調用。前面的代碼在設置超時調用以後立刻又調用了clearTimeout(),結果就跟什麼也沒有發生同樣。

超時調用的代碼都是在全局做用域中執行的,所以函數中this 的值在非嚴格模式下指向window 對象,在嚴格模式下是undefined。

間歇調用與超時調用相似,只不過它會按照指定的時間間隔重複執行代碼,直至間歇調用被取消或者頁面被卸載。設置間歇調用的方法是setInterval(),它接受的參數與setTimeout()相同:要執行的代碼(字符串或函數)和每次執行以前須要等待的毫秒數。

//不建議傳遞字符串!
setInterval ("alert('Hello world!') ", 10000);

//推薦的調用方式
setInterval (function() {
    alert("Hello world!");
}, 10000);

調用setInterval()方法一樣也會返回一個間歇調用ID,該ID 可用於在未來某個時刻取消間歇調用。要取消還沒有執行的間歇調用,可使用clearInterval()方法並傳入相應的間歇調用ID。取消間歇調用的重要性要遠遠高於取消超時調用,由於在不加干涉的狀況下,間歇調用將會一直執行到頁面卸載。

var num = 0;
var max = 10;
var intervalId = null;

function incrementNumber() {
    num++;
    //若是執行次數達到了max 設定的值,則取消後續還沒有執行的調用
    if (num == max) {
      clearInterval(intervalId);
      alert("Done");
    }
}

intervalId = setInterval(incrementNumber, 500);

 

在這個例子中,變量num 每半秒鐘遞增一次,當遞增到最大值時就會取消先前設定的間歇調用。這個模式也可使用超時調用來實現

var num = 0;
var max = 10;
function incrementNumber() {
    num++;
    //若是執行次數未達到max 設定的值,則設置另外一次超時調用
    if (num < max) {
        setTimeout(incrementNumber, 500);
    } else {
        alert("Done");
    }
}
setTimeout(incrementNumber, 500);

可見,在使用超時調用時,沒有必要跟蹤超時調用ID,由於每次執行代碼以後,若是再也不設置另外一次超時調用,調用就會自行中止。通常認爲,使用超時調用來模擬間歇調用的是一種最佳模式。

8.1.7 系統對話框

瀏覽器經過alert()、confirm()和prompt()方法能夠調用系統對話框向用戶顯示消息。系統對話框與在瀏覽器中顯示的網頁沒有關係,也不包含HTML。它們的外觀由操做系統及(或)瀏覽器設置決定,而不是由CSS 決定。此外,經過這幾個方法打開的對話框都是同步和模態的。也就是說,顯示這些對話框的時候代碼會中止執行,而關掉這些對話框後代碼又會恢復執行。

alert()方法,這個方法接受一個字符串並將其顯示給用戶。具體來講,調用alert()方法的結果就是向用戶顯示一個系統對話框,其中包含指定的文本和一個OK(「肯定」)按鈕。

alert("Hello world!");

第二種對話框是調用confirm()方法生成的。從向用戶顯示消息的方面來看,這種「確認」對話框很像是一個「警告」對話框。但兩者的主要區別在於「確認」對話框除了顯示OK 按鈕外,還會顯示一個Cancel(「取消」)按鈕,兩個按鈕可讓用戶決定是否執行給定的操做。

confirm("Are yous ure?");

爲了肯定用戶是單擊了OK 仍是Cancel,能夠檢查confirm()方法返回的布爾值:true 表示單擊了OK,false 表示單擊了Cancel 或單擊了右上角的X 按鈕。確認對話框的典型用法以下。

if (confirm("Are you sure?")) {
    alert("I'm so glad you're sure! ");
} else {
    alert("I'm sorry to hear you're not sure. ");
}

最後一種對話框是經過調用prompt()方法生成的,這是一個「提示」框,用於提示用戶輸入一些文本。提示框中除了顯示OK 和Cancel 按鈕以外,還會顯示一個文本輸入域,以供用戶在其中輸入內容。prompt()方法接受兩個參數:要顯示給用戶的文本提示和文本輸入域的默認值(能夠是一個空字符串)。

prompt("What's your name?","Michael");

若是用戶單擊了OK 按鈕,則prompt()返回文本輸入域的值;若是用戶單擊了Cancel 或沒有單擊OK 而是經過其餘方式關閉了對話框,則該方法返回null。

var result = prompt("What is your name? ", "");
if (result !== null) {
    alert("Welcome, " + result);
}

還有兩個能夠經過JavaScript 打開的對話框,即「查找」和「打印」。這兩個對話框都是異步顯示的,可以將控制權當即交還給腳本。這兩個對話框與用戶經過瀏覽器菜單的「查找」和「打印」命令打開的對話框相同。而在JavaScript 中則能夠像下面這樣經過window 對象的find()和print()方法打開它們。

//顯示「打印」對話框
window.print();

//顯示「查找」對話框
window.find();

8.2 location 對象

location 是最有用的BOM對象之一,它提供了與當前窗口中加載的文檔有關的信息,還提供了一些導航功能。事實上,location 對象是很特別的一個對象,由於它既是window 對象的屬性,也是document 對象的屬性;換句話說,window.location 和document.location 引用的是同一個對象。location 對象的用處不僅表如今它保存着當前文檔的信息,還表如今它將URL 解析爲獨立的片斷,讓開發人員能夠經過不一樣的屬性訪問這些片斷。下表列出了location 對象的全部屬性。

屬性名 例子 說明
hash "#contents" 返回URL中的hash(#號後跟零或多個字符),若是URL中不包含散列,則返回空字符串
host "www.360.com:80" 返回服務器名稱和端口號(若是有)
hostname "www.360.com" 返回不帶端口號的服務器名稱
href "http://www.360.com" 返回當前加載頁面的完整URL。而location對象的toString()方法也返回這個值
pathname "/wileyCDA/" 返回URL中的目錄和(或文件名)
port "8080" 返回URL中指定的端口號。若是URL中不包含端口號,則這個屬性返回空字符串
protocol "http" 返回頁面使用的協議。一般是http:或https:
search "?q=javascript" 返回URL的查詢字符串。這個字符串以問號開頭

8.2.1 查詢字符串參數

雖然經過上面的屬性能夠訪問到location 對象的大多數信息,但其中訪問URL 包含的查詢字符串的屬性並不方便。儘管location.search 返回從問號到URL 末尾的全部內容,但卻沒有辦法逐個訪問其中的每一個查詢字符串參數。爲此,能夠像下面這樣建立一個函數,用以解析查詢字符串,而後返回包含全部參數的一個對象:

function getQueryStringArgs(){

    //取得查詢字符串並去掉開頭的問號
    var qs = (location.search.length > 0 ? location.search.substring(1) : ""),
    
    //保存數據的對象
    args = {},

    //取得每一項
    items = qs.length ? qs.split("&") : [],
    item = null,
    name = null,
    value = null,

    //在for 循環中使用
    i = 0,
    len = items.length;

    //逐個將每一項添加到args 對象中
    for (i=0; i < len; i++){
        item = items[i].split("=");
        name = decodeURIComponent(item[0]);
        value = decodeURIComponent(item[1]);
        if (name.length) {
            args[name] = value;
        }
    }
    return args;
}

下面給出了使用這個函數的示例。

//假設查詢字符串是?q=javascript&num=10
var args = getQueryStringArgs();
alert(args["q"]); //"javascript"
alert(args["num"]); //"10"

可見,每一個查詢字符串參數都成了返回對象的屬性。這樣就極大地方便了對每一個參數的訪問。

8.2.2 位置操做

使用location 對象能夠經過不少方式來改變瀏覽器的位置。首先,也是最經常使用的方式,就是使用assign()方法併爲其傳遞一個URL,以下所示。

location.assign("http://www.wrox.com");

這樣,就能夠當即打開新URL 並在瀏覽器的歷史記錄中生成一條記錄。若是是將location.href或window.location 設置爲一個URL 值,也會以該值調用assign()方法。例如,下列兩行代碼與顯式調用assign()方法的效果徹底同樣。

window.location = "http://www.wrox.com";
location.href = "http://www.wrox.com";

在這些改變瀏覽器位置的方法中,最經常使用的是設置location.href 屬性。

另外,修改location 對象的其餘屬性也能夠改變當前加載的頁面。下面的例子展現了經過將hash、search、hostname、pathname 和port 屬性設置爲新值來改變URL。

//假設初始URL 爲http://www.wrox.com/WileyCDA/

//將URL 修改成"http://www.wrox.com/WileyCDA/#section1"
location.hash = "#section1";

//將URL 修改成"http://www.wrox.com/WileyCDA/?q=javascript"
location.search = "?q=javascript";

//將URL 修改成"http://www.yahoo.com/WileyCDA/"
location.hostname = "www.yahoo.com";

//將URL 修改成"http://www.yahoo.com/mydir/"
location.pathname = "mydir";

//將URL 修改成"http://www.yahoo.com:8080/WileyCDA/"
location.port = 8080;

每次修改location 的屬性(hash 除外),頁面都會以新URL 從新加載。

當經過上述任何一種方式修改URL 以後,瀏覽器的歷史記錄中就會生成一條新記錄,所以用戶經過單擊「後退」按鈕都會導航到前一個頁面。要禁用這種行爲,可使用replace()方法。這個方法只接受一個參數,即要導航到的URL;結果雖然會致使瀏覽器位置改變,但不會在歷史記錄中生成新記錄。在調用replace()方法以後,用戶不能回到前一個頁面,來看下面的例子:

<!DOCTYPE html>
<html>
    <head>
        <title>You won't be able to get back here</title>
    </head>
    <body>
        <p>Enjoy this page for a second, because you won't be coming back here.</p>
        <script type="text/javascript">
          setTimeout(function () {
              location.replace("http://www.wrox.com/");
          }, 1000);
        </script>
    </body>
</html>

若是將這個頁面加載到瀏覽器中,瀏覽器就會在1 秒鐘後從新定向到www.wrox.com。而後,「後退」按鈕將處於禁用狀態,若是不從新輸入完整的URL,則沒法返回示例頁面。

與位置有關的最後一個方法是reload(),做用是從新加載當前顯示的頁面。若是調用reload()時不傳遞任何參數,頁面就會以最有效的方式從新加載。也就是說,若是頁面自上次請求以來並無改變過,頁面就會從瀏覽器緩存中從新加載。若是要強制從服務器從新加載,則須要像下面這樣爲該方法傳遞參數true。

location.reload(); //從新加載(有可能從緩存中加載)
location.reload(true); //從新加載(從服務器從新加載)

位於reload()調用以後的代碼可能會也可能不會執行,這要取決於網絡延遲或系統資源等因素。爲此,最好將reload()放在代碼的最後一行。

8.3 navigatior 對象

與其餘BOM 對象的狀況同樣,每一個瀏覽器中的navigator 對象也都有一套本身的屬性。下表列出了存在於全部瀏覽器中的屬性和方法,以及支持它們的瀏覽器版本。

屬性或方法 說明 IE Firefox

Safari/Chrome

Opera
appCodeName 瀏覽器的名稱。一般都是Mozilla,即
使在非Mozilla瀏覽器中也是如此
3.0+ 1.0+ 1.0+ 7.0+
appMinorVersion 次版本信息 4.0+ - - 9.5+
appName 完整的瀏覽器名稱  3.0+ 1.0+ 1.0+ 7.0+
appVersion  瀏覽器的版本。通常不與實際的瀏覽器
版本對應
3.0+   1.0+ 1.0+ 7.0+
buildID 瀏覽器編譯版本 - 2.0+ - -
cookieEnabled 表示cookie是否啓用 4.0+ 1.0+ 1.0+ 7.0+
cpuClass  客戶端計算機中使用的CPU類型(x8六、
68K、Alpha、PPC或Other)
4.0+ - - -
javaEnabled() 表示當前瀏覽器中是否啓用了Java 4.0+  1.0+ 1.0+ 7.0+
language 瀏覽器的主語言 - 1.0+ 1.0+ 7.0+
mimeTypes 在瀏覽器中註冊的MIME類型數組 4.0+  1.0+  1.0+ 7.0+
onLine 表示瀏覽器是否鏈接到了因特網 4.0+ 1.0+ - 9.5+
opsProfile  彷佛早就不用了。查不到相關文檔 4.0+ - - -
oscpu  客戶端計算機的操做系統或使用的CPU - 1.0+ - -
platform  瀏覽器所在的系統平臺 4.0+ 1.0+ 1.0+  7.0+
plugins 瀏覽器中安裝的插件信息的數組 4.0+ 1.0+ 1.0+ 7.0+
preference()  設置用戶的首選項 - 1.5+ - -
product  產品名稱(如 Gecko) -  1.0+  1.0+ -
productSub 關於產品的次要信息(如Gecko的版本) - 1.0+ 1.0+ -
register-ContentHandler() 針對特定的MIME類型將一個站點註冊
爲處理程序
- 2.0+ - -
register-ProtocolHandler() 針對特定的協議將一個站點註冊爲處
理程序
- 2.0+ - -
securityPolicy  已經廢棄。安全策略的名稱。爲了與
Netscape Navigator 4向後兼容而保留下來
- 1.0+ - -
systemLanguage 操做系統的語言 4.0+ - - -
taintEnabled()

已經廢棄。表示是否容許變量被修改(taint)。爲了
與Netscape Navigator 3向後兼容而保留下來

4.0+ 1.0+ - 7.0+
userAgent  瀏覽器的用戶代理字符串 3.0+ 1.0+ 1.0+  7.0+
userLanguage 操做系統的默認語言 4.0+ - - 7.0+
userProfile 藉以訪問用戶我的信息的對象 4.0+ - - -
vendor 瀏覽器的品牌 - 1.0+ 1.0+ -
vendorSub 有關供應商的次要信息 - 1.0+ 1.0+ -

表中的這些navigator 對象的屬性一般用於檢測顯示網頁的瀏覽器類型

8.3.1 檢測插件

檢測瀏覽器中是否安裝了特定的插件是一種最多見的檢測例程。對於非IE 瀏覽器,可使用plugins 數組來達到這個目的。該數組中的每一項都包含下列屬性。

  • name:插件的名字。
  • description:插件的描述。
  • filename:插件的文件名。
  • length:插件所處理的MIME 類型數量。

通常來講,name 屬性中會包含檢測插件必需的全部信息,但有時候也不徹底如此。在檢測插件時,須要像下面這樣循環迭代每一個插件並將插件的name 與給定的名字進行比較。

//檢測插件(在IE 中無效)
function hasPlugin(name){
    name = name.toLowerCase();
        for (var i=0; i < navigator.plugins.length; i++){
            if (navigator. plugins [i].name.toLowerCase().indexOf(name) > -1){
              return true;
        }
    }
    return false;
}

//檢測Flash
alert(hasPlugin("Flash"));
//檢測QuickTime
alert(hasPlugin("QuickTime"));

這個hasPlugin()函數接受一個參數:要檢測的插件名。第一步是將傳入的名稱轉換爲小寫形式,以便於比較。而後,迭代plugins 數組,經過indexOf()檢測每一個name 屬性,以肯定傳入的名稱是否出如今字符串的某個地方。比較的字符串都使用小寫形式能夠避免因大小寫不一致致使的錯誤。而傳入的參數應該儘量具體,以免混淆。應該說,像Flash 和QuickTime 這樣的字符串就比較具體了,不容易致使混淆。

檢測IE 中的插件比較麻煩,由於IE 不支持Netscape 式的插件。在IE 中檢測插件的惟一方式就是使用專有的ActiveXObject 類型,並嘗試建立一個特定插件的實例。IE 是以COM對象的方式實現插件的,而COM對象使用惟一標識符來標識。所以,要想檢查特定的插件,就必須知道其COM標識符。

//檢測IE 中的插件
function hasIEPlugin(name){
    try {
        new ActiveXObject(name);
        return true;
    } catch (ex){
        return false;
    }
}
//檢測Flash
alert(hasIEPlugin("ShockwaveFlash.ShockwaveFlash"));
//檢測QuickTime
alert(hasIEPlugin("QuickTime.QuickTime"));

在這個例子中,函數hasIEPlugin()只接收一個COM 標識符做爲參數

鑑於檢測這兩種插件的方法差異太大,所以典型的作法是針對每一個插件分別建立檢測函數,而不是使用前面介紹的通用檢測方法。

//檢測全部瀏覽器中的Flash
function hasFlash(){
    var result = hasPlugin("Flash");
    if (!result){
        result = hasIEPlugin("ShockwaveFlash.ShockwaveFlash");
    }
    return result;
}

//檢測全部瀏覽器中的QuickTime
function hasQuickTime(){
    var result = hasPlugin("QuickTime");
    if (!result){
        result = hasIEPlugin("QuickTime.QuickTime");
    }
    return result;
}

//檢測Flash
alert(hasFlash());

//檢測QuickTime
alert(hasQuickTime());

上面代碼中定義了兩個函數:hasFlash()和hasQuickTime()。每一個函數都是先嚐試使用不針對IE 的插件檢測方法。若是返回了false(在IE 中會這樣),那麼再使用針對IE 的插件檢測方法。若是IE 的插件檢測方法再返回false,則整個方法也將返回false。只要任何一次檢測返回true,整個方法都會返回true。

8.3.2 註冊處理程序

Firefox 2 爲navigator 對象新增了registerContentHandler()和registerProtocolHandler()方法。這兩個方法可讓一個站點指明它能夠處理特定類型的信息。隨着RSS 閱讀器和在線電子郵件程序的興起,註冊處理程序就爲像使用桌面應用程序同樣默認使用這些在線應用程序提供了一種方式。

其中,registerContentHandler()方法接收三個參數:要處理的MIME 類型、能夠處理該MIME類型的頁面的URL 以及應用程序的名稱。舉個例子,要將一個站點註冊爲處理RSS 源的處理程序,可使用以下代碼。

navigator.registerContentHandler("application/rss+xml","http://www.somereader.com?feed=%s", "Some Reader");

第一個參數是RSS 源的MIME 類型。第二個參數是應該接收RSS 源URL 的URL,其中的%s 表示RSS 源URL,由瀏覽器自動插入。當下一次請求RSS 源時,瀏覽器就會打開指定的URL,而相應的Web 應用程序將以適當方式來處理該請求。

相似的調用方式也適用於registerProtocolHandler()方法,它也接收三個參數:要處理的協議(例如,mailto 或ftp)、處理該協議的頁面的URL 和應用程序的名稱。例如,要想將一個應用程序註冊爲默認的郵件客戶端,可使用以下代碼。

navigator.registerProtocolHandler("mailto","http://www.somemailclient.com?cmd=%s", "Some Mail Client");

這個例子註冊了一個mailto 協議的處理程序,該程序指向一個基於Web 的電子郵件客戶端。一樣,第二個參數仍然是處理相應請求的URL,而%s 則表示原始的請求。

8.4 screen對象

screen 對象基本上只用來代表客戶端的能力,其中包括瀏覽器窗口外部的顯示器的信息,如像素寬度和高度等。每一個瀏覽器中的screen 對象都包含着各不相同的屬性,下表列出了全部屬性及支持相應屬性的瀏覽器。

這些信息常常集中出如今測定客戶端能力的站點跟蹤工具中,但一般不會用於影響功能。不過,有時候也可能會用到其中的信息來調整瀏覽器窗口大小,使其佔據屏幕的可用空間,例如: 

window.resizeTo(screen.availWidth, screen.availHeight);

8.5 history 對象

 history 對象保存着用戶上網的歷史記錄,從窗口被打開的那一刻算起。由於history 是window對象的屬性,所以每一個瀏覽器窗口、每一個標籤頁乃至每一個框架,都有本身的history 對象與特定的window 對象關聯。出於安全方面的考慮,開發人員沒法得知用戶瀏覽過的URL。不過,藉由用戶訪問過的頁面列表,一樣能夠在不知道實際URL 的狀況下實現後退和前進。 

使用go()方法能夠在用戶的歷史記錄中任意跳轉,能夠向後也能夠向前。這個方法接受一個參數,表示向後或向前跳轉的頁面數的一個整數值。負數表示向後跳轉(相似於單擊瀏覽器的「後退」按鈕),正數表示向前跳轉(相似於單擊瀏覽器的「前進」按鈕)。來看下面的例子。 

//後退一頁
history.go(-1);

//前進一頁
history.go(1);

//前進兩頁
history.go(2);

也能夠給go()方法傳遞一個字符串參數,此時瀏覽器會跳轉到歷史記錄中包含該字符串的第一個位置——可能後退,也可能前進,具體要看哪一個位置最近。若是歷史記錄中不包含該字符串,那麼這個方法什麼也不作,例如: 

//跳轉到最近的wrox.com 頁面
history.go("wrox.com");

//跳轉到最近的nczonline.net 頁面
history.go("nczonline.net");

另外,還可使用兩個簡寫方法back()和forward()來代替go()。顧名思義,這兩個方法能夠模仿瀏覽器的「後退」和「前進」按鈕。

//後退一頁
history.back();

//前進一頁
history.forward();

除了上述幾個方法外,history 對象還有一個length 屬性,保存着歷史記錄的數量。這個數量包括全部歷史記錄,即全部向後和向前的記錄。對於加載到窗口、標籤頁或框架中的第一個頁面而言,history.length 等於0。經過像下面這樣測試該屬性的值,能夠肯定用戶是否一開始就打開了你的頁面。 

if (history.length == 0){
    //這應該是用戶打開窗口後的第一個頁面
}
相關文章
相關標籤/搜索