??ECMAScript 是 JavaScript 的核心,但如果要在 Web 中使用 JavaScript,那么 BOM(瀏覽器對象模型)則無疑才是真正的核心。
??BOM 提供了很多對象,用于訪問瀏覽器的功能,這些功能與任何網頁內容無關。
??多年來,缺少事實上的規范導致 BOM 既有意思又有問題,因為瀏覽器提供商會按照各自的想法隨意去擴展它。
??于是,瀏覽器之間共有的對象就成為了事實上的標準。這些對象在瀏覽器中得以存在,很大程度上是由于它們提供了與瀏覽器的互操作性。
??W3C 為了把瀏覽器中 JavaScript 最基本的部分標準化,已經將 BOM 的主要方面納入了 HTML5 的規范中。
1、 window 對象
??BOM 的核心對象是 window,它表示瀏覽器的一個實例。在瀏覽器中,window 對象有雙重角色,它即是通過 JavaScript 訪問瀏覽器窗口的一個接口,又是 ECMAScript 規定的 Global 對象。
??這意味著在網頁中定義的任何一個對象、變量和函數,都以 window 作為其 Global 對象,因此有權訪問 parseInt() 等方法。
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
console.log(window.age); // 29
console.log(window.color); // undefined
??上述代碼中使用 var 語句添加的 window 屬性有一個名為 [[Configurable]] 的特性,這個特性的值被設置為 false,因此這樣定義的屬性不可以通過 delete 操作符刪除。IE8 及更早版本在遇到使用 delete 刪除 window 屬性的語句時,不管該屬性最初是如何創建的,都會拋出錯誤,以示警告。IE9 及更高版本不會拋出錯誤。
??還要記住一件事:嘗試訪問未聲明的變量會拋出錯誤,但是通過查詢 window 對象,可以知道某個可能未聲明的變量是否存在。示例:
// 這里會拋出錯誤,因為 oldValue 未定義
var newValue = oldValue;
// 這里不會拋出錯誤,因為這是一次屬性查詢
// newValue 的值是 undefined
var newValue = window.oldValue;
??很多全局 JavaScript 對象(如 location 和 navigator)實際上都是 window 對象的屬性。
??Windows Mobile 平臺的 IE 瀏覽器不允許通過 window.property = value 之類的形式,直接在 window 對象上創建新的屬性或方法。可是,在全局作用域聲明的所有變量和函數,照樣會變成 window 對象的成員。
1.2、窗口關系及框架
??如果頁面中包含框架,則每個框架都擁有自己的 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>
??以上代碼創建了一個框架集,其中一個框架居上,兩個框架居下。對上述例子而言,可以通過 window.frames[0] 或者 window.frames["topFrame"] 來引用上方的框架。不過,恐怕你最好使用 top 而非 window 來引用這些框架(例如,通過 top.frames[0])。
??我們知道,top 對象始終指向最高(最外)層的框架,也就是瀏覽器窗口。使用它可以確保在一個框架中正確的訪問另一個框架。因為對于在一個框架中編寫的任何代碼來說,其中的 window 對象指向的都是那個框架的特定實例,而非最高層的框架。
??下圖展示了在最高層窗口中,通過代碼來訪問前面例子中的每個框架的不同方式。
??與 top 相對的另一個 window 對象是 parent。顧名思義,parent(父)對象始終指向當前框架的直接上層框架。在某些情況下,parent 有可能等于 top;但在沒有框架的情況下,parent 一定等于 top(此時它們都等于 window)。示例:
<html>
<head>
<title>Frameset Example</title>
</head>
<frameset rows="100,*">
<frame src="frame.htm" name="topFrame">
<frameset cols="50%,50%">
<frame src="anotherframe.htm" name="leftFrame">
<frame src="anotherframeset.htm" name="rightFrame">
</frameset>
</frameset>
</html>
??這個框架集中的一個框架包含了另一個框架集,該框架集的代碼如下所示。
<html>
<head>
<title>Frameset Example</title>
</head>
<frameset cols="50%,50%">
<frame src="red.htm" name="redFrame">
<frame src="blue.htm" name="blueFrame">
</frameset>
</html>
??瀏覽器在加載完第一個框架集之后,會繼續將第二個框架集加載到 rightFrame 中。如果代碼位于 redFrame(或 blueFrame)中,那么 parent 對象指向的就是 rightFrame。可是,如果代碼位于 topFrame 中,則 parent 指向的是 top,因為 topFrame 的直接上層框架就是最外層框架。
??下圖展示了在將前面例子加載到瀏覽器后,不同 window 對象的值。
??注意,除非最高層窗口是通過 window.open() 打開的,否則其 window 對象的 name 屬性不會包含任何值。
??與框架有關的最后一個對象時 self,它始終指向 window;實際上,self 和 window 對象可以互換使用。引入 self 對象的目的只是為了與 top 和 parent 對象對應起來,因此它不格外包含其他值。
??所有這些對象都是 window 對象的屬性,可以通過 window.parent、window.top 等形式來訪問。同時,這也意味著可以將不同層次的 window 對象連綴起來,例如 window.parent.parent.frames[0]。
??在使用框架的情況下,瀏覽器中會存在多個 Global 對象。在每個框架中定義的全局變量會自動成為框架中 window 對象的屬性。
??由于每個 window 對象都包含原生類型的構造函數,因此每個框架都有一套自己的構造函數,這些構造函數一一對象,但并不相等。例如,top.Object 并不等于 top.frames[0].Object。這個問題會影響到對跨框架傳遞的對象使用 instanceof 操作符。
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。
??更讓人捉摸不透是,Firefox、Safari 和 Chrome 始終返回頁面中每個框架的 top.screenX 和 top.screenY 值。即使在頁面由于被設置了外邊距而發生偏移的情況下,相對于 window 對象使用 screenX 和 screenY 每次也都會返回相同的值。而 IE 和 Opera 則會給出框架相對于屏幕邊界的精確坐標值。
??最終結果,就是無法在跨瀏覽器的條件下取得窗口左邊和上邊的精確坐標值。然而,使用 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);
??需要注意的是,這兩個方法可能會被瀏覽器禁用;而且,在 Opera 和 IE 7(及更高版本)中默認就是禁用的。另外,這兩個方法都不適用于框架,只能對最外層的 window 對象使用。
1.4、窗口大小
??跨瀏覽器確定一個窗口的大小不是一件簡單的事。IE9+、Firefox、Safari、Opera 和 Chrome 均為此提供了 4 個屬性:innerWidth、innerHeight、outerWidth 和 outerHeight。
??在 IE9+、Safari 和 Firefox 中,outerWidth 和 outerHeight 返回瀏覽器窗口本身的尺寸(無論是從最外層的 window 對象還是從某個框架訪問)。在 Opera中,這兩個屬性的值表示頁面視圖容器的大小。(這里所謂的“頁面視圖容器”指的是 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 的值分別賦給了 pageWidth 和 pageHeight。然后檢查 pageWidth 中保存的是不是一個數值;如果不是,則通過檢查 document.compatMode 來確定頁面是否處于標準模式。如果是,則分別使用 document.documentElement.clientWidth 和 document.documentElement.clientHeight 的值。否則,就使用 document.body.clientWidth 和 document.body.clientHeight 的值。
??對于移動設備,window.innerWidth 和 window.innerHeight 保存著可見視口,也就是屏幕上可見頁面區域的大小。移動 IE 瀏覽器不支持這些屬性,但通過 document.documentElement.clientWidth 和document.documentElement.clientHeihgt 提供了相同的信息。隨著頁面的縮放,這些值也會相應變化。
??在其他移動瀏覽器中,document.documentElement 度量的是布局視口,即渲染后頁面的實際大小(與可見視口不同,可見視口只是整個頁面中的一小部分)。移動 IE 瀏覽器把布局視口的信息保存在 document.body.clientWidth 和 document.body.clientHeight 中。這些值不會隨著頁面縮放變化。由于與桌面瀏覽器間存在這些差異,最好是先檢測一下用戶是否在使用移動設備,然后再決定使用哪個屬性。
??另外,使用 resizeTo() 和 resizeBy() 方法可以調整瀏覽器窗口的大小。這兩個方法都接收兩個參數,其中 resizeTo() 接收瀏覽器窗口的新寬度和新高度,而 resizeBy() 接收新窗口與原窗口的寬度和高度之差。來看下面的例子。
// 調整到 100×100
window.resizeTo(100, 100);
// 調整到 200×150
window.resizeBy(100, 50);
// 調整到 300×300
window.resizeTo(300, 300);
??需要注意的是,這兩個方法與移動窗口位置的方法類似,也有可能被瀏覽器禁用;而且,在 Opera 和 IE7(及更高版本)中默認就是禁用的。另外,這兩個方法同樣不適用于框架,而只能對最外層的 window 對象使用。
1.5、導航和打開窗口
??使用 window.open() 方法既可以導航到一個特定的 URL,也可以打開一個新的瀏覽器窗口。
??window.open() 方法可以接收 4 個參數:
- 要加載的 URL,
- 窗口目標,
- 一個特性字符串,
- 一個表示新頁面是否取代瀏覽器歷史記錄中當前加載頁面的布爾值。
??通常只須傳遞第一個參數,最后一個參數只在不打開新窗口的情況下使用。
??如果為 window.open() 傳遞了第二個參數,而且該參數是已有窗口或框架的名稱,那么就會在具有該名稱的窗口或框架中加載第一個參數指定的 URL。看下面的例子。
// 等同于< a target="topFrame"></a>
window.open("http://www.wrox.com/", "topFrame");
??調用這行代碼,就如同用戶單擊了 href 屬性為 http://www.wrox.com/,target 屬性為 "topFrame" 的鏈接。
??如果有一個名叫 "topFrame" 的窗口或者框架,就會在該窗口或框架加載這個 URL;否則,就會創建一個新窗口并將其命名為 "topFrame"。此外,第二個參數也可以是下列任何一個特殊的窗口名稱:_self、_parent、_top 或_blank。
1、彈出窗口
??如果給 window.open() 傳遞的第二個參數并不是一個已經存在的窗口或框架,那么該方法就會根據在第三個參數位置上傳入的字符串創建一個新窗口或新標簽頁。
??如果沒有傳入第三個參數,那么就會打開一個帶有全部默認設置(工具欄、地址欄和狀態欄等)的新瀏覽器窗口(或者打開一個新標簽頁——根據瀏覽器設置)。在不打開新窗口的情況下,會忽略第三個參數。
??第三個參數是一個逗號分隔的設置字符串,表示在新窗口中都顯示哪些特性。下表列出了可以出現在這個字符串中的設置選項。
設 置 | 值 | 說 明 |
---|---|---|
fullscreen | yes或no | 表示瀏覽器窗口是否最大化。僅限IE |
width | 數值 | 表示新窗口的寬度。不能小于100 |
height | 數值 | 表示新窗口的高度。不能小于100 |
top | 數值 | 表示新窗口的上坐標。不能是負值 |
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 |
??表中所列的部分或全部設置選項,都可以通過逗號分隔的名值對列表來指定。其中,名值對以等號表示(注意,整個特性字符串中不允許出現空格),如下面的例子所示。
window.open("http://www.wrox.com/", "wroxWindow", "height=400,width=400,top=10,left=10,resizable=yes");
??這行代碼會打開一個新的可以調整大小的窗口,窗口初大小為 400×400 像素,并且距屏幕上沿和左邊各 10 像素。
??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();
??但是,這個方法僅適用于通過 window.open() 打開的彈出窗口。
??對于瀏覽器的主窗口,如果沒有得到用戶的允許是不能關閉它的。不過,彈出窗口倒是可以調用 top.close() 在不經用戶允許的情況下關閉自己。彈出窗口關閉之后,窗口的引用仍然還在,但除了像下面這樣檢測其 closed 屬性之外,已經沒有其他用處了。
wroxWin.close();
console.log(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");
console.log(wroxWin.opener == window); / /true
??雖然彈出窗口中有一個指針指向打開它的原始窗口,但原始窗口中并沒有這樣的指針指向彈出窗口。窗口并不跟蹤記錄它們打開的彈出窗口,因此我們只能在必要的時候自己來手動實現跟蹤。
??有些瀏覽器(如 IE8 和 Chrome)會在獨立的進程中運行每個標簽頁。當一個標簽頁打開另一個標簽頁時,如果兩個 window 對象之間需要彼此通信,那么新標簽頁就不能運行在獨立的進程中。在 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 采取了不同的處理方式。它不會像其他瀏覽器那樣簡單地屏蔽這些彈出窗口,而是只顯示它們的標題欄,并把它們放在瀏覽器窗口的右下角。
??在打開計算機硬盤中的網頁時,IE 會解除對彈出窗口的某些限制。但是在服務器上執行這些代碼會受到對彈出窗口的限制。
3、彈出窗口屏蔽程序
??大多數瀏覽器都內置有彈出窗口屏蔽程序,而沒有內置此類程序的瀏覽器,也可以安裝 Yahoo! Toolbar 等帶有內置屏蔽程序的實用工具。結果就是用戶可以將絕大多數不想看到彈出窗口屏蔽掉。
??于是,在彈出窗口被屏蔽時,就應該考慮兩種可能性。
??如果是瀏覽器內置的屏蔽程序阻止的彈出窗口,那么 window.open() 很可能會返回 null。此時,只要檢測這個返回的值就可以確定彈出窗口是否被屏蔽了,如下面的例子所示。
var wroxWin = window.open("http://www.wrox.com", "_blank");
if (wroxWin == null){
console.log("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) {
console.log("The popup was blocked!");
}
??在任何情況下,以上代碼都可以檢測出調用 window.open() 打開的彈出窗口是不是被屏蔽了。
??但要注意的是,檢測彈出窗口是否被屏蔽只是一方面,它并不會阻止瀏覽器顯示與被屏蔽的彈出窗口有關的消息。
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() {
console.log("Hello world!");
}, 1000);
// 注意:把它取消
clearTimeout(timeoutId);
??只要是在指定的時間尚未過去之前調用 clearTimeout(),就可以完全取消超時調用。前面的代碼在設置超時調用之后馬上又調用了 clearTimeout(),結果就跟什么也沒有發生一樣。
??超時調用的代碼都是在全局作用域中執行的,因此函數中 this 的值在非嚴格模式下指向 window 對象,在嚴格模式下是 undefined。
??間歇調用與超時調用類似,只不過它會按照指定的時間間隔重復執行代碼,直至間歇調用被取消或者頁面被卸載。
??設置間歇調用的方法是 setInterval(),它接受的參數與 setTimeout() 相同:要執行的代碼(字符串或函數)和每次執行之前需要等待的毫秒數。示例:
// 不建議傳遞字符串!
setInterval ("alert('Hello world!') ", 10000);
// 推薦的調用方式
setInterval (function() {
console.log("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);
console.log("Done");
}
}
intervalId = setInterval(incrementNumber, 500);
??在上述例子中,變量 num 每半秒鐘遞增一次,當遞增到最大值時就會取消先前設定的間歇調用。
??這個模式也可以使用超時調用來實現,示例:
var num = 0;
var max = 10;
function incrementNumber() {
num++;
// 如果執行次數未達到 max 設定的值,則設置另一次超時調用
if (num < max) {
setTimeout(incrementNumber, 500);
} else {
console.log("Done");
}
}
setTimeout(incrementNumber, 500);
??可見,在使用超時調用時,沒有必要跟蹤超時調用 ID,因為每次執行代碼之后,如果不再設置另一次超時調用,調用就會自行停止。
??一般認為,使用超時調用來模擬間歇調用的是一種最佳模式。在開發環境下,很少使用真正的間歇調用,原因是后一個間歇調用可能會在前一個間歇調用結束之前啟動。
??而像前面示例中那樣使用超時調用,則完全可以避免這一點。所以,最好不要使用間歇調用。
1.7、系統對話框
??瀏覽器通過 alert()、confirm() 和 prompt() 方法可以調用系統對話框向用戶顯示消息。系統對話框與瀏覽器中顯示的網頁沒有關系,也不包含 HTML。它們的外觀有操作系統及(或)瀏覽器設置決定,而不是由 CSS 決定。
??此外,通過這幾個方法打開的對話框都是同步和模態的。也就是說,顯示這些對話框的時候代碼會停止執行,而關掉這些對話框后代碼又會恢復執行。
??alert() 方法接收一個字符串并將其顯示給用戶。具體來說,調用 alert() 方法的結果就是向用戶顯示一個系統對話框,其中包含指定的文本和一個 OK(“確定”)按鈕。
??通常使用 alert() 生成的 “警告” 對話框向用戶顯示一些他們無法控制的消息,例如錯誤消息。而用戶只能在看完消息后關閉對話框。
??第二種對話框是調用 confirm() 方法生成的。從向用戶顯示消息的方面來看,這種 “確認” 對話框很像一個 “警告” 對話框。但二者的主要區別在于 “確認” 對話框除了顯示 OK 按鈕外,還會顯示一個 Cancel(“取消”)按鈕,兩個按鈕可以讓用戶決定是否執行給定的操作。
??為了確定用戶是單擊了 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. ");
}
??在上述例子中,第一行代碼(if 條件語句)會向用戶顯示一個確認對話框。如果用戶單擊了 OK,則通過一個警告框向用戶顯示消息 I’m so glad you’ re sure! 。如果用戶單擊的是 Cancel 按鈕,則通過警告框顯示 I’m sorry to hear you’re not sure.。這種模式經常在用戶想要執行刪除操作的時候使用,例如刪除電子郵件。
??最后一種對話框是通過調用 prompt() 方法生成的,這是一個 “提示” 框,用于提示用戶輸入一些文本。提示框中除了顯示 OK 和 Cancel 按鈕之外,還會顯示一個文本輸入域,以供用戶在其中輸入內容。
??prompt( )方法接受兩個參數:要顯示給用戶的文本提示和文本輸入域的默認值(可以是一個空字符串)。
??如果用戶單擊了 OK 按鈕,則 prompt() 返回文本輸入域的值;如果用戶單擊了 Cancel 或沒有單擊 OK 而是通過其他方式關閉了對話框,則該方法返回 null。示例:
var result = prompt("What is your name? ", "");
if (result !== null) {
alert("Welcome, " + result);
}
??綜上所述,這些系統對話框很適合向用戶顯示消息并請用戶作出決定。由于不涉及 HTML、CSS 或 JavaScript,因此它們是增強 Web 應用程序的一種便捷方式。
??除了上述三種對話框之外,Google Chrome 瀏覽器還引入了一種新特性。如果當前腳本在執行過程中會打開兩個或多個對話框,那么從第二個對話框開始,每個對話框中都會顯示一個復選框,以便用戶阻止后續的對話框顯示,除非用戶刷新頁面。
??如果用戶勾選了其中的復選框,并且關閉了對話框,那么除非用戶刷新頁面,所有后續的系統對話框(包括警告框、確認框和提示框)都會被屏蔽。Chrome 沒有就對話框是否顯示向開發人員提供任何
信息。
??由于瀏覽器會在空閑時重置對話框計數器,因此如果兩次獨立的用戶操作分別打開兩個警告框,那么這兩個警告框中都不會顯示復選框。而如果是同一次用戶操作會生成兩個警告框,那么第二個警告框中就會顯示復選框。這個新特性出現以后,IE9 和 Firefox 4 也實現了它。
??還有兩個可以通過 JavaScript 打開的對話框,即 “查找” 和 “打印”。這兩個對話框都是異步顯示的,能夠將控制權立即交還給腳本。
??這兩個對話框與用戶通過瀏覽器菜單的 “查找” 和 “打印” 命令打開的對話框相同。而在 JavaScript 中則可以像下面這樣通過 window 對象的 find() 和 print() 方法打開它們。
// 顯示“打印”對話框
window.print();
// 顯示“查找”對話框
window.find();
??這兩個方法同樣不會就用戶在對話框中的操作給出任何信息,因此它們的用處有限。
??另外,既然這兩個對話框是異步顯示的,那么 Chrome 的對話框計數器就不會將它們計算在內,所以它們也不會受用戶禁用后續對話框顯示的影響。
2、location 對象
??location 是最有用的 BOM 對象之一,它提供了與當前窗口中加載的文檔有關的信息,還提供了一些導航功能。
??事實上,location 對象時很特別的一個對象,因為它既是 window 對象的屬性,也是 document 對象的屬性;換句話說,,window.location 和 document.location 引用的是同一個對象。
??location 對象的用處不止表現在它保存著當前文檔的信息,還表現在它將 URL 解析為獨立的片段,讓開發人員可以通過不同的屬性訪問這些片段。
??下表列出了 location 對象的所有屬性(注:省略了每個屬性前面的 location 前綴)。
屬 性 名 | 例 子 | 說 明 |
---|---|---|
hash | "#contents" | 返回 URL 中的 hash (#號后跟零或多個字符), 如果 URL 中不包含散列, 則返回空字符串 |
host | "www.wrox.com:80" | 返回服務器名稱和端口號(如果有) |
hostname | "www.wrox.com" | 返回不帶端口號的服務器名稱 |
href | "http:/www.wrox.com" | 返回當前加載頁面的完整URL。 而 location 對象的 toString() 方法也返回這個值 |
pathname | "/WileyCDA/" | 返回URL中的目錄和(或)文件名 |
port | "8080" | 返回URL中指定的端口號。 如果URL中不包含端口號, 則這個屬性返回空字符串 |
protocol | "http:" | 返回頁面使用的協議。 通常是 http: 或 https: |
search | "?q=javascript" | 返回 URL 的查詢字符串。 這個字符串以問號開頭 |
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;
}
??上述函數的第一步是先去掉查詢字符串開頭的問號。當然,前提是 location.search 中必須要包含一或多個字符。
??然后,所有參數將被保存在 args 對象中,該對象以字面量形式創建。
??接下來,根據和號(&)來分割查詢字符串,并返回 name=value 格式的字符串數組。
??下面的 for 循環會迭代這個數組,然后再根據等于號分割每一項,從而返回第一項為參數名,第二項為參數值的數組。
再使
??用 decodeURIComponent() 分別解碼 name 和 value(因為查詢字符串應該是被編碼過的)。
??最后,將 name 作為 args 對象的屬性,將 value 作為相應屬性的值。
??示例:
// 假設查詢字符串是?q=javascript&num=10
var args = getQueryStringArgs();
console.log(args["q"]); // "javascript"
console.log(args["num"]); // "10"
??可見,每個查詢字符串參數都成了返回對象的屬性。這樣就極大地方便了對每個參數的訪問。
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.;
??在這些改變瀏覽器位置的方法中,最常用的是設置 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 重新加載。
??在 IE8、Firefox 1、Safari 2+、Opera 9+和 Chrome 中,修改 hash 的值會在瀏覽器的歷史記錄中生成一條新記錄。
??在 IE 的早期版本中,hash 屬性不會在用戶單擊 “后退” 和 “前進” 按鈕時被更新,只會在用戶單擊包含 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() 放在代碼的最后一行。
3、navigator 對象
??最早由 Netscape Navigator 2.0 引入的 navigator 對象,現在已經成為識別客戶端瀏覽器的事實標準。
??雖然其他瀏覽器也通過其他方式提供了相同或相似的信息(例如,IE 中的 window.clientInformation 和 Opera 中的 window.opera),但 navigator 對象卻是所有支持 JavaScript 的瀏覽器所共有的。
??與其他 BOM 對象的情況一樣,每個瀏覽器中的 navigator 對象也都有一套自己的屬性。
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
console.log(hasPlugin("Flash"));
// 檢測 QuickTime
console.log(hasPlugin("QuickTime"));
??hasPlugin()函數接受一個參數:要檢測的插件名。
??第一步是將傳入的名稱轉換為小寫形式,以便于比較。然后,迭代 plugins 數組,通過 indexOf()檢測每個 name 屬性,以確定傳入的名稱是否出現在字符串的某個地方。比較的字符串都使用小寫形式可以避免因大小寫不一致導致的錯誤。
??傳入的參數應該盡可能具體,以避免混淆。應該說,像 Flash 和 QuickTime 這樣的字符串就比較具體了,不容易導致混淆。在 Firefox、Safari、Opera 和 Chrome 中可以使用這種方法來檢測插件。
??每個插件對象本身也是一個 MimeType 對象的數組,這些對象可以通過方括號語法來訪問。
??每個 MimeType 對象有 4 個屬性:包含 MIME 類型描述的 description、回指插件對象的 enabledPlugin、表示與 MIME 類型對應的文件擴展名的字符串 suffixes(以逗號分隔)和表示完整 MIME 類型字符串的 type。
??檢測 IE 中的插件比較麻煩,因為 IE 不支持 Netscape 式的插件。在 IE 中檢測插件的唯一方式就是使用專有的 ActiveXObject 類型,并嘗試創建一個特定插件的實例。
??IE 是以 COM 對象的方式實現插件的,而 COM 對象使用唯一標識符來標識。因此,要想檢查特定的插件,就必須知道其 COM 標識符。
??例如,Flash 的標識符是 ShockwaveFlash.ShockwaveFlash。知道唯一標識符之后,就可以編寫類似下面的函數來檢測 IE 中是否安裝相應插件了。
//檢測 IE 中的插件
function hasIEPlugin(name){
try {
new ActiveXObject(name);
return true;
} catch (ex){
return false;
}
}
// 檢測 Flash
console.log(hasIEPlugin("ShockwaveFlash.ShockwaveFlash"));
// 檢測 QuickTime
console.log(hasIEPlugin("QuickTime.QuickTime"));
??在上述例子中,函數 hasIEPlugin() 只接收一個 COM 標識符作為參數。
??在函數內部,首先會嘗試創建一個 COM 對象的實例。之所以要在 try-catch 語句中進行實例化,是因為創建未知 COM 對象會導致拋出錯誤。這樣,如果實例化成功,則函數返回 true;否則,如果拋出了錯誤,則執行 catch塊,結果就會返回 false。
??例子最后檢測 IE 中是否安裝了 Flash 和 QuickTime 插件。
??鑒于檢測這兩種插件的方法差別太大,因此典型的做法是針對每個插件分別創建檢測函數,而不是使用前面介紹的通用檢測方法。來看下面的例子。
// 檢測所有瀏覽器中的 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
console.log(hasFlash());
// 檢測 QuickTime
console.log(hasQuickTime());
??上面代碼中定義了兩個函數:hasFlash() 和 hasQuickTime()。每個函數都是先嘗試使用不針對 IE 的插件檢測方法。如果返回了 false(在 IE 中會這樣),那么再使用針對 IE 的插件檢測方法。
??如果 IE 的插件檢測方法再返回 false,則整個方法也將返回 false。只要任何一次檢測返回 true,整個方法都會返回 true。
??plugins 集合有一個名叫 refresh() 的方法,用于刷新 plugins 以反映最新安裝的插件。
??這個方法接收一個參數:表示是否應該重新加載頁面的一個布爾值。如果將這個值設置為 true,則會重新加載包含插件的所有頁面;否則,只更新 plugins 集合,不重新加載頁面。
3.2、注冊處理程序
??Firefox 2為 navigator 對象新增了registerContentHandler() 和 registerProtocolHandler() 方法(這兩個方法是在 HTML5 中定義的)。這兩個方法可以讓一個站點指明它可以處理特定類型的信息。
??隨著 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 應用程序將以適當方式來處理該請求。
??Firefox 4 及之前版本只允許在 registerContentHandler() 方法中使用三個 MIME 類型:application/rss+xml、application/atom+xml 和 application/vnd.mozilla.maybe. feed。這三個 MIME 類型的作用都一樣,即為 RSS 或 ATOM 新聞源(feed)注冊處理程序。
??類似的調用方式也適用于 registerProtocolHandler() 方法,它也接收三個參數:要處理的協議(例如,mailto 或 ftp)、處理該協議的頁面的 URL 和應用程序的名稱。
??例如,要想將一個應用程序注冊為默認的郵件客戶端,可以使用如下代碼。
navigator.registerProtocolHandler("mailto", "http://www.somemailclient.com?cmd=%s", "Some Mail Client");
??上述例子注冊了一個 mailto 協議的處理程序,該程序指向一個基于 Web 的電子郵件客戶端。同樣,第二個參數仍然是處理相應請求的 URL,而%s 則表示原始的請求。
4、screen 對象
??JavaScript 中有幾個對象在編程中用處不大,而 screen 對象就是其中之一。
??screen 對象基本上只用來表明客戶端的能力,其中包括瀏覽器窗口外部的顯示器的信息,如像素寬度和高度等。每個瀏覽器中的 screen 對象都包含著各不相同的屬性。
??這些信息經常幾種出現在測定客戶端能力的站點跟蹤工具中,但通常不會用于影響功能。
??不過,有是有也可能會用到其中的信息來調整瀏覽器窗口的大小,使其占據屏幕的可用空間。示例:
window.resizeTo(screen.availWidth, screen.availHeight);
??前面提過,許多瀏覽器都會禁用調整瀏覽器窗口大小的能力,因此上面這行代碼不一定在所有環境下都有效。
??設計移動設備的屏幕大小時,情況有點不一樣。運行 iOS 的設備始終會像是吧設備豎著拿在手里一樣,因此返回的值是 768 x 1024。而 Android 設備則會相應調用 screen.width 和 screen.height 的值。
5、history 對象
??history 對象保存著用戶上網的歷史記錄,從窗口被打開的那一刻算起。因為 history 是 window 對象的屬性,因此每個瀏覽器窗口、每個標簽頁乃至每個框架,都有自己的 history 對象與特定的 window 對象關聯。
??出于安全方面考慮,開發人員無法得知用戶瀏覽過的 URL。不過,借由用戶訪問過的頁面列表,同樣可以在不知道實際 URL 的情況下實現后退和前進。
??使用 go() 方法可以在用戶的歷史記錄中任意跳轉,可以向后也可以向前。
??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){
// 這應該是用戶打開窗口后的第一個頁面
}
??雖然 history 并不常用,但在創建自定義的 “后退” 和 “前進” 按鈕,以及檢測當前頁面是不是用戶歷史記錄中的第一個頁面時,還是必須使用它。
??當頁面的 URL 改變是,就會生成一條歷史記錄。在 IE8+、Opera、Firefox、Safari 3 及更高版本以及 Chrome 中,這里所說的改變包括 URL 中 hash 的變化(因此,設置 location.hash 會在這些瀏覽器中生成一條新的歷史記錄)。
小結
??瀏覽器對象模型(BOM)以及 window 對象為依托,表示瀏覽器窗口以及頁面可見區域。同時,window 對象還是 ECMAScript 中的 Global 對象,因而所有全局變量和函數都是它的屬性,且所有原生的構造函數及其他函數也都存在于它的命名空間下。
??這里討論了下列 BOM 的組成部分。
- 在使用框架時,每個框架都有自己的 window 對象以及所有原生構造數及其他函數的副本。每個框架都保存在 frames 集合中,可以通過位置或通過名稱來訪問。
- 有一些窗口指針,可以用來引用其他框架,包括父框架。
- top 對象始終指向最外圍的框架,也就是整個瀏覽器窗口。
- parent 對象表示包含當前框架的框架,而 self 對象則回指 window。
- 使用 location 對象可以通過編程方式來訪問瀏覽器的導航系統。設置相應的屬性,可以逐段或整體性地修改瀏覽器的 URL。
- 調用 replace() 方法可以導航到一個新 URL,同時該 URL 會替換瀏覽器歷史記錄中當前顯示的頁面。
- navigator 對象提供了與瀏覽器有關的信息。到底提供哪些信息,很大程度上取決于用戶的瀏覽器;不過,也有一些公共的屬性(如 userAgent)存在于所有瀏覽器中。
??BOM 中還有兩個對象:screen 和 history,但它們的功能有限。
??screen 對象中保存著與客戶端顯示器有關的信息,這些信息一般只用于站點分析。
??history 對象為訪問瀏覽器的歷史記錄開了一個小縫隙,開發人員可以據此判斷歷史記錄的數量,也可以在歷史記錄中向后或向前導航到任意頁面。