# window 對象
## 概述
瀏覽器里面,`window`對象(注意,`w`為小寫)指當前的瀏覽器窗口。它也是當前頁面的頂層對象,即最高一層的對象,所有其他對象都是它的下屬。一個變量如果未聲明,那么默認就是頂層對象的屬性。
```javascript
a = 1;
window.a // 1
```
上面代碼中,`a`是一個沒有聲明就直接賦值的變量,它自動成為頂層對象的屬性。
`window`有自己的實體含義,其實不適合當作最高一層的頂層對象,這是一個語言的設計失誤。最早,設計這門語言的時候,原始設想是語言內置的對象越少越好,這樣可以提高瀏覽器的性能。因此,語言設計者 Brendan Eich 就把`window`對象當作頂層對象,所有未聲明就賦值的變量都自動變成`window`對象的屬性。這種設計使得編譯階段無法檢測出未聲明變量,但到了今天已經沒有辦法糾正了。
## window 對象的屬性
### window.name
`window.name`屬性是一個字符串,表示當前瀏覽器窗口的名字。窗口不一定需要名字,這個屬性主要配合超鏈接和表單的`target`屬性使用。
```javascript
window.name = 'Hello World!';
console.log(window.name)
// "Hello World!"
```
該屬性只能保存字符串,如果寫入的值不是字符串,會自動轉成字符串。各個瀏覽器對這個值的儲存容量有所不同,但是一般來說,可以高達幾MB。
只要瀏覽器窗口不關閉,這個屬性是不會消失的。舉例來說,訪問`a.com`時,該頁面的腳本設置了`window.name`,接下來在同一個窗口里面載入了`b.com`,新頁面的腳本可以讀到上一個網頁設置的`window.name`。頁面刷新也是這種情況。一旦瀏覽器窗口關閉后,該屬性保存的值就會消失,因為這時窗口已經不存在了。
### window.closed,window.opener
`window.closed`屬性返回一個布爾值,表示窗口是否關閉。
```javascript
window.closed // false
```
上面代碼檢查當前窗口是否關閉。這種檢查意義不大,因為只要能運行代碼,當前窗口肯定沒有關閉。這個屬性一般用來檢查,使用腳本打開的新窗口是否關閉。
```javascript
var popup = window.open();
if ((popup !== null) && !popup.closed) {
// 窗口仍然打開著
}
```
`window.opener`屬性表示打開當前窗口的父窗口。如果當前窗口沒有父窗口(即直接在地址欄輸入打開),則返回`null`。
```javascript
window.open().opener === window // true
```
上面表達式會打開一個新窗口,然后返回`true`。
如果兩個窗口之間不需要通信,建議將子窗口的`opener`屬性顯式設為`null`,這樣可以減少一些安全隱患。
```javascript
var newWin = window.open('example.html', 'newWindow', 'height=400,width=400');
newWin.opener = null;
```
上面代碼中,子窗口的`opener`屬性設為`null`,兩個窗口之間就沒辦法再聯系了。
通過`opener`屬性,可以獲得父窗口的全局屬性和方法,但只限于兩個窗口同源的情況(參見《同源限制》一章),且其中一個窗口由另一個打開。`<a>`元素添加`rel="noopener"`屬性,可以防止新打開的窗口獲取父窗口,減輕被惡意網站修改父窗口 URL 的風險。
```html
<a href="https://an.evil.site" target="_blank" rel="noopener">
惡意網站
</a>
```
### window.self,window.window
`window.self`和`window.window`屬性都指向窗口本身。這兩個屬性只讀。
```javascript
window.self === window // true
window.window === window // true
```
### window.frames,window.length
`window.frames`屬性返回一個類似數組的對象,成員為頁面內所有框架窗口,包括`frame`元素和`iframe`元素。`window.frames[0]`表示頁面中第一個框架窗口。
如果`iframe`元素設置了`id`或`name`屬性,那么就可以用屬性值,引用這個`iframe`窗口。比如`<iframe name="myIFrame">`可以用`frames['myIFrame']`或者`frames.myIFrame`來引用。
`frames`屬性實際上是`window`對象的別名。
```javascript
frames === window // true
```
因此,`frames[0]`也可以用`window[0]`表示。但是,從語義上看,`frames`更清晰,而且考慮到`window`還是全局對象,因此推薦表示多窗口時,總是使用`frames[0]`的寫法。更多介紹請看下文的《多窗口操作》部分。
`window.length`屬性返回當前網頁包含的框架總數。如果當前網頁不包含`frame`和`iframe`元素,那么`window.length`就返回`0`。
```javascript
window.frames.length === window.length // true
```
上面代碼表示,`window.frames.length`與`window.length`應該是相等的。
### window.frameElement
`window.frameElement`屬性主要用于當前窗口嵌在另一個網頁的情況(嵌入`<object>`、`<iframe>`或`<embed>`元素),返回當前窗口所在的那個元素節點。如果當前窗口是頂層窗口,或者所嵌入的那個網頁不是同源的,該屬性返回`null`。
```javascript
// HTML 代碼如下
// <iframe src="about.html"></iframe>
// 下面的腳本在 about.html 里面
var frameEl = window.frameElement;
if (frameEl) {
frameEl.src = 'other.html';
}
```
上面代碼中,`frameEl`變量就是`<iframe>`元素。
### window.top,window.parent
`window.top`屬性指向最頂層窗口,主要用于在框架窗口(frame)里面獲取頂層窗口。
`window.parent`屬性指向父窗口。如果當前窗口沒有父窗口,`window.parent`指向自身。
```javascript
if (window.parent !== window.top) {
// 表明當前窗口嵌入不止一層
}
```
對于不包含框架的網頁,這兩個屬性等同于`window`對象。
### window.status
`window.status`屬性用于讀寫瀏覽器狀態欄的文本。但是,現在很多瀏覽器都不允許改寫狀態欄文本,所以使用這個方法不一定有效。
### window.devicePixelRatio
`window.devicePixelRatio`屬性返回一個數值,表示一個 CSS 像素的大小與一個物理像素的大小之間的比率。也就是說,它表示一個 CSS 像素由多少個物理像素組成。它可以用于判斷用戶的顯示環境,如果這個比率較大,就表示用戶正在使用高清屏幕,因此可以顯示較大像素的圖片。
### 位置大小屬性
以下屬性返回`window`對象的位置信息和大小信息。
**(1)window.screenX,window.screenY**
`window.screenX`和`window.screenY`屬性,返回瀏覽器窗口左上角相對于當前屏幕左上角的水平距離和垂直距離(單位像素)。這兩個屬性只讀。
**(2) window.innerHeight,window.innerWidth**
`window.innerHeight`和`window.innerWidth`屬性,返回網頁在當前窗口中可見部分的高度和寬度,即“視口”(viewport)的大小(單位像素)。這兩個屬性只讀。
用戶放大網頁的時候(比如將網頁從100%的大小放大為200%),這兩個屬性會變小。因為這時網頁的像素大小不變(比如寬度還是960像素),只是每個像素占據的屏幕空間變大了,因此可見部分(視口)就變小了。
注意,這兩個屬性值包括滾動條的高度和寬度。
**(3)window.outerHeight,window.outerWidth**
`window.outerHeight`和`window.outerWidth`屬性返回瀏覽器窗口的高度和寬度,包括瀏覽器菜單和邊框(單位像素)。這兩個屬性只讀。
**(4)window.scrollX,window.scrollY**
`window.scrollX`屬性返回頁面的水平滾動距離,`window.scrollY`屬性返回頁面的垂直滾動距離,單位都為像素。這兩個屬性只讀。
注意,這兩個屬性的返回值不是整數,而是雙精度浮點數。如果頁面沒有滾動,它們的值就是`0`。
舉例來說,如果用戶向下拉動了垂直滾動條75像素,那么`window.scrollY`就是75左右。用戶水平向右拉動水平滾動條200像素,`window.scrollX`就是200左右。
```javascript
if (window.scrollY < 75) {
window.scroll(0, 75);
}
```
上面代碼中,如果頁面向下滾動的距離小于75像素,那么頁面向下滾動75像素。
**(5)window.pageXOffset,window.pageYOffset**
`window.pageXOffset`屬性和`window.pageYOffset`屬性,是`window.scrollX`和`window.scrollY`別名。
### 組件屬性
組件屬性返回瀏覽器的組件對象。這樣的屬性有下面幾個。
- `window.locationbar`:地址欄對象
- `window.menubar`:菜單欄對象
- `window.scrollbars`:窗口的滾動條對象
- `window.toolbar`:工具欄對象
- `window.statusbar`:狀態欄對象
- `window.personalbar`:用戶安裝的個人工具欄對象
這些對象的`visible`屬性是一個布爾值,表示這些組件是否可見。這些屬性只讀。
```javascript
window.locationbar.visible
window.menubar.visible
window.scrollbars.visible
window.toolbar.visible
window.statusbar.visible
window.personalbar.visible
```
### 全局對象屬性
全局對象屬性指向一些瀏覽器原生的全局對象。
- `window.document`:指向`document`對象,詳見《document 對象》一章。注意,這個屬性有同源限制。只有來自同源的腳本才能讀取這個屬性。
- `window.location`:指向`Location`對象,用于獲取當前窗口的 URL 信息。它等同于`document.location`屬性,詳見《Location 對象》一章。
- `window.navigator`:指向`Navigator`對象,用于獲取環境信息,詳見《Navigator 對象》一章。
- `window.history`:指向`History`對象,表示瀏覽器的瀏覽歷史,詳見《History 對象》一章。
- `window.localStorage`:指向本地儲存的 localStorage 數據,詳見《Storage 接口》一章。
- `window.sessionStorage`:指向本地儲存的 sessionStorage 數據,詳見《Storage 接口》一章。
- `window.console`:指向`console`對象,用于操作控制臺,詳見《console 對象》一章。
- `window.screen`:指向`Screen`對象,表示屏幕信息,詳見《Screen 對象》一章。
### window.isSecureContext
`window.isSecureContext`屬性返回一個布爾值,表示當前窗口是否處在加密環境。如果是 HTTPS 協議,就是`true`,否則就是`false`。
## window 對象的方法
### window.alert(),window.prompt(),window.confirm()
`window.alert()`、`window.prompt()`、`window.confirm()`都是瀏覽器與用戶互動的全局方法。它們會彈出不同的對話框,要求用戶做出回應。注意,這三個方法彈出的對話框,都是瀏覽器統一規定的式樣,無法定制。
**(1)window.alert()**
`window.alert()`方法彈出的對話框,只有一個“確定”按鈕,往往用來通知用戶某些信息。
```javascript
window.alert('Hello World');
```
用戶只有點擊“確定”按鈕,對話框才會消失。對話框彈出期間,瀏覽器窗口處于凍結狀態,如果不點“確定”按鈕,用戶什么也干不了。
`window.alert()`方法的參數只能是字符串,沒法使用 CSS 樣式,但是可以用`\n`指定換行。
```javascript
alert('本條提示\n分成兩行');
```
**(2)window.prompt()**
`window.prompt()`方法彈出的對話框,提示文字的下方,還有一個輸入框,要求用戶輸入信息,并有“確定”和“取消”兩個按鈕。它往往用來獲取用戶輸入的數據。
```javascript
var result = prompt('您的年齡?', 25)
```
上面代碼會跳出一個對話框,文字提示為“您的年齡?”,要求用戶在對話框中輸入自己的年齡(默認顯示25)。用戶填入的值,會作為返回值存入變量`result`。
`window.prompt()`的返回值有兩種情況,可能是字符串(有可能是空字符串),也有可能是`null`。具體分成三種情況。
1. 用戶輸入信息,并點擊“確定”,則用戶輸入的信息就是返回值。
2. 用戶沒有輸入信息,直接點擊“確定”,則輸入框的默認值就是返回值。
3. 用戶點擊了“取消”(或者按了 ESC 按鈕),則返回值是`null`。
`window.prompt()`方法的第二個參數是可選的,但是最好總是提供第二個參數,作為輸入框的默認值。
**(3)window.confirm()**
`window.confirm()`方法彈出的對話框,除了提示信息之外,只有“確定”和“取消”兩個按鈕,往往用來征詢用戶是否同意。
```javascript
var result = confirm('你最近好嗎?');
```
上面代碼彈出一個對話框,上面只有一行文字“你最近好嗎?”,用戶選擇點擊“確定”或“取消”。
`confirm`方法返回一個布爾值,如果用戶點擊“確定”,返回`true`;如果用戶點擊“取消”,則返回`false`。
```javascript
var okay = confirm('Please confirm this message.');
if (okay) {
// 用戶按下“確定”
} else {
// 用戶按下“取消”
}
```
`confirm`的一個用途是,用戶離開當前頁面時,彈出一個對話框,問用戶是否真的要離開。
```javascript
window.onunload = function () {
return window.confirm('你確定要離開當面頁面嗎?');
}
```
這三個方法都具有堵塞效應,一旦彈出對話框,整個頁面就是暫停執行,等待用戶做出反應。
### window.open(), window.close(),window.stop()
**(1)window.open()**
`window.open`方法用于新建另一個瀏覽器窗口,類似于瀏覽器菜單的新建窗口選項。它會返回新窗口的引用,如果無法新建窗口,則返回`null`。
```javascript
var popup = window.open('somefile.html');
```
上面代碼會讓瀏覽器彈出一個新建窗口,網址是當前域名下的`somefile.html`。
`open`方法一共可以接受三個參數。
```javascript
window.open(url, windowName, [windowFeatures])
```
- `url`:字符串,表示新窗口的網址。如果省略,默認網址就是`about:blank`。
- `windowName`:字符串,表示新窗口的名字。如果該名字的窗口已經存在,則占用該窗口,不再新建窗口。如果省略,就默認使用`_blank`,表示新建一個沒有名字的窗口。另外還有幾個預設值,`_self`表示當前窗口,`_top`表示頂層窗口,`_parent`表示上一層窗口。
- `windowFeatures`:字符串,內容為逗號分隔的鍵值對(詳見下文),表示新窗口的參數,比如有沒有提示欄、工具條等等。如果省略,則默認打開一個完整 UI 的新窗口。如果新建的是一個已經存在的窗口,則該參數不起作用,瀏覽器沿用以前窗口的參數。
下面是一個例子。
```javascript
var popup = window.open(
'somepage.html',
'DefinitionsWindows',
'height=200,width=200,location=no,status=yes,resizable=yes,scrollbars=yes'
);
```
上面代碼表示,打開的新窗口高度和寬度都為200像素,沒有地址欄,但有狀態欄和滾動條,允許用戶調整大小。
第三個參數可以設定如下屬性。
- left:新窗口距離屏幕最左邊的距離(單位像素)。注意,新窗口必須是可見的,不能設置在屏幕以外的位置。
- top:新窗口距離屏幕最頂部的距離(單位像素)。
- height:新窗口內容區域的高度(單位像素),不得小于100。
- width:新窗口內容區域的寬度(單位像素),不得小于100。
- outerHeight:整個瀏覽器窗口的高度(單位像素),不得小于100。
- outerWidth:整個瀏覽器窗口的寬度(單位像素),不得小于100。
- menubar:是否顯示菜單欄。
- toolbar:是否顯示工具欄。
- location:是否顯示地址欄。
- personalbar:是否顯示用戶自己安裝的工具欄。
- status:是否顯示狀態欄。
- dependent:是否依賴父窗口。如果依賴,那么父窗口最小化,該窗口也最小化;父窗口關閉,該窗口也關閉。
- minimizable:是否有最小化按鈕,前提是`dialog=yes`。
- noopener:新窗口將與父窗口切斷聯系,即新窗口的`window.opener`屬性返回`null`,父窗口的`window.open()`方法也返回`null`。
- resizable:新窗口是否可以調節大小。
- scrollbars:是否允許新窗口出現滾動條。
- dialog:新窗口標題欄是否出現最大化、最小化、恢復原始大小的控件。
- titlebar:新窗口是否顯示標題欄。
- alwaysRaised:是否顯示在所有窗口的頂部。
- alwaysLowered:是否顯示在父窗口的底下。
- close:新窗口是否顯示關閉按鈕。
對于那些可以打開和關閉的屬性,設為`yes`或`1`或不設任何值就表示打開,比如`status=yes`、`status=1`、`status`都會得到同樣的結果。如果想設為關閉,不用寫`no`,而是直接省略這個屬性即可。也就是說,如果在第三個參數中設置了一部分屬性,其他沒有被設置的`yes/no`屬性都會被設成`no`,只有`titlebar`和關閉按鈕除外(它們的值默認為`yes`)。
上面這些屬性,屬性名與屬性值之間用等號連接,屬性與屬性之間用逗號分隔。
```javascript
'height=200,width=200,location=no,status=yes,resizable=yes,scrollbars=yes'
```
另外,`open()`方法的第二個參數雖然可以指定已經存在的窗口,但是不等于可以任意控制其他窗口。為了防止被不相干的窗口控制,瀏覽器只有在兩個窗口同源,或者目標窗口被當前網頁打開的情況下,才允許`open`方法指向該窗口。
`window.open`方法返回新窗口的引用。
```javascript
var windowB = window.open('windowB.html', 'WindowB');
windowB.window.name // "WindowB"
```
注意,如果新窗口和父窗口不是同源的(即不在同一個域),它們彼此不能獲取對方窗口對象的內部屬性。
下面是另一個例子。
```javascript
var w = window.open();
console.log('已經打開新窗口');
w.location = 'http://example.com';
```
上面代碼先打開一個新窗口,然后在該窗口彈出一個對話框,再將網址導向`example.com`。
由于`open`這個方法很容易被濫用,許多瀏覽器默認都不允許腳本自動新建窗口。只允許在用戶點擊鏈接或按鈕時,腳本做出反應,彈出新窗口。因此,有必要檢查一下打開新窗口是否成功。
```javascript
var popup = window.open();
if (popup === null) {
// 新建窗口失敗
}
```
**(2)window.close()**
`window.close`方法用于關閉當前窗口,一般只用來關閉`window.open`方法新建的窗口。
```javascript
popup.close()
```
該方法只對頂層窗口有效,`iframe`框架之中的窗口使用該方法無效。
**(3)window.stop()**
`window.stop()`方法完全等同于單擊瀏覽器的停止按鈕,會停止加載圖像、視頻等正在或等待加載的對象。
```javascript
window.stop()
```
### window.moveTo(),window.moveBy()
`window.moveTo()`方法用于移動瀏覽器窗口到指定位置。它接受兩個參數,分別是窗口左上角距離屏幕左上角的水平距離和垂直距離,單位為像素。
```javascript
window.moveTo(100, 200)
```
上面代碼將窗口移動到屏幕`(100, 200)`的位置。
`window.moveBy()`方法將窗口移動到一個相對位置。它接受兩個參數,分別是窗口左上角向右移動的水平距離和向下移動的垂直距離,單位為像素。
```javascript
window.moveBy(25, 50)
```
上面代碼將窗口向右移動25像素、向下移動50像素。
為了防止有人濫用這兩個方法,隨意移動用戶的窗口,目前只有一種情況,瀏覽器允許用腳本移動窗口:該窗口是用`window.open()`方法新建的,并且窗口里只有它一個 Tab 頁。除此以外的情況,使用上面兩個方法都是無效的。
### window.resizeTo(),window.resizeBy()
`window.resizeTo()`方法用于縮放窗口到指定大小。
它接受兩個參數,第一個是縮放后的窗口寬度(`outerWidth`屬性,包含滾動條、標題欄等等),第二個是縮放后的窗口高度(`outerHeight`屬性)。
```javascript
window.resizeTo(
window.screen.availWidth / 2,
window.screen.availHeight / 2
)
```
上面代碼將當前窗口縮放到,屏幕可用區域的一半寬度和高度。
`window.resizeBy()`方法用于縮放窗口。它與`window.resizeTo()`的區別是,它按照相對的量縮放,`window.resizeTo()`需要給出縮放后的絕對大小。
它接受兩個參數,第一個是水平縮放的量,第二個是垂直縮放的量,單位都是像素。
```javascript
window.resizeBy(-200, -200)
```
上面的代碼將當前窗口的寬度和高度,都縮小200像素。
### window.scrollTo(),window.scroll(),window.scrollBy()
`window.scrollTo`方法用于將文檔滾動到指定位置。它接受兩個參數,表示滾動后位于窗口左上角的頁面坐標。
```javascript
window.scrollTo(x-coord, y-coord)
```
它也可以接受一個配置對象作為參數。
```javascript
window.scrollTo(options)
```
配置對象`options`有三個屬性。
- `top`:滾動后頁面左上角的垂直坐標,即 y 坐標。
- `left`:滾動后頁面左上角的水平坐標,即 x 坐標。
- `behavior`:字符串,表示滾動的方式,有三個可能值(`smooth`、`instant`、`auto`),默認值為`auto`。
```javascript
window.scrollTo({
top: 1000,
behavior: 'smooth'
});
```
`window.scroll()`方法是`window.scrollTo()`方法的別名。
`window.scrollBy()`方法用于將網頁滾動指定距離(單位像素)。它接受兩個參數:水平向右滾動的像素,垂直向下滾動的像素。
```javascript
window.scrollBy(0, window.innerHeight)
```
上面代碼用于將網頁向下滾動一屏。
如果不是要滾動整個文檔,而是要滾動某個元素,可以使用下面三個屬性和方法。
- Element.scrollTop
- Element.scrollLeft
- Element.scrollIntoView()
### window.print()
`window.print`方法會跳出打印對話框,與用戶點擊菜單里面的“打印”命令效果相同。
常見的打印按鈕代碼如下。
```javascript
document.getElementById('printLink').onclick = function () {
??window.print();
}
```
非桌面設備(比如手機)可能沒有打印功能,這時可以這樣判斷。
```javascript
if (typeof window.print === 'function') {
// 支持打印功能
}
```
### window.focus(),window.blur()
`window.focus()`方法會激活窗口,使其獲得焦點,出現在其他窗口的前面。
```javascript
var popup = window.open('popup.html', 'Popup Window');
if ((popup !== null) && !popup.closed) {
??popup.focus();
}
```
上面代碼先檢查`popup`窗口是否依然存在,確認后激活該窗口。
`window.blur()`方法將焦點從窗口移除。
當前窗口獲得焦點時,會觸發`focus`事件;當前窗口失去焦點時,會觸發`blur`事件。
### window.getSelection()
`window.getSelection`方法返回一個`Selection`對象,表示用戶現在選中的文本。
```javascript
var selObj = window.getSelection();
```
使用`Selection`對象的`toString`方法可以得到選中的文本。
```javascript
var selectedText = selObj.toString();
```
### window.getComputedStyle(),window.matchMedia()
`window.getComputedStyle()`方法接受一個元素節點作為參數,返回一個包含該元素的最終樣式信息的對象,詳見《CSS 操作》一章。
`window.matchMedia()`方法用來檢查 CSS 的`mediaQuery`語句,詳見《CSS 操作》一章。
### window.requestAnimationFrame()
`window.requestAnimationFrame()`方法跟`setTimeout`類似,都是推遲某個函數的執行。不同之處在于,`setTimeout`必須指定推遲的時間,`window.requestAnimationFrame()`則是推遲到瀏覽器下一次重流時執行,執行完才會進行下一次重繪。重繪通常是 16ms 執行一次,不過瀏覽器會自動調節這個速率,比如網頁切換到后臺 Tab 頁時,`requestAnimationFrame()`會暫停執行。
如果某個函數會改變網頁的布局,一般就放在`window.requestAnimationFrame()`里面執行,這樣可以節省系統資源,使得網頁效果更加平滑。因為慢速設備會用較慢的速率重流和重繪,而速度更快的設備會有更快的速率。
該方法接受一個回調函數作為參數。
```javascript
window.requestAnimationFrame(callback)
```
上面代碼中,`callback`是一個回調函數。`callback`執行時,它的參數就是系統傳入的一個高精度時間戳(`performance.now()`的返回值),單位是毫秒,表示距離網頁加載的時間。
`window.requestAnimationFrame()`的返回值是一個整數,這個整數可以傳入`window.cancelAnimationFrame()`,用來取消回調函數的執行。
下面是一個`window.requestAnimationFrame()`執行網頁動畫的例子。
```javascript
var element = document.getElementById('animate');
element.style.position = 'absolute';
var start = null;
function step(timestamp) {
if (!start) start = timestamp;
var progress = timestamp - start;
// 元素不斷向左移,最大不超過200像素
element.style.left = Math.min(progress / 10, 200) + 'px';
// 如果距離第一次執行不超過 2000 毫秒,
// 就繼續執行動畫
if (progress < 2000) {
window.requestAnimationFrame(step);
}
}
window.requestAnimationFrame(step);
```
上面代碼定義了一個網頁動畫,持續時間是2秒,會讓元素向右移動。
### window.requestIdleCallback()
`window.requestIdleCallback()`跟`setTimeout`類似,也是將某個函數推遲執行,但是它保證將回調函數推遲到系統資源空閑時執行。也就是說,如果某個任務不是很關鍵,就可以使用`window.requestIdleCallback()`將其推遲執行,以保證網頁性能。
它跟`window.requestAnimationFrame()`的區別在于,后者指定回調函數在下一次瀏覽器重排時執行,問題在于下一次重排時,系統資源未必空閑,不一定能保證在16毫秒之內完成;`window.requestIdleCallback()`可以保證回調函數在系統資源空閑時執行。
該方法接受一個回調函數和一個配置對象作為參數。配置對象可以指定一個推遲執行的最長時間,如果過了這個時間,回調函數不管系統資源有無空閑,都會執行。
```javascript
window.requestIdleCallback(callback[, options])
```
`callback`參數是一個回調函數。該回調函數執行時,系統會傳入一個`IdleDeadline`對象作為參數。`IdleDeadline`對象有一個`didTimeout`屬性(布爾值,表示是否為超時調用)和一個`timeRemaining()`方法(返回該空閑時段剩余的毫秒數)。
`options`參數是一個配置對象,目前只有`timeout`一個屬性,用來指定回調函數推遲執行的最大毫秒數。該參數可選。
`window.requestIdleCallback()`方法返回一個整數。該整數可以傳入`window.cancelIdleCallback()`取消回調函數。
下面是一個例子。
```javascript
requestIdleCallback(myNonEssentialWork);
function myNonEssentialWork(deadline) {
while (deadline.timeRemaining() > 0) {
doWorkIfNeeded();
}
}
```
上面代碼中,`requestIdleCallback()`用來執行非關鍵任務`myNonEssentialWork`。該任務先確認本次空閑時段有剩余時間,然后才真正開始執行任務。
下面是指定`timeout`的例子。
```javascript
requestIdleCallback(processPendingAnalyticsEvents, { timeout: 2000 });
```
上面代碼指定,`processPendingAnalyticsEvents`必須在未來2秒之內執行。
如果由于超時導致回調函數執行,則`deadline.timeRemaining()`返回`0`,`deadline.didTimeout`返回`true`。
如果多次執行`window.requestIdleCallback()`,指定多個回調函數,那么這些回調函數將排成一個隊列,按照先進先出的順序執行。
## 事件
`window`對象可以接收以下事件。
### load 事件和 onload 屬性
`load`事件發生在文檔在瀏覽器窗口加載完畢時。`window.onload`屬性可以指定這個事件的回調函數。
```javascript
window.onload = function() {
var elements = document.getElementsByClassName('example');
for (var i = 0; i < elements.length; i++) {
var elt = elements[i];
// ...
}
};
```
上面代碼在網頁加載完畢后,獲取指定元素并進行處理。
### error 事件和 onerror 屬性
瀏覽器腳本發生錯誤時,會觸發`window`對象的`error`事件。我們可以通過`window.onerror`屬性對該事件指定回調函數。
```javascript
window.onerror = function (message, filename, lineno, colno, error) {
console.log("出錯了!--> %s", error.stack);
};
```
由于歷史原因,`window`的`error`事件的回調函數不接受錯誤對象作為參數,而是一共可以接受五個參數,它們的含義依次如下。
- 出錯信息
- 出錯腳本的網址
- 行號
- 列號
- 錯誤對象
老式瀏覽器只支持前三個參數。
并不是所有的錯誤,都會觸發 JavaScript 的`error`事件(即讓 JavaScript 報錯)。一般來說,只有 JavaScript 腳本的錯誤,才會觸發這個事件,而像資源文件不存在之類的錯誤,都不會觸發。
下面是一個例子,如果整個頁面未捕獲錯誤超過3個,就顯示警告。
```javascript
window.onerror = function(msg, url, line) {
if (onerror.num++ > onerror.max) {
alert('ERROR: ' + msg + '\n' + url + ':' + line);
return true;
}
}
onerror.max = 3;
onerror.num = 0;
```
需要注意的是,如果腳本網址與網頁網址不在同一個域(比如使用了 CDN),瀏覽器根本不會提供詳細的出錯信息,只會提示出錯,錯誤類型是“Script error.”,行號為0,其他信息都沒有。這是瀏覽器防止向外部腳本泄漏信息。一個解決方法是在腳本所在的服務器,設置`Access-Control-Allow-Origin`的 HTTP 頭信息。
```bash
Access-Control-Allow-Origin: *
```
然后,在網頁的`<script>`標簽中設置`crossorigin`屬性。
```html
<script crossorigin="anonymous" src="//example.com/file.js"></script>
```
上面代碼的`crossorigin="anonymous"`表示,讀取文件不需要身份信息,即不需要 cookie 和 HTTP 認證信息。如果設為`crossorigin="use-credentials"`,就表示瀏覽器會上傳 cookie 和 HTTP 認證信息,同時還需要服務器端打開 HTTP 頭信息`Access-Control-Allow-Credentials`。
### window 對象的事件監聽屬性
除了具備元素節點都有的 GlobalEventHandlers 接口,`window`對象還具有以下的事件監聽函數屬性。
- `window.onafterprint`:`afterprint`事件的監聽函數。
- `window.onbeforeprint`:`beforeprint`事件的監聽函數。
- `window.onbeforeunload`:`beforeunload`事件的監聽函數。
- `window.onhashchange`:`hashchange`事件的監聽函數。
- `window.onlanguagechange`: `languagechange`的監聽函數。
- `window.onmessage`:`message`事件的監聽函數。
- `window.onmessageerror`:`MessageError`事件的監聽函數。
- `window.onoffline`:`offline`事件的監聽函數。
- `window.ononline`:`online`事件的監聽函數。
- `window.onpagehide`:`pagehide`事件的監聽函數。
- `window.onpageshow`:`pageshow`事件的監聽函數。
- `window.onpopstate`:`popstate`事件的監聽函數。
- `window.onstorage`:`storage`事件的監聽函數。
- `window.onunhandledrejection`:未處理的 Promise 對象的`reject`事件的監聽函數。
- `window.onunload`:`unload`事件的監聽函數。
## 多窗口操作
由于網頁可以使用`iframe`元素,嵌入其他網頁,因此一個網頁之中會形成多個窗口。如果子窗口之中又嵌入別的網頁,就會形成多級窗口。
### 窗口的引用
各個窗口之中的腳本,可以引用其他窗口。瀏覽器提供了一些特殊變量,用來返回其他窗口。
- `top`:頂層窗口,即最上層的那個窗口
- `parent`:父窗口
- `self`:當前窗口,即自身
下面代碼可以判斷,當前窗口是否為頂層窗口。
```javascript
if (window.top === window.self) {
// 當前窗口是頂層窗口
} else {
// 當前窗口是子窗口
}
```
下面的代碼讓父窗口的訪問歷史后退一次。
```javascript
window.parent.history.back();
```
與這些變量對應,瀏覽器還提供一些特殊的窗口名,供`window.open()`方法、`<a>`標簽、`<form>`標簽等引用。
- `_top`:頂層窗口
- `_parent`:父窗口
- `_blank`:新窗口
下面代碼就表示在頂層窗口打開鏈接。
```html
<a href="somepage.html" target="_top">Link</a>
```
### iframe 元素
對于`iframe`嵌入的窗口,`document.getElementById`方法可以拿到該窗口的 DOM 節點,然后使用`contentWindow`屬性獲得`iframe`節點包含的`window`對象。
```javascript
var frame = document.getElementById('theFrame');
var frameWindow = frame.contentWindow;
```
上面代碼中,`frame.contentWindow`可以拿到子窗口的`window`對象。然后,在滿足同源限制的情況下,可以讀取子窗口內部的屬性。
```javascript
// 獲取子窗口的標題
frameWindow.title
```
`<iframe>`元素的`contentDocument`屬性,可以拿到子窗口的`document`對象。
```javascript
var frame = document.getElementById('theFrame');
var frameDoc = frame.contentDocument;
// 等同于
var frameDoc = frame.contentWindow.document;
```
`<iframe>`元素遵守同源政策,只有當父窗口與子窗口在同一個域時,兩者之間才可以用腳本通信,否則只有使用`window.postMessage`方法。
`<iframe>`窗口內部,使用`window.parent`引用父窗口。如果當前頁面沒有父窗口,則`window.parent`屬性返回自身。因此,可以通過`window.parent`是否等于`window.self`,判斷當前窗口是否為`iframe`窗口。
```javascript
if (window.parent !== window.self) {
// 當前窗口是子窗口
}
```
`<iframe>`窗口的`window`對象,有一個`frameElement`屬性,返回`<iframe>`在父窗口中的 DOM 節點。對于非嵌入的窗口,該屬性等于`null`。
```javascript
var f1Element = document.getElementById('f1');
var f1Window = f1Element.contentWindow;
f1Window.frameElement === f1Element // true
window.frameElement === null // true
```
### window.frames 屬性
`window.frames`屬性返回一個類似數組的對象,成員是所有子窗口的`window`對象。可以使用這個屬性,實現窗口之間的互相引用。比如,`frames[0]`返回第一個子窗口,`frames[1].frames[2]`返回第二個子窗口內部的第三個子窗口,`parent.frames[1]`返回父窗口的第二個子窗口。
注意,`window.frames`每個成員的值,是框架內的窗口(即框架的`window`對象),而不是`iframe`標簽在父窗口的 DOM 節點。如果要獲取每個框架內部的 DOM 樹,需要使用`window.frames[0].document`的寫法。
另外,如果`<iframe>`元素設置了`name`或`id`屬性,那么屬性值會自動成為全局變量,并且可以通過`window.frames`屬性引用,返回子窗口的`window`對象。
```javascript
// HTML 代碼為 <iframe id="myFrame">
window.myFrame // [HTMLIFrameElement]
frames.myframe === myFrame // true
```
另外,`name`屬性的值會自動成為子窗口的名稱,可以用在`window.open`方法的第二個參數,或者`<a>`和`<frame>`標簽的`target`屬性。
- 前言
- 入門篇
- 導論
- 歷史
- 基本語法
- 數據類型
- 概述
- null,undefined 和布爾值
- 數值
- 字符串
- 對象
- 函數
- 數組
- 運算符
- 算術運算符
- 比較運算符
- 布爾運算符
- 二進制位運算符
- 其他運算符,運算順序
- 語法專題
- 數據類型的轉換
- 錯誤處理機制
- 編程風格
- console 對象與控制臺
- 標準庫
- Object 對象
- 屬性描述對象
- Array 對象
- 包裝對象
- Boolean 對象
- Number 對象
- String 對象
- Math 對象
- Date 對象
- RegExp 對象
- JSON 對象
- 面向對象編程
- 實例對象與 new 命令
- this 關鍵字
- 對象的繼承
- Object 對象的相關方法
- 嚴格模式
- 異步操作
- 概述
- 定時器
- Promise 對象
- DOM
- 概述
- Node 接口
- NodeList 接口,HTMLCollection 接口
- ParentNode 接口,ChildNode 接口
- Document 節點
- Element 節點
- 屬性的操作
- Text 節點和 DocumentFragment 節點
- CSS 操作
- Mutation Observer API
- 事件
- EventTarget 接口
- 事件模型
- Event 對象
- 鼠標事件
- 鍵盤事件
- 進度事件
- 表單事件
- 觸摸事件
- 拖拉事件
- 其他常見事件
- GlobalEventHandlers 接口
- 瀏覽器模型
- 瀏覽器模型概述
- window 對象
- Navigator 對象,Screen 對象
- Cookie
- XMLHttpRequest 對象
- 同源限制
- CORS 通信
- Storage 接口
- History 對象
- Location 對象,URL 對象,URLSearchParams 對象
- ArrayBuffer 對象,Blob 對象
- File 對象,FileList 對象,FileReader 對象
- 表單,FormData 對象
- IndexedDB API
- Web Worker
- 附錄:網頁元素接口
- a
- img
- form
- input
- button
- option
- video,audio