花了半個多月的時間,終于又把“JS紅寶書”又擼了一遍。
第一次讀“JS紅寶書”還是2015年初學JS的時候,那時候只是把語法部分讀了一遍,還有一些瀏覽器相關(guān)知識做了下了解,大概也就讀了半本的樣子,
就開始了用JS進行開發(fā)了,在成長的道路上遇見了JQuery,當時真的是感覺到JQuery太友好了,慢慢放下了原生開發(fā)。
現(xiàn)在呢,更多的時候是在用框架進行開發(fā),越來越覺得自己的JS基礎(chǔ)很缺乏,然后就開啟了“JS紅寶書”二刷之路。
下面就把書中自己覺得重要的、沒有掌握的知識整理出來。因為我覺得還是會三刷“JS紅寶書”,希望把這本700多頁的書越讀越薄,勉勵。
章節(jié)
- 事件
- 表單腳本
- HTML5腳本編程
- 錯誤處理與調(diào)試
- JSON
- Ajax 與 Comet
- 高級技巧
- 離線應(yīng)用與客戶端存儲
- 新型的API
事件
事件流
事件冒泡
IE的事件流叫做事件冒泡,即事件開始時由最具體的元素接受,然后逐級向上傳播到較為不具體的節(jié)點。
事件捕獲
Netscape 團隊提出的事件流叫做事件捕獲,事件捕獲的用意在于在事件到達預(yù)定目標之前捕獲它。
DOM事件流
“DOM2級事件”規(guī)定的事件流包括三個階段:事件捕獲階段
、處于目標階段
和事件冒泡階段
。
事件處理程序
DOM0 級事件處理程序
每個元素(包括window
和document
)都有自己的事件處理程序,這些屬性通常全部小寫。
var btn = document.getElementById('myBtn');
btn.onclick = function () {
console.log('clicked');
}
DOM 0級方法指定的事件處理程序被認為是元素的方法,因此,這個時候的事件處理程序是在元素的作用域中運行,也就是說程序中的this
可以引用當前元素。
var btn = document.getElementById('myBtn');
btn.onclick = function () {
console.log(this.id); // 'myBtn'
}
以這種方式添加的事件處理程序會在事件流的冒泡階段被處理。
DOM2 級事件處理程序
addEventListener()
removeEventListener()
定義了兩個方法用于處理指定和刪除事件處理程序的操作。所有的DOM節(jié)點中都包含這兩個方法,接受三個參數(shù):事件名、事件處理程序和布爾值。最后這個布爾值如果是true
,表示在捕獲階段調(diào)用事件處理程序;false
表示在冒泡階段調(diào)用事件處理程序,默認是false
。
通過addEventListener()
添加的事件處理程序只能使用removeEventListener()
來移除。如果通過addEventListener()
添加的匿名函數(shù)將無法移除。
btn.addEventListener('click', function () { //匿名函數(shù)
...
})
注:大多數(shù)情況下,都是將事件處理程序添加到事件流的冒泡階段(false
),這樣可以最大限度地兼容各種瀏覽器。最好只在需要在事件到達目標之前截獲它的時候?qū)⑹录幚沓绦蛱砑拥讲东@階段。如果不是特別需要,我們不建議在事件捕獲階段注冊事件處理程序。
IE事件處理程序
attachEvent()
detachEvent()
這兩個方法接受兩個參數(shù):事件名(帶on
)和事件處理函數(shù)。
var btn = document.getElementById("myBtn");
btn.attachEvent("onclick", function(){
alert("Clicked");
});
注:在IE 中使用attachEvent()
與使用DOM0 級方法的主要區(qū)別在于事件處理程序的作用域。
- DOM0 級作用域是其所屬元素
-
attachEvent()
方法的作用域是全局(this === window
)
var btn = document.getElementById("myBtn");
btn.attachEvent("onclick", function(){
alert("Clicked");
});
btn.attachEvent("onclick", function(){
alert("Hello world!");
});
注:與DOM方法不同的是,這些事件處理程序不是以添加它們的順序執(zhí)行,而是以相反的順序被觸發(fā)。單擊這個例子中的按鈕,首先看到的是"Hello world!",然后才是"Clicked"。
事件對象
在觸發(fā)DOM上的某個事件時,會產(chǎn)生一個事件對象event
。
DOM中的事件對象
event
對象成員
| 屬性/方法 | 類型 | 讀/寫 | 說明 |
| : - : | : -- : | : -- : | : -- : | : -- : |
| bubbles
| Boolean
| 只讀 | 表明事件是否冒泡 |
| cancelable
| Boolean
| 只讀 | 表明是否可以取消事件的默認行為 |
| currentTarget
| Element
| 只讀 | 其事件處理程序當前正在處理事件的那個元素 |
| defaultPrevented
| Boolean
| 只讀 | 為true
表示已經(jīng)調(diào)用preventDefault()
|
| detail
| Integer
| 只讀 | 與事件相關(guān)的細節(jié)信息 |
| eventPhase
| Integer
| 只讀 | 調(diào)用事件處理程序的階段:1 捕獲,2 處于目標,3 冒泡 |
| preventDefault()
| Function
| 只讀 | 取消事件的默認行為。如果cancelable
是true
,則可以使用這個方法 |
| stopImmediatePropagation()
| Function
| 只讀 | 取消事件的進一步冒泡或捕獲,同時阻止任何事件處理程序被調(diào)用 |
| stopPropagation()
| Function
| 只讀 | 取消事件的進一步捕獲或冒泡。如果bubbles
為true
,則可以使用這個方法 |
| target
| Element
| 只讀 | 事件的目標 |
| trusted
| Boolean
| 只讀 | 為true
表示事件是瀏覽器生成,false
是開發(fā)人員創(chuàng)建 |
| type
| String
| 只讀 | 被觸發(fā)的事件類型 |
| view
| AbstractView
| 只讀 | 與事件關(guān)聯(lián)的抽象視圖。等同于發(fā)生事件的window
對象 |
在事件處理程序內(nèi)部,對象this
始終等于currentTarget
的值,而target
則只包含事件的實際目標。如果直接將事件處理程序指定給了目標元素,則this
、currentTarget
和target
包含相同的值。
document.body.onclick = function(event){
alert(event.currentTarget === document.body); //true
alert(this === document.body); //true
alert(event.target === document.getElementById("myBtn")); //true
};
調(diào)用event
方法
var link = document.getElementById("myLink");
link.onclick = function(event){
event.preventDefault();
};
跨瀏覽器的事件對象
var EventUtil = {
addHandler: function(element, type, handler){
//省略的代碼
},
getEvent: function(event){
return event ? event : window.event;
},
getTarget: function(event){
return event.target || event.srcElement;
},
preventDefault: function(event){
if (event.preventDefault){
event.preventDefault();
} else {
event.returnValue = false;
}
},
removeHandler: function(element, type, handler){
//省略的代碼
},
stopPropagation: function(event){
if (event.stopPropagation){
event.stopPropagation();
} else {
event.cancelBubble = true;
}
}
};
事件類型
UI 事件
-
load
事件 -
unload
事件 -
resize
事件 -
scroll
事件
焦點事件
-
blur
事件:失去焦點 -
focus
事件:獲得焦點
鼠標與滾動事件
-
click
事件 -
dbclick
事件 -
mousedown
事件:按下鼠標 -
mouseenter
事件:光標移入 -
mouseleave
事件:光標移出 -
mousemove
事件:鼠標在元素內(nèi)部移動重復(fù)觸發(fā) -
mouseout
事件:在鼠標指針位于一個元素上方,然后用戶將其移入另一個元素時觸發(fā)。又移入的另一個元素可能位于前一個元素的外部,也可能是這個元素的子元素 -
mouseover
事件:在鼠標指針位于一個元素外部,然后用戶將其首次移入另一個元素邊界之內(nèi)時觸發(fā) -
mouseup
事件:釋放鼠標按鈕時觸發(fā)
頁面上的所有元素都支持鼠標事件。除了mouseenter
和mouseleave
,所有鼠標事件都會冒泡,也可以被取消,而取消鼠標事件將會影響瀏覽器的默認行為。
只有在同一個元素上相繼觸發(fā)mousedown
和mouseup
事件,才會觸發(fā)click
事件;如果mousedown
或mouseup
中的一個被取消,就不會觸發(fā)click
事件。
觸摸設(shè)備
iOS和Android設(shè)備的相關(guān)事件:
- 不支持
dbclick
事件。雙擊瀏覽器窗口會放大畫面 - 輕擊可單擊元素會觸發(fā)
mousemove
事件。。如果此操作會導致內(nèi)容變化,將不再有其他事件發(fā)生;如果屏幕沒有因此變化,那么會依次發(fā)生mousedown
、mouseup
和click
事件。輕擊不可單擊的元素不會觸發(fā)任何事件。可單擊的元素是指那些單擊可產(chǎn)生默認操作的元素(如鏈接),或者那些已經(jīng)被指定了onclick
事件處理程序的元素。 -
mousemove
事件也會觸發(fā)mouseover
和mouseout
事件 - 兩個手指放在屏幕上且頁面隨手指移動而滾動時會觸發(fā)
mousewheel
和scroll
事件。
HTML5事件
-
contextmenu
事件 -
beforeunload
事件 -
DOMContentLoaded
事件 -
readystatechange
事件-
uninitialized
未初始化 loading
loaded
-
interactive
:可以操作對象,但還沒有完全加載 complete
-
-
hashchange
事件
設(shè)備事件
-
orientationchange
事件:橫豎屏,有三個值: -90 ,0, 90
觸摸與手勢事件
- 觸摸事件
touchstart
touchmove
touchend
touchcancel
- 手勢事件
gesturestart
gesturechange
gestureend
內(nèi)存和性能
事件委托
例如在<ul>
為添加一個click
事件,所有<li>
子元素點擊事件都會冒泡到<ul>
上。
表單腳本
表單基礎(chǔ)知識
提交表單
<input type="submit" value="Submit Form">
重置表單
<input type="reset" value="Reset Form">
表單字段
每個表單都有elements
屬性,該屬性是表單中所有表單元素的集合。
var form = document.getElementById("form1");
//取得表單中的第一個字段
var field1 = form.elements[0];
//取得名為"textbox1"的字段
var field2 = form.elements["textbox1"];
//取得表單中包含的字段的數(shù)量
var fieldCount = form.elements.length;
文本框腳本
過濾輸入
屏蔽特定的字符,需要檢測keypress
事件對應(yīng)的字符編碼。
EventUtil.addHandler(textbox, 'keypress', function (event) {
event = EventUtil.getEvent(event);
var target = EventUtil.getTarget(event);
var charCode = EventUtil.getCharCode(event);
if (!/\d/.test(String.fromCharCode(charCode))) {
EventUtil.preventDefault(event);
}
})
HTML5約束驗證API
輸入模式
HTML5為文本字段新增了pattern
屬性。
<input type="text" pattern="\d+" name="count">
檢測有效性
使用checkValidity()
方法可以檢測表單中的某個字段是否有效。是否有效的判斷依據(jù)是一些<input>
的約束條件。
if (document.forms[0].elements[0].checkValidity()){
//字段有效,繼續(xù)
} else {
//字段無效
}
也可以檢測整個表單是否有效
if(document.forms[0].checkValidity()){
//表單有效,繼續(xù)
} else {
//表單無效
}
禁用驗證
<form method="post" action="signup.php" novalidate>
<!--這里插入表單元素-->
</form>
HTML5 腳本編程
跨文檔消息傳遞
跨文檔消息傳送(cross-document messaging)簡稱XDM。其核心方法是postMessage()
方法。
postMessage()
方法接受兩個參數(shù):一條消息和一個表示消息接收方來自哪個域的字符串。
// 注意:所有支持XDM的瀏覽器也支持iframe的`contentWindow`屬性
var iframeWindow = document.getElementById('myframe').contentWindow;
iframeWindow.postMessage('A secret', 'https://yeasoenzhang.github.io');
嘗試向內(nèi)嵌框架中發(fā)送一條消息,并指定框架中的文檔必須來源于https://yeasonzhang.github.io
域。
接收到XDM消息時,會觸發(fā)window
對象的message
事件,這個事件是以異步形式觸發(fā)。
傳遞的onmessage
處理程序的事件對象包含三個重要信息:
-
data
:作為postMessage()
第一個參數(shù)傳入的字符串數(shù)據(jù) -
origin
:發(fā)送消息的文檔所在的域。 -
source
:發(fā)送消息的文檔的window
對象的代理。
EventUtil.addHandler(window, "message", function(event){
//確保發(fā)送消息的域是已知的域
if (event.origin == "https://yeasonzhang.github.io"){
//處理接收到的數(shù)據(jù)
processMessage(event.data);
//可選:向來源窗口發(fā)送回執(zhí)
event.source.postMessage("Received!", "http://p2p.wrox.com");
}
});
XDM 還有一些怪異之處。首先,postMessage()
的第一個參數(shù)最早是作為“永遠都是字符串”來實現(xiàn)的。但后來這個參數(shù)的定義改了,改成允許傳入任何數(shù)據(jù)結(jié)構(gòu)。可是,并非所有瀏覽器都實現(xiàn)了這一變化。為保險起見,使用postMessage()
時,最好還是只傳字符串。如果你想傳入結(jié)構(gòu)化的數(shù)據(jù),最佳選擇是先在要傳入的數(shù)據(jù)上調(diào)用JSON.stringify()
,通過postMessage()
傳入得到的字符串,然后再在onmessage
事件處理程序中調(diào)用JSON.parse()
。
原生拖放
拖放事件
拖動某個元素時,將依次觸發(fā)的事件:
dragstart
drag
dragend
當某個元素被拖動到一個有效的放置目標時,會依次觸發(fā)下列事件:
dragenter
dragover
-
dragleave
(離開)或drag
(放進去了)
dataTransfer對象
dataTransfer
對象,它是事件對象的一個屬性,用于被拖動元素向放置目標傳遞字符串格式的數(shù)據(jù)。該對象有兩個主要方法:
getData()
setData()
//設(shè)置和接收文本數(shù)據(jù)
event.dataTransfer.setData("text", "some text");
var text = event.dataTransfer.getData("text");
//設(shè)置和接收URL
event.dataTransfer.setData("URL", "http://www.wrox.com/");
var url = event.dataTransfer.getData("URL");
不過,保存在dataTransfer
對象中的數(shù)據(jù)只能在drap
事件處理程序中讀取。如果在ondrop
處理程序中沒有讀到數(shù)據(jù),那就是dataTransfer
對象已經(jīng)被銷毀,數(shù)據(jù)也丟失了。
drapEffect 與 effectAllowed
dateTransfer
對象有兩個屬性:
dropEffect
effectAllowed
dropEffect
,屬性可以知道被拖動的元素能夠執(zhí)行那種放置行為。
-
none
:不能放在這里 -
move
:應(yīng)該把拖放的元素移動到放置目標 -
copy
:應(yīng)該把拖動的元素復(fù)制到放置目標 -
link
:表示放置目標會打開拖動的元素
要使用dropEffect
屬性,必須在ondragenter
事件處理程序中針對放置目標來設(shè)置。
effectAllowed
屬性表示允許拖動元素的哪種dropEffect
。
-
uninitialized
:沒有給被拖動的元素放置任何放置行為 -
none
:被拖動的元素不能有任何行為 -
copy
:只允許值為copy
的dropEffect
-
link
:只允許值為link
的dropEffect
-
move
:只允許值為move
的dropEffect
-
copyLink
:允許值為copy
和link
的dropEffect
-
copyMove
:允許值為copy
和move
的dropEffect
-
linkMove
:允許值為link
和move
的dropEffect
-
all
: 允許任意dropEffect
必須在ondragstart
事件處理程序中設(shè)置effectAllowed
屬性。
可拖動
HTML5為所有元素規(guī)定了draggable
屬性,表示元素是否可以拖動。只有圖像和鏈接的draggable
默認是true
<!-- 讓這個圖像不可以拖動 -->

<!-- 讓這個元素可以拖動 -->
<div draggable="true">...</div>
其他成員
HTML5規(guī)定了dateTransfer
對象還應(yīng)該包含下列方法和屬性。
addElement(element)
clearData(format)
setDragImage(element, x, y)
type
錯誤處理與調(diào)試
錯誤處理
try-catch 語句
try {
// 可能出錯的代碼
} catch (err) {
// 處理發(fā)生的錯誤
}
finally
子句
只要代碼中包含finially
子句,無論try
還是catch
語句中的return
語句都將被忽略。
錯誤類型
Error
EvalError
RangeError
ReferenceError
SyntaxError
TypeError
URIError
try {
someFunction();
} catch (error) {
if (error instanceof TypeError) {
//...
} else {
//
}
}
拋出錯誤
與try-catch
語句相配的還有一個throw
操作符,用于隨時拋出自定義錯誤。拋出錯誤時,必須要給throw
操作符指定一個值,這個值是什么類型,沒有要求。
throw 12345;
throw "Hello world!";
throw true;
throw { name: "JavaScript"};
遇到throw
操作符時,代碼會立即停止執(zhí)行。只有當try-catch
語句捕獲到被拋出值,代碼才會繼續(xù)執(zhí)行
自定義錯誤類型
可以利用原型鏈通過繼承Error
創(chuàng)建自定義錯誤類型。需要為新創(chuàng)建的錯誤類型指定name
和message
屬性
function CustomError (message) {
this.name = 'CustomError';
this.message = message;
}
CustomError.prototype = new Error();
throw new CustomError('Error msg');
Error事件
任何沒有通過try-catch
處理的錯誤都會觸發(fā)window
對象的error
事件。
在任何Web瀏覽器中,onerror
事件處理程序都不會創(chuàng)建event
對象,但它可以接受三個參數(shù):錯誤消息、錯誤所在的URL和行號。
要指定onerror 事件處理程序,必須使用如下所示的DOM0 級技術(shù),它沒有遵循“DOM2 級事件”的標準格式(addEventListener
)。
window.onerror = function(message, url, line){
alert(message);
};
只要發(fā)生錯誤,無論是不是瀏覽器生成的,都會觸發(fā)error
事件,然后讓瀏覽器的默認機制發(fā)揮作用,這時候我們需要阻止瀏覽器的默認行為(return false
)。
window.onerror = function (message, url, line) {
console.log(message);
retrun false;
}
常見的錯誤類型
- 類型轉(zhuǎn)換錯誤
- 數(shù)據(jù)類型錯誤
- 通信錯誤
在數(shù)據(jù)檢測的時候,基本類型的值應(yīng)該使用typeof
來檢測,對象的值應(yīng)該使用instanceof
。
JSON
解析與序列化
JSON對象
JSON
對象有兩個方法:stringify
和parse()
。
var book = {
title: "Professional JavaScript",
authors: [
"Nicholas C. Zakas"
],
edition: 3,
year: 2011
};
var jsonText = JSON.stringify(book);
以上就把Javascript
對象序列化為一個JSON
字符串(沒有空格和縮進)
{"title":"Professional JavaScript","authors":["Nicholas C. Zakas"],"edition":3,"year":2011}
如果傳給JSON.parse()
的字符串不是有效的JSON
,會拋出錯誤。
序列化選項
JSON.stringify()
除了要序列化的JS
對象外,還可以接受兩個參數(shù),一個是過濾器(數(shù)組或函數(shù)),第二個參數(shù)是一個選項,表示是都在JSON
字符串中保留縮進。
過濾結(jié)果
var book = {
"title": "Professional JavaScript",
"authors": [
"Nicholas C. Zakas"
],
edition: 3,
year: 2011
};
var jsonText = JSON.stringify(book, ["title", "edition"]);
第二個參數(shù)中包含兩個字符串"title", "edition"
,所以只會返回對應(yīng)的屬性
{"title":"Professional JavaScript","edition":3}
過濾器為函數(shù)
var book = {
"title": "Professional JavaScript",
"authors": [
"Nicholas C. Zakas"
],
edition: 3,
year: 2011
};
var jsonText = JSON.stringify(book, function(key, value){
switch(key){
case "authors":
return value.join(",")
case "year":
return 5000;
case "edition":
return undefined;
default:
return value;
}
});
注:返回undefined
刪除該屬性,上例的edition
屬性就會被刪除。
字符串縮進
JSON.stringify()
方法的第三個參數(shù)用于控制結(jié)果中的縮進和空白符。可以是數(shù)字,表示縮進的空格數(shù);也可以是字符串,將該字符串作為縮進的表示。
toJSON()
方法
解析選項
JSON.parse()
方法也可以接受第二參數(shù),該參數(shù)是一個函數(shù)(被稱為還原函數(shù)),傳入函數(shù)的參數(shù)均為key, value
。
如果還原函數(shù)返回undefined
,則表示要從結(jié)果中刪除響應(yīng)的鍵。
Ajax與Comet
XMLHttpRequest 對象
XHR的用法
-
open('method', 'url', bool)
:第三個參數(shù)表示是否異步發(fā)送 -
send()
:接受一個參數(shù)作為請求主體發(fā)送的數(shù)據(jù),如果不需要則傳入null
XHR
對象的屬性
-
responseText
:作為相應(yīng)主體被返回的文本 -
responseXML
:如果相應(yīng)的內(nèi)容類型是"text/xml"
或"application/xml"
,這個屬性中將包含這響應(yīng)數(shù)據(jù)的XML DOM文檔 -
status
:響應(yīng)的HTTP狀態(tài) -
statusText
:HTTP狀態(tài)的說明
同步請求
xhr.open("get", "example.txt", false);
xhr.send(null);
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){
alert(xhr.responseText);
} else {
alert("Request was unsuccessful: " + xhr.status);
}
-
readyState
:表示請求/響應(yīng)過程的階段- 0:未初始化,尚未調(diào)用
open()
方法 - 1:啟動,調(diào)用了
open()
方法,尚未調(diào)用send()
方法 - 2:發(fā)送,調(diào)用了
send()
方法,尚未接收到響應(yīng)。 - 3:接收,接收到部分響應(yīng)數(shù)據(jù)
- 4:完成,已經(jīng)接收到全部響應(yīng)數(shù)據(jù)
- 0:未初始化,尚未調(diào)用
var xhr = createXHR();
xhr.onreadystatechange = function(){
if (xhr.readyState == 4){
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){
alert(xhr.responseText);
} else {
alert("Request was unsuccessful: " + xhr.status);
}
}
};
xhr.open("get", "example.txt", true);
xhr.send(null);
-
abort()
:在接收到響應(yīng)之前通過該方法取消異步請求。
建議調(diào)用這個方法之后,對XHR
對象進行解引用操作。
HTTP 頭部信息
默認情況下,在發(fā)送XHR
請求的同時,還會發(fā)送下列頭部信息:
-
Accept
:瀏覽器能夠處理的內(nèi)容類型 -
Accept-Charset
:瀏覽器能夠顯示的字符集 -
Accept-Encoding
:瀏覽器能夠處理的壓縮編碼 -
Accept-Language
:瀏覽器當前設(shè)置的語言 -
Connection
:瀏覽器與服務(wù)器之間連接的類型 -
Cookie
: 當前頁面的 Cookie -
Host
:發(fā)出請求的頁面所在的域 -
Referer
:發(fā)出請求的頁面的URI -
User-Agent
:瀏覽器的用戶代理
自定義請求頭部信息,使用setRequestHeader()
方法,該方法接受兩個參數(shù):頭部字段的名稱和頭部字段的值。
要成功發(fā)送請求頭部信息,必須在調(diào)用open()
方法之后且調(diào)用send()
方法之前調(diào)用serRequestHeader()
。
var xhr = createXHR();
xhr.onreadystatechange = function(){
// ...
};
xhr.open("get", "example.php", true);
xhr.setRequestHeader("MyHeader", "MyValue");
xhr.send(null);
注建議使用自定義的頭部字段名稱,不要使用瀏覽器正常發(fā)送的字段名稱,否則有可能會影響服務(wù)器的響應(yīng)。有的瀏覽器允許開發(fā)人員重寫默認的頭部信息,但有的瀏覽器則不允許這樣做。
調(diào)用XHR
對象的getResponseHeader()
方法,接受一個參數(shù):頭部字段名稱。就能取得相應(yīng)的響應(yīng)頭部信息。
調(diào)用getAllResponseHeaders()
方法可以取得包含所有頭部信息的字符串。
GET請求
使用GET
請求經(jīng)常會發(fā)生一個錯誤,就是查詢字符串的格式有問題。查詢字符串中每個參數(shù)的名稱和值都必須使用encodeURIComponent()
進行編碼,然后才能放到URL的末尾。
function addURLParam(url, name, value) {
url += (url.indexOf("?") == -1 ? "?" : "&");
url += encodeURIComponent(name) + "=" + encodeURIComponent(value);
return url;
}
var url = "example.php";
//添加參數(shù)
url = addURLParam(url, "name", "Nicholas");
url = addURLParam(url, "book", "Professional JavaScript");
//初始化請求
xhr.open("get", url, false);
POST請求
如果我們希望用XHR模仿表單提交,需要將Content-Type
頭部信息設(shè)置為application/x-www-form-urlencoded
(表單提交的內(nèi)容類型)
function submitData(){
var xhr = createXHR();
xhr.onreadystatechange = function(){
if (xhr.readyState == 4){
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){
alert(xhr.responseText);
} else {
alert("Request was unsuccessful: " + xhr.status);
}
}
};
xhr.open("post", "postexample.php", true);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
var form = document.getElementById("user-info");
xhr.send(serialize(form));
}
XMLHttpRequest 2 級
FormData
FormData
為序列化表單以及創(chuàng)建于表單格式相同的數(shù)據(jù)提供了便利。
var data = new FormData();
data.append('name', 'Yeaseon');
append
方法可以將表單的字段和值,傳入FormData
對象中。也可以預(yù)先填入表單中的字段:
var data = new FormData(document.form[0]);
FormData
的方便就在于不必手動修改XHR對象的請求頭部。
超時設(shè)定
XHR
對象添加了一個timeout
屬性,表示請求在等待多少毫秒之后終止。如果規(guī)定時間內(nèi)瀏覽器沒有收到響應(yīng),就會觸發(fā)timeout
事件,進而調(diào)用ontimeout
事件處理程序。
var xhr = createXHR();
xhr.onreadystatechange = function(){
// ...
};
xhr.open("get", "timeout.php", true);
xhr.timeout = 1000; //將超時設(shè)置為1 秒鐘
xhr.ontimeout = function(){
alert("Request did not return in a second.");
};
xhr.send(null);
超時之后請求終止,但是此時的readyState
可能已經(jīng)變?yōu)榱?code>4,就意味著會調(diào)用onreadystatechange
事件。
可是,如果在超時終止請求之后再訪問status
屬性,就會導致錯誤。為避免瀏覽器報告錯誤,可以將檢查status
屬性的語句封裝在一個try-catch
語句當中。
overrideMimeType()方法
用于重寫XHR
響應(yīng)的MIME
類型。因為返回響應(yīng)的MIME
類型決定了XHR
對象如何處理它,所以提供一種方法能夠重寫服務(wù)器返回的MIME
類型是很有用的。
var xhr = createXHR();
xhr.open("get", "text.php", true);
xhr.overrideMimeType("text/xml");
xhr.send(null);
進度事件
有以下6個進度事件:
-
loadstart
:在接收到響應(yīng)數(shù)據(jù)的第一個字節(jié)觸發(fā) -
progress
:在接收響應(yīng)期間持續(xù)不斷地觸發(fā) -
error
:在請求發(fā)生錯誤時觸發(fā) -
abort
:在因為調(diào)用abort()
方法而終止連接時觸發(fā) -
load
:在接收到完整的響應(yīng)數(shù)據(jù)時觸發(fā) -
loadend
:在通信完成或者觸發(fā)error
、abort
,或load
事件后觸發(fā)
progress事件
onprogress
事件處理程序會接收到一個event
對象,target
屬性指向XHR
對象,包含著三個額外的屬性:
-
lengthComputable
:表示進度信息是否可用的布爾值 -
position
:表示已經(jīng)接受的字節(jié)數(shù) -
totalSize
:表示根據(jù)Content-Length
響應(yīng)頭部確定的預(yù)期字節(jié)數(shù)。
跨資源共享
IE對CORS的實現(xiàn)
微軟在IE8中引入了XDR
類型,類似與XHR
對象,兩者的不同之處:
- cookie不會隨請求發(fā)送,也不會隨響應(yīng)返回
- 只能設(shè)置請求頭部信息中的
Content-Type
字段 - 不能訪問響應(yīng)頭部信息
- 只支持
GET
和POST
請求
請求返回之后,就會觸發(fā)load
事件,響應(yīng)數(shù)據(jù)也會保存在responseText
屬性中:
var xdr = new XDomainRequest();
xdr.onload = function () {
console.log(xdr.responseText);
};
xdr.onerror = function(){
alert("An error occurred.");
};
xdr.open('get', 'http://..../xxx/');
xdr.send(null);
在請求返回之前可以調(diào)用abort()
方法終止請求。
xdr.abort();
XDR
對象也支持timeout
屬性以及ontimeout
事件處理程序
var xdr = new XDomainRequest();
xdr.onload = function(){
alert(xdr.responseText);
};
xdr.onerror = function(){
alert("An error occurred.");
};
xdr.timeout = 1000;
xdr.ontimeout = function(){
alert("Request took too long.");
};
xdr.open("get", "http://www.somewhere-else.com/page/");
xdr.send(null);
為了支持POST
請求,XDR
對象提供了contentType
屬性,用來表示發(fā)送數(shù)據(jù)的格式。
var xdr = new XDomainRequest();
xdr.onload = function () {
//
}
xdr.onerror = function () {
//
}
xdr.open('post', 'http://www.somewhere-else.com/page/');
xdr.contentType = 'application/x-www-form-urlencoded';
xdr.send('name1=value1&name2=value2');
其他瀏覽器對CORS的實現(xiàn)
與IE中的XDR
對象不同,通過跨域XHR
對象可以訪問status
和statusText
屬性,并且支持同步請求。同時也有一些限制:
- 不能使用
setRequestHeader()
設(shè)置自定義頭部 - 不能發(fā)送和接收
cookie
- 調(diào)用
getAllResponseHeaders()
方法總會返回空字符串
var xhr = createXHR();
xhr.onreadystatechange = function(){
if (xhr.readyState == 4){
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){
alert(xhr.responseText);
} else {
alert("Request was unsuccessful: " + xhr.status);
}
}
};
xhr.open("get", "http://www.somewhere-else.com/page/", true);
xhr.send(null);
其他跨域技術(shù)
圖像Ping
var img = new Image();
img.onload = img.onerror = function(){
alert("Done!");
};
img.src = "http://www.example.com/test?name=Nicholas";
JSONP
JSONP
是JSON with padding的簡寫。JSONP
只不過時被包含在函數(shù)調(diào)用中的JSON
:
callback({"name": "Yeaseon"});
JSONP
由兩部分組成:回調(diào)函數(shù)和數(shù)據(jù)。回調(diào)函數(shù)是當響應(yīng)到來時應(yīng)該在頁面中調(diào)用的函數(shù)。回調(diào)函數(shù)的名字一般是請求中指定的。下面是一個經(jīng)典的JSONP
請求
http://freegeoip.net/json/?callback=handleResponse
這里指定的回調(diào)函數(shù)的名字叫做handleResponse
。
JSONP
是通過動態(tài)<script>
元素來使用的,使用時可以為src
屬性指定一個跨域URL
。
function handleResponse(response){
alert("You’re at IP address " + response.ip + ", which is in " +
response.city + ", " + response.region_name);
}
var script = document.createElement("script");
script.src = "http://freegeoip.net/json/?callback=handleResponse";
document.body.insertBefore(script, document.body.firstChild);
服務(wù)器發(fā)送事件
SSE支持短輪詢、長輪訓和HTTP流,而且能在斷開連接時自動確定何時重新連接。
SSE API
要預(yù)訂新的事件流,首先要創(chuàng)建一個新的EventSource
對象,并傳入一個入口點:
var source = new EventSource('myevents.php');
傳入的URL必須與創(chuàng)建對象的頁面同源。
EventSource
的實例有一個readyState
屬性:0
表示正連接到服務(wù)器,1
表示打開了連接,2
表示關(guān)閉了連接。
EventSource
實例還有三個事件:
-
open
:在建立連接時觸發(fā) -
message
:在從服務(wù)器接收到新事件時觸發(fā) -
error
:在無法建立連接時觸發(fā)
服務(wù)器發(fā)回的數(shù)據(jù)以字符串形式保存在event.data
中。
默認情況下,EventSource
對象會保持與服務(wù)器的活動連接。如果想強制立即斷開連接并且不在重新連接,可以調(diào)用close()
方法。
Web Sockets
由于 Web Sockets 使用了自定義的協(xié)議,所以 URL 模式也略有不同。未加密的連接不再是 http:// ,而是 ws:// ;加密的連接也不是 https:// ,而是 wss:// 。
Web Sockets API
創(chuàng)建一個WebSockets實例對象:
var socket = new WebSocket("ws://www.example.com/server.php");
WebSocket也有一個表示當前狀態(tài)的readyState
屬性:
-
WebSocket.OPENING (0)
:正在建立連接 -
WebSocket.OPEN (1)
:已經(jīng)建立連接 -
WebSocket.CLOSING (2)
:正在關(guān)閉連接 -
WebSocket.CLOSE (3)
:已經(jīng)關(guān)閉連接
發(fā)送和接收數(shù)據(jù)
向服務(wù)器發(fā)送數(shù)據(jù),使用send()
方法并傳入任意字符串:
var socket = new WebSocket('ws:// www.example.com/server.php');
socket.send('Hello World');
Web Sockets只能發(fā)送純文本數(shù)據(jù),對于復(fù)雜的數(shù)據(jù)結(jié)構(gòu),在發(fā)送之前,必須進行序列化。
var message = {
time: new Date(),
text: 'Hello world',
clientId: 'adfalsf39'
};
socket.send(JSON.stringify(message));
當服務(wù)器向客戶端發(fā)來消息時,WebSocket對象就會觸發(fā)message
事件。這個message
事件與其他傳遞消息的協(xié)議類似,也是把返回的數(shù)據(jù)保存在event.data
屬性中。
socket.onmessage = function (event) {
var data = event.data;
// ....
}
與send()
類似,event.data
中返回的數(shù)據(jù)也是字符串,需要手工解析這些數(shù)據(jù)。
其他事件
WebSocket對象還有其他三個事件,在連接生命周期的不同階段觸發(fā):
-
open
:在成功建立連接時觸發(fā) -
error
:在發(fā)生錯誤時觸發(fā),連接不能持續(xù) -
close
:在連接關(guān)閉時觸發(fā)
WebSocked對象不支持DOM 2級事件監(jiān)聽:
var socket = new WebSocket("ws://www.example.com/server.php");
socket.onopen = function(){
alert("Connection established.");
};
socket.onerror = function(){
alert("Connection error.");
};
socket.onclose = function(){
alert("Connection closed.");
};
高級技巧
高級函數(shù)
安全的類型檢測
用于區(qū)分原生和非原生JavaScript對象,通過Object.prototype.toString()
。
function isArray(value){
return Object.prototype.toString.call(value) == "[object Array]";
}
function isFunction(value){
return Object.prototype.toString.call(value) == "[object Function]";
}
function isRegExp(value){
return Object.prototype.toString.call(value) == "[object RegExp]";
}
作用域安全的構(gòu)造函數(shù)
防止構(gòu)造函數(shù)內(nèi)this
指針的指向被改變(指向window
)
function Person (name, age, job) {
if (this instanceof Person) {
this.name = name;
this.age = age;
this.job = job;
} else {
return new Person(name, age, job);
}
}
惰性載入函數(shù)
function createXHR(){
if (typeof XMLHttpRequest != "undefined"){
return new XMLHttpRequest();
} else if (typeof ActiveXObject != "undefined"){
if (typeof arguments.callee.activeXString != "string"){
var versions = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0", "MSXML2.XMLHttp"],
i,len;
for (i=0,len=versions.length; i < len; i++){
try {
new ActiveXObject(versions[i]);
arguments.callee.activeXString = versions[i];
break;
} catch (ex){
//跳過
}
}
}
return new ActiveXObject(arguments.callee.activeXString);
} else {
throw new Error("No XHR object available.");
}
}
第一種改法:
function createXHR () {
if (typeof XMLHttpRequest != 'undefined') {
createXHR = function () {
return new XMLHttpRequest();
};
} else if (typeof ActiveXObjext != 'undefined') {
createXHR = function () {
if (typeof arguments.callee.activeXString != 'string') {
var versions = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0", "MSXML2.XMLHttp"],
i,len;
for (i = 0; len = versions.length; i < len; i++) {
try {
new ActiveXObject(versions[i]);
arguments.callee.activeXString = versions[i];
break;
} catch (e) {
// skip
}
}
}
return new ActiveXObject(arguments.callee.activeXString);
};
} else {
createXHR = function () {
throw new Error('No XHR object available.');
}
}
return createXHR();
}
第二種改法:
var createXHR = (function () {
if (typeof XMLHttpRequest != 'undefined') {
return function () {
return new XMLHttpRequest();
};
} else if (typeof ActiveXObjext != 'undefined') {
return function () {
if (typeof arguments.callee.activeXString != 'string') {
var versions = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0", "MSXML2.XMLHttp"],
i,len;
for (i = 0; len = versions.length; i < len; i++) {
try {
new ActiveXObject(versions[i]);
arguments.callee.activeXString = versions[i];
break;
} catch (e) {
// skip
}
}
}
return new ActiveXObject(arguments.callee.activeXString);
};
} else {
return function () {
throw new Error('No XHR object available.');
}
}
})();
函數(shù)綁定
bind()
函數(shù),語法如下:
function bind (fn, context) {
return function () {
return fn.apply(context, arguments);
}
}
離線應(yīng)用與客戶端存儲
離線檢測
navigator.onLine
屬性可以判斷設(shè)備否能訪問網(wǎng)絡(luò)。
HTML5定義兩個事件:online
和offline
,當網(wǎng)絡(luò)狀態(tài)變化時,分別觸發(fā)這兩個事件:
EventUtil.addHandler(window, 'online', function () {
console.log('online');
});
EventUtil.addHandler(window, 'offline', function () {
console.log('offline');
});
數(shù)據(jù)存儲
Web存儲機制
Web Storage規(guī)范包含兩種對象的定義:sessionStorage
和globalStorage
。這兩個對象在支持的瀏覽器中都是以windows
對象屬性的形式存在。
Storage
類型
Storage
類型提供最大的存儲空間來存儲名值對。
-
clear()
:刪除所有值 -
getItem(name)
:根據(jù)指定的名字name
獲取對應(yīng)的值 -
key(index)
:獲得index
位置處的值的名字 -
removeItem(name)
:刪除由name
指定的名值對 -
setItem(name, value)
:為指定的name
設(shè)置一個對應(yīng)的值
sessionStorage
對象
sessionStorage
對象存儲特定于某個會話的數(shù)據(jù),也就是該數(shù)據(jù)只保持到瀏覽器關(guān)閉。存儲在sessionStorage
中的數(shù)據(jù)可以跨越頁面刷新而存在。
//使用方法存儲數(shù)據(jù)
sessionStorage.setItem("name", "Nicholas");
//使用屬性存儲數(shù)據(jù)
sessionStorage.book = "Professional JavaScript";
//使用方法讀取數(shù)據(jù)
var name = sessionStorage.getItem("name");
//使用屬性讀取數(shù)據(jù)
var book = sessionStorage.book;
//使用delete 刪除一個值——在WebKit 中無效
delete sessionStorage.name;
//使用方法刪除一個值
sessionStorage.removeItem("book");
可以通過結(jié)合length
屬性和key()
方法來迭代sessionStorage
中的值:
for (var i = 0, len = sessionStorage.length; i < len; i++) {
var key = sessionStorage.key(i);
var value = sessionStorage.getItem(key);
console.log(key + ' = ' + value);
}
還可以使用for-in
循環(huán)來迭代sessionStorage
中的值:
for (var key in sessionStorage) {
var value = sessionStorage.getItem(key);
console.log(key + ' = ' + value);
}
globalStorage
對象
這個對象的目的是跨越會話存儲數(shù)據(jù),,但有特定的訪問限制。要使用globalStorage
,首先要指定哪些域可以訪問該數(shù)據(jù)。
// 保存數(shù)據(jù)
globalStorage['wrox.com'].name = 'Yeaseon';
// 獲取數(shù)據(jù)
var name = globalStorage['wrox.com'].name;
上例,訪問的是針對域名wrox.com
的存儲空間。globalStorage
對象不是Storage
的實例,
而具體的globalStorage['wrox.com']
才是。這個存儲空間對于wrox.com
及其所有子域都是可以訪問的。
globalStorage["www.wrox.com"].name = "Yeaseon";
globalStorage["www.wrox.com"].book = "Professional JavaScript";
globalStorage["www.wrox.com"].removeItem("name");
var book = globalStorage["www.wrox.com"].getItem("book");
localStorage
對象
localStorage
對象是HTML5規(guī)范中作為持久保存客戶端數(shù)據(jù)的方案,并且取代globalStorage
。要訪問同一個localStorage
對象,頁面必須來自同一個域名(子域名無效),必須同源。
//使用方法存儲數(shù)據(jù)
localStorage.setItem("name", "Nicholas");
//使用屬性存儲數(shù)據(jù)
localStorage.book = "Professional JavaScript";
//使用方法讀取數(shù)據(jù)
var name = localStorage.getItem("name");
//使用屬性讀取數(shù)據(jù)
var book = localStorage.book;
storage
事件
對Storage
對象進行任何修改,都會在文檔上觸發(fā)storage
事件。這個事件的event
對象有以下屬性。
-
domain
:發(fā)生變化的存儲空間的域名 -
key
:設(shè)置或刪除的鍵名 -
newValue
:如果是設(shè)置值,則是新值;如果是刪除鍵,則是null
-
oldValue
:鍵被更改之前的值
在這四個屬性中,IE8 和Firefox 只實現(xiàn)了domain
屬性。在撰寫本書的時候,WebKit 尚不支持
storage
事件
IndexedDB
Indexed Database API,簡稱IndexedDB,是在瀏覽器中保存結(jié)構(gòu)化數(shù)據(jù)的一種數(shù)據(jù)庫。IndexedDB設(shè)計的操作完全是異步進行。
var indexedDB = window.indexedDB || window.msIndexedDB || window.mozIndexedDB || window.webkitIndexedDB;
數(shù)據(jù)庫
IndexedDB就是一個數(shù)據(jù)庫,它最大的特色就是使用對象保存數(shù)據(jù),而不是使用表來保存數(shù)據(jù)。
indexDB.open()
,傳入一個數(shù)據(jù)庫參數(shù)。如果該數(shù)據(jù)庫存在就會發(fā)送一個打開它的請求;如果該數(shù)據(jù)庫不存在,就會發(fā)送一個創(chuàng)建并打開它的請求。請求會返回一個IDBRequest
對象,這個對象上可以添加onerror
和onsuccess
事件處理程序。
var request, database;
request = indexedDB.open('admin');
request.onerror = function (event) {
console.log(event.target.errorCode);
};
request.onsuccess = function (event) {
database = event.target.result;
};
event.target
都指向request
對象,因此他們可以互換使用。
發(fā)生錯誤了,event.target.errorCode
中將會保存一個錯誤碼:
-
IDBDatebaseException.UNKNOWN_ERR(1)
:意外錯誤 -
IDBDatebaseException.NON_TRANSIENT_ERR(2)
:操作不合法 -
IDBDatebaseException.NOT_FOUND_ERR(3)
:未發(fā)現(xiàn)要操作的數(shù)據(jù)庫 -
IDBDatebaseException.CONSTRAINT_ERR(4)
:違反了數(shù)據(jù)庫約束 -
IDBDatebaseException.DATA_ERR(5)
:提供給事務(wù)的數(shù)據(jù)不能滿足要求 -
IDBDatebaseException.NOT_ALLOWED_ERR(6)
:操作不合法 -
IDBDatebaseException.TRANSACTION_INACTIVE_ERR(7)
:試圖重用已完成的事務(wù) -
IDBDatebaseException.ABORT_ERR(8)
:請求中斷 -
IDBDatebaseException.READ_ONLY_ERR(9)
:試圖在只讀模式下寫入或修改數(shù)據(jù) -
IDBDatebaseException.TIMEOUT_ERR(10)
:在有效時間內(nèi)未完成操作 -
IDBDatebaseException.QUOTA_ERR(11)
:磁盤空間不足
指定數(shù)據(jù)庫版本號,通過setVersion()
方法:
if (database.version != '1.0') {
request = database.setVersion('1.0');
request.onerror = function (event) {
console.log(event.target.errorCode);
};
request.onsuccess = function (event) {
console.log(''Database name: ' + database.name + ', Version: ' + database.version);
}
} else {
console.log(''Database name: ' + database.name + ', Version: ' + database.version);
}
對象存儲空間
假設(shè)要保存的用戶記錄由用戶名、密碼等組成,那么保存一條記錄的對象應(yīng)該類似:
var user = {
username: '007',
firstname: 'James',
lastname: 'Bond',
password: 'foo'
}
如果使用username
屬性作為這個對象存儲空間的鍵,這個username
必須全局唯一,而且大部分時候都要通過這個鍵來訪問數(shù)據(jù)。
var store = db.createObjectStore('users', { keyPath: 'username' });
其中第二個參數(shù)中的keyPath
屬性,就是空間中將要保存的對象的一個屬性,而這個屬性將作為存儲空間的鍵來使用。
通過add()
或put()
方法來向存儲空間添加數(shù)據(jù)。著兩個方法都接收一個參數(shù),就是要保存的對象。
//users 中保存著一批用戶對象
var i=0,
request,
requests = [],
len = users.length;
while(i < len){
request = store.add(users[i++]);
request.onerror = function(){
//處理錯誤
};
request.onsuccess = function(){
//處理成功
};
requests.push(request);
}
事務(wù)
在數(shù)據(jù)庫對象上調(diào)用transaction()
可以創(chuàng)建事務(wù),任何時候,只要想讀取或修改數(shù)據(jù),都要通過事務(wù)來組織所有操作。
// 創(chuàng)建事務(wù)
var transaction = db.transaction();
可以傳入要訪問的一或多個對象存儲空間。
var transaction = db.transaction('users');
var transaction = db.transaction(['users', 'anotherStore']);
前面這些事務(wù)都是以只讀方式訪問數(shù)據(jù)。要修改訪問方式,必須在創(chuàng)建事務(wù)時傳入第二個參數(shù),這個參數(shù)表示訪問模式:
-
IDBTransaction.READ_ONLY(0)
:只讀 -
IDBTransaction.READ_WRITE(1)
:讀寫 -
IDBTransaction.VERSION_CHANGE(2)
:改變
在Chrome中叫做webkitIDBTransaction
,可以使用一下代碼兼容:
var IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction;
這樣就能方便的指定transaction()
第二個參數(shù):
var transaction = db.transaction('users', IDBTransaction.READ_WRITE);
取得事務(wù)的索引后,使用objectStore()
方法并傳入存儲空間的名稱,就可以訪問指定的存儲空間。然后通過如下方法操作對象:
add()
put()
get()
delete()
clear()
var request = db.transaction('users').objectStore('users').get('007');
request.onerror = function (event) {
console.log('Did not get the object!');
};
request.onsuccess = function (event) {
var result = event.target.result;
console.log(result.firstName); // 'James'
};
也可以針對事務(wù)對象本身進行事件處理,存在兩個事件onerror
,oncomplete
:
transaction.onerror = function (event) {
// 整個事務(wù)都被取消了
}
transaction.oncomplete = function (event) {
// 整個事務(wù)都成功完成了
}
注:在oncomplete
事件的事件對象中訪問不到get()
請求返回的數(shù)據(jù),必須在onsuccess
事件中處理。
鍵范圍
var IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange;
有四種定義鍵范圍的方法:
-
only()
:取得指定對象的鍵 -
lowerBound()
:指定結(jié)果集的下界 -
upperBound()
:指定結(jié)果集的上界 -
bound()
:同時指定上、下界
var onlyRange = IDBKeyRange.only("007");
//從鍵為"007"的對象開始,然后可以移動到最后
var lowerRange = IDBKeyRange.lowerBound("007");
//從鍵為"007"的對象的下一個對象開始,然后可以移動到最后
var lowerRange = IDBKeyRange.lowerBound("007", true);
//從頭開始,到鍵為"ace"的對象為止
var upperRange = IDBKeyRange.upperBound("ace");
//從頭開始,到鍵為"ace"的對象的上一個對象為止
var upperRange = IDBKeyRange.upperBound("ace", true);
//從鍵為"007"的對象開始,到鍵為"ace"的對象為止
var boundRange = IDBKeyRange.bound("007", "ace");
//從鍵為"007"的對象的下一個對象開始,到鍵為"ace"的對象為止
var boundRange = IDBKeyRange.bound("007", "ace", true);
//從鍵為"007"的對象的下一個對象開始,到鍵為"ace"的對象的上一個對象為止
var boundRange = IDBKeyRange.bound("007", "ace", true, true);
//從鍵為"007"的對象開始,到鍵為"ace"的對象的上一個對象為止
var boundRange = IDBKeyRange.bound("007", "ace", false, true);
新型的API
Page Visibility API
Page Visibility API 是為了讓開發(fā)人員知道頁面是否對用戶可見推出的。
-
document.hidden
:表示頁面是否隱藏的布爾值。 -
document.visibilityState
- 頁面在后臺標簽頁中或瀏覽器最小化
- 頁面在前臺標簽頁中
- 實際的頁面已經(jīng)隱藏,但用戶可以看到頁面的預(yù)覽
- 頁面在屏幕外執(zhí)行預(yù)渲染處理
-
visibilitychange
事件:當文檔可見性發(fā)生改變時,觸發(fā)該事件。
Geolocation API
Geolocation API 在瀏覽器中的實現(xiàn)是navigator.geolocation
對象。
getCurrentPosition()
調(diào)用這個方法就會觸發(fā)請求用戶共享地理定位信息的對話框。這個方法接收三個參數(shù):成功回調(diào),可選的失敗回調(diào)和可選的選項對象。
成功回調(diào)
會接收到一個Position
對象參數(shù),該對象有兩個屬性:coords
和timestamp
。
coords
對象中包含于位置相關(guān)的信息:
-
latitude
:十進制度數(shù)表示的緯度 -
longitude
:十進制度數(shù)表示的經(jīng)度 -
accuracy
:經(jīng)緯度坐標的精度,以米為單位
navigator.geolocation.getCurrentPosition(function (position) {
drawMapCenteredAt(position.coords.latitude, position.coords.longitude);
});
失敗回調(diào)
在被調(diào)用的時候也會接受到一個參數(shù),這個參數(shù)是一個對象,包含連個屬性:message
和code
。code
保存著一個數(shù)值,表示錯誤的類型:用戶拒絕共享(1)、位置無效(2)或者超時(3)。
navigator.geolocation.getCurrentPosition(function (position) {
drawMapCenteredAt(position.coords.latitude, position.coords.longitude);
}, function (error) {
console.log('Error code:' + error.code);
console.log('Error message:' + error.message);
});
第三個參數(shù)是一個可選對象
,用于設(shè)定信息的類型。可以設(shè)置的選項有三個:
-
enableHightAccuracy
:布爾值,表示必須盡可能使用最準確定的位置信息 -
timeout
:以毫秒數(shù)表示的等待位置信息的最長時間 -
maximumAge
:表示上一次取得的坐標信息的有效時間,以毫秒表示,如果時間到則重新取得新坐標信息
navigator.geolocation.getCurrentPosition(function (position) {
drawMapCenteredAt(position.coords.latitude, position.coords.longitude);
}, function (error) {
console.log('Error code:' + error.code);
console.log('Error message:' + error.message);
}, {
enableHighAccuracy: true,
timeout: 5000,
maximumAge: 25000
});
File API
File API 在表單中的文件輸入字段的基礎(chǔ)上,又添加了一些直接訪問文件信息的接口。HTML5在DOM中為文件輸入元素添加了一個files
集合。每個File對象都有下列只讀屬性。
-
name
:本地文件系統(tǒng)的文件名 -
size
:文件的字節(jié)大小 -
type
:字符串,文件的MIME類型 -
lastModifiedDate
:字符串,文件上一次修改的時間
FileReader 類型
FileReader 類型實現(xiàn)的是一種異步文件讀取機制。可以把FileReader
想象成XMLHttpRequest
。
-
readAsText(file, encoding)
:以純文本形式讀取文件,將讀取到的文本保存在result
屬性中,第二個參數(shù)用于指定編碼類型(可選) -
readAsDataURL(file)
:讀取文件并將文件以數(shù)據(jù)URI形式保存在result
屬性中 -
readAsBinaryString(file)
:讀取文件并將一個字符串保存在result
屬性中,字符串中的每個字符表示一字節(jié) -
readAsArrayBuffer(file)
:讀取文件并將一個包含文件內(nèi)容的ArrayBuffer
保存在result
屬性中
由于讀取過程是異步的,所以FileReader
提供了三個事件:
progress
error
load
progress
事件,每50ms就會觸發(fā)一次,通過事件對象可以獲得與XHR
的progress
事件相同的信息:
lengthComputable
loaded
total
由于種種原因無法讀取文件,都會觸發(fā)error
事件,相關(guān)信息都會保存到FileReader
的error
屬性中。error.code
即錯誤碼:
-
1
:為找到文件 -
2
:安全性錯誤 -
3
:讀取中斷 -
4
:文件不可讀 -
5
:編碼錯誤
文件加載成功后會觸發(fā)load
事件。
var filesList = document.getElementById('files-list');
EventUtil.addHandler(filesList, 'change', function (event) {
var info = '',
output = document.getElementById('output'),
progress = document.getElementById('progress'),
files = EventUtil.getTarget(event).files,
type = 'default',
reader = new FileReader();
if (/image/.test(files[0].type)) {
reader.readAsDateURL(files[0]);
type = 'image';
} else {
reader.readAsText(files[0]);
type = 'text';
}
reader.onerror = function () {
output.innerHTML = 'Could not read file, error code is ' + reader.error.code;
};
reader.onprogress = function () {
if (event.lengthComputable) {
progress.innerHTML = event.loaded + '/' + event.total;
}
};
reader.onload = function () {
var html = '';
switch (type) {
case 'image':
html = '<img src=\"' + reader.result + '\">';
break;
case 'text':
html = reader.result;
break;
}
output.innerHTML = html;
};
});
讀取拖放的文件
從桌面上把文件拖放到瀏覽器中會觸發(fā)drop
事件。而且可以在event.dataTransfer. files
中讀取到被放置的文件,當然此時它是一個File
對象,與通過文件輸入字段取得的File
對象一樣。
var droptarget = document.getElementById('droptarget');
function handleEvent(event) {
var info = '',
output = document.getElementById('output');
files, i, len;
EventUtil.preventDefault(event);
if (event.type == 'drop') {
files = event.dataTransfer.files; //轉(zhuǎn)換成File對象
i = 0;
len = files.length;
while (i < len) {
info += files[i].name + ' (' + files[i].type + ', ' + files[i].size + ' bytes)<br>';
i++;
}
output.innerHTML = info;
}
}
// 阻止默認事件,只有 drop 事件會被處理
EventUtil.addHandler(droptarget, "dragenter", handleEvent);
EventUtil.addHandler(droptarget, "dragover", handleEvent);
EventUtil.addHandler(droptarget, "drop", handleEvent);
使用XHR上傳文件
創(chuàng)建一個FormDate
對象,通過它調(diào)用append()
方法并傳入相應(yīng)的File
對象作為參數(shù),再把FormData
對象傳遞給XHR
的send()
方法。
var droptarget = document.getElementById('droptarget');
function handleEvent(event) {
var info = '',
output = document.getElementById('output'),
data, xhr,
files, i, len;
EventUtil.preventDefault(event);
if (event.type == 'drop') {
data = new FormData();
files = event.dataTransfer.files;
i = 0;
len = files.length;
while (i < len) {
data.append('file' + i, files[i]);
i++;
}
xhr = new XMLHttpRequest();
xhr.open('post', 'FileAPIExapleUpload.php', true);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
console.log(xhr.responseText);
}
};
xhr.send(data);
}
}
EventUtil.addHandler(droptarget, "dragenter", handleEvent);
EventUtil.addHandler(droptarget, "dragover", handleEvent);
EventUtil.addHandler(droptarget, "drop", handleEvent);