??HTML5 規(guī)范了很多新 HTML 標(biāo)記。為了配合這些標(biāo)記的變化,HTML5 規(guī)范也用顯著篇幅定義了很多 JavaScript API。
??定義這些 API 的用意就是簡化此前實現(xiàn)起來困難重重的任務(wù),最終簡化創(chuàng)建動態(tài) Web 界面的工作。
1、跨文檔消息傳送
??跨文檔消息傳送(cross-document messaging),有時候簡稱為 XDM,指的是在來自不同域的頁面間傳遞消息。例如,www.wrox.com 域中的頁面與位于一個內(nèi)嵌框架中的 p2p.wrox.com 域中的頁面通信。
??在 XDM 機制出現(xiàn)之前,要穩(wěn)妥地實現(xiàn)這種通信需要花很多功夫。XDM 把這種機制規(guī)范化,讓我們能既穩(wěn)妥又簡單地實現(xiàn)跨文檔通信。
??XDM 的核心是 postMessage() 方法。在HTML5 規(guī)范中,除了 XDM 部分之外的其他部分也會提到這個方法名,但都是為了同一目的:向另一個地方傳遞數(shù)據(jù)。
??對于 XDM 而言,“另一個地方”指的是包含在當(dāng)前頁面中的 <iframe> 元素,或者由當(dāng)前頁面彈出的窗口。
??postMessage() 方法接收兩個參數(shù):一條消息和一個表示消息接收方來自哪個域的字符串。第二個參數(shù)對保障安全通信非常重要,可以放置瀏覽器把消息發(fā)送到不安全的地方。示例:
// 注意:所有支持 XMD 的瀏覽器也支持 iframe 的contentWindow 屬性
var iframeWindow = document.getElementById('myframe').contentWindow;
iframeWindow.postMessage("A secret", "http://www.wrox.com");
??最后一行代碼嘗試向內(nèi)嵌框架中發(fā)送一條消息,并指定框架中的文檔必須來源于"http://www.wrox.com"域。如果來源匹配,消息就會傳遞到內(nèi)嵌框架中;否則,postMessage() 什么也不做。
??這一限制可以避免窗口中的位置在你不知情的情況下發(fā)生改變。如果傳給 postMessage() 的第二個參數(shù)是"*",則表示可以把消息發(fā)送給來自任何域的文檔,但我們不推薦這樣做。
??接收到 XDM 消息時,會觸發(fā) window 對象的 message 事件。這個事件是以異步形式觸發(fā)的,因此從發(fā)送消息到接收消息(觸發(fā)接收窗口的 message 事件)可能要經(jīng)過一段時間的延遲。觸發(fā) message 事件后,傳遞給 onmessage 處理程序的事件對象包含以下三方面的重要信息。
- data:作為 postMessage() 第一個參數(shù)傳入的字符串?dāng)?shù)據(jù)。
- origin:發(fā)送消息的文檔所在的域,例如"http://www.wrox.com"。
- source:發(fā)送消息的文檔的 window 對象的代理。這個代理對象主要用于在發(fā)送上一條消息的窗口中調(diào)用 postMessage() 方法。如果發(fā)送消息的窗口來自同一個域,那這個對象就是 window。
??接收到消息后驗證發(fā)送窗口的來源是至關(guān)重要的。就像給 postMessage() 方法指定第二個參數(shù),以確保瀏覽器不會把消息發(fā)送給未知頁面一樣,在 onmessage 處理程序中檢測消息來源可以確保傳入的消息來自已知的頁面?;镜臋z測模式如下。
EventUtil.addHandler(window, "message", function(event){
// 確保發(fā)送消息的域是已知的域
if (event.origin == "http://www.wrox.com"){
// 處理接收到的數(shù)據(jù)
processMessage(event.data);
// 可選:向來源窗口發(fā)送回執(zhí)
event.source.postMessage("Received!", "http://p2p.wrox.com");
}
});
??還是要提醒大家,event.source 大多數(shù)情況下只是 window 對象的代理,并非實際的 window 對象。換句話說,不能通過這個代理對象訪問 window 對象的其他任何信息。
??記住,只通過這個代理調(diào)用 postMessage() 就好,這個方法永遠(yuǎn)存在,永遠(yuǎn)可以調(diào)用。
??XDM 還有一些怪異之處。首先,postMessage() 的第一個參數(shù)最早是作為“永遠(yuǎn)都是字符串”來實現(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()。
??在通過內(nèi)嵌框架加載其他域的內(nèi)容時,使用 XDM 是非常方便的。因此,在混搭(mashup)和社交網(wǎng)絡(luò)應(yīng)用中,這種傳遞消息的方法極為常用。有了 XDM,包含<iframe>的頁面可以確保自身不受惡意內(nèi)容的侵?jǐn)_,因為它只通過 XDM 與嵌入的框架通信。而 XDM 也可以在來自相同域的頁面間使用。
??支持 XDM 的瀏覽器有 IE8+、Firefox 3.5+、Safari 4+、Opera、Chrome、iOS 版 Safari 及 Android 版 WebKit。XDM 已經(jīng)作為一個規(guī)范獨立出來,現(xiàn)在它的名字叫 Web Messaging,官方頁面是 http://dev.w3.org/html5/postmsg/。
2、 原生拖放
??最早在網(wǎng)頁中引入 JavaScript 拖放功能的是 IE4。當(dāng)時,網(wǎng)頁中只有兩種對象可以拖放:圖像和某些文本。
??拖動圖像時,把鼠標(biāo)放在圖像上,按住鼠標(biāo)不放就可以拖動它。拖動文本時,要先選中文本,然后可以像拖動圖像一樣拖動被選中的文本。
??在 IE 4 中,唯一有效的放置目標(biāo)是文本框。到了 IE5,拖
放功能得到擴展,添加了新的事件,而且?guī)缀蹙W(wǎng)頁中的任何元素都可以作為放置目標(biāo)。IE5.5 更進(jìn)一步,讓網(wǎng)頁中的任何元素都可以拖放。(IE6 同樣也支持這些功能。)
??HTML5 以 IE 的實例為基礎(chǔ)制定了拖放規(guī)范。Firefox 3.5、Safari 3+和 Chrome 也根據(jù) HTML5 規(guī)范實現(xiàn)了原生拖放功能。
??說到拖放,最有意思的恐怕就是能夠在框架間、窗口間,甚至在應(yīng)用間拖放網(wǎng)頁元素了。瀏覽器對拖放的支持為實現(xiàn)這些功能提供了便利。
2.1、 拖放事件
??通過拖放事件,可以控制拖放相關(guān)的各個方面。其中最關(guān)鍵的地方在于確定哪里發(fā)生了拖放事件,有些事件是在被拖動的元素上觸發(fā)的,而有些事件是在放置目標(biāo)上觸發(fā)的。拖動某元素時,將依次觸發(fā)下列事件:
????(1) dragstart
????(2) drag
????(3) dragend
??按下鼠標(biāo)鍵并開始移動鼠標(biāo)時,會在被拖放的元素上觸發(fā) dragstart 事件。此時光標(biāo)變成“不能放”符號(圓環(huán)中有一條反斜線),表示不能把元素放到自己上面。拖動開始時,可以通過 ondragstart 事件處理程序來運行 JavaScript 代碼。
??觸發(fā) dragstart 事件后,隨即會觸發(fā) drag 事件,而且在元素被拖動期間會持續(xù)觸發(fā)該事件。這個事件與 mousemove 事件相似,在鼠標(biāo)移動過程中,mousemove 事件也會持續(xù)發(fā)生。
??當(dāng)拖動停止時(無論是把元素放到了有效的放置目標(biāo),還是放到了無效的放置目標(biāo)上),會觸發(fā) dragend 事件。
??上述三個事件的目標(biāo)都是被拖動的元素。默認(rèn)情況下,瀏覽器不會在拖動期間改變被拖動元素的外觀,但你可以自己修改。不過,大多數(shù)瀏覽器會為正被拖動的元素創(chuàng)建一個半透明的副本,這個副本始終跟隨著光標(biāo)移動。
??當(dāng)某個元素被拖動到一個有效的放置目標(biāo)上時,下列事件會依次發(fā)生:
????(1) dragenter
????(2) dragover
????(3) dragleave 或 drop
??只要有元素被拖動到放置目標(biāo)上,就會觸發(fā) dragenter 事件(類似于 mouseover 事件)。
??緊隨其后的是 dragover 事件,而且在被拖動的元素還在放置目標(biāo)的范圍內(nèi)移動時,就會持續(xù)觸發(fā)該事件。
??如果元素被拖出了放置目標(biāo),dragover 事件不再發(fā)生,但會觸發(fā) dragleave 事件(類似于 mouseout 事件)。
??如果元素被放到了放置目標(biāo)中,則會觸發(fā) drop 事件而不是 dragleave 事件。上述三個事件的目標(biāo)都是作為放置目標(biāo)的元素。
2.2、 自定義放置目標(biāo)
??在拖動元素經(jīng)過某些無效放置目標(biāo)時,可以看到一種特殊的光標(biāo)(圓環(huán)中有一條反斜線),表示不能放置。雖然所有元素都支持放置目標(biāo)事件,但這些元素默認(rèn)是不允許放置的。
??如果拖動元素經(jīng)過不允許放置的元素,無論用戶如何操作,都不會發(fā)生 drop 事件。不過,你可以把任何元素變成有效的放置目標(biāo),方法是重寫 dragenter 和 dragover 事件的默認(rèn)行為。
??例如,假設(shè)有一個 ID 為"droptarget" 的<div>元素,可以用如下代碼將它變成一個放置目標(biāo)。
var droptarget = document.getElementById("droptarget");
EventUtil.addHandler(droptarget, "dragover", function(event){
EventUtil.preventDefault(event);
});
EventUtil.addHandler(droptarget, "dragenter", function(event){
EventUtil.preventDefault(event);
});
??以上代碼執(zhí)行后,你就會發(fā)現(xiàn)當(dāng)拖動著元素移動到放置目標(biāo)上時,光標(biāo)變成了允許放置的符號。當(dāng)然,釋放鼠標(biāo)也會觸發(fā) drop 事件。
??在 Firefox 3.5+中,放置事件的默認(rèn)行為是打開被放到放置目標(biāo)上的 URL。換句話說,如果是把圖像拖放到放置目標(biāo)上,頁面就會轉(zhuǎn)向圖像文件;而如果是把文本拖放到放置目標(biāo)上,則會導(dǎo)致無效 URL
錯誤。因此,為了讓 Firefox 支持正常的拖放,還要取消 drop 事件的默認(rèn)行為,阻止它打開 URL:
EventUtil.addHandler(droptarget, "drop", function(event){
EventUtil.preventDefault(event);
});
2.3、 dataTransfer 對象
??只有簡單的拖放而沒有數(shù)據(jù)變化是沒有什么用的。為了在拖放操作時實現(xiàn)數(shù)據(jù)交換,IE5 引入了 dataTransfer 對象,它是事件對象的一個屬性,用于從被拖動元素向放置目標(biāo)傳遞字符串格式的數(shù)據(jù)。
??因為它是事件對象的屬性,所以只能在拖放事件的事件處理程序中訪問 dataTransfer 對象。在事件處理程序中,可以使用這個對象的屬性和方法來完善拖放功能。目前,HTML5 規(guī)范草案也收入了 dataTransfer 對象。
??dataTransfer 對象有兩個主要方法:getData() 和 setData()。getData() 可以取得由 setData() 保存的值。
??setData() 方法的第一個參數(shù),也是 getData() 方法唯一的一個參數(shù),是一個字符串,表示保存的數(shù)據(jù)類型,取值為"text"或"URL",如下所示:
// 設(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");
??IE只定義了"text"和"URL"兩種有效的數(shù)據(jù)類型,而 HTML5 則對此加以擴展,允許指定各種 MIME 類型??紤]到向后兼容,HTML5 也支持"text"和"URL",但這兩種類型會被映射為"text/plain"和"text/uri-list"。
??實際上,dataTransfer 對象可以為每種 MIME 類型都保存一個值。換句話說,同時在這個對象中保存一段文本和一個 URL 不會有任何問題。
??不過,保存在 dataTransfer 對象中的數(shù)據(jù)只能在 drop 事件處理程序中讀取。如果在 ondrop 處理程序中沒有讀到數(shù)據(jù),那就是 dataTransfer 對象已經(jīng)被銷毀,數(shù)據(jù)也丟失了。
??在拖動文本框中的文本時,瀏覽器會調(diào)用 setData() 方法,將拖動的文本以"text"格式保存在 dataTransfer 對象中。
??類似地,在拖放鏈接或圖像時,會調(diào)用 setData() 方法并保存 URL。
??然后,在這些元素被拖放到放置目標(biāo)時,就可以通過 getData() 讀到這些數(shù)據(jù)。當(dāng)然,作為開發(fā)人員,你也可以在 dragstart 事件處理程序中調(diào)用 setData(),手工保存自己要傳輸?shù)臄?shù)據(jù),以便將來使用。
??將數(shù)據(jù)保存為文本和保存為 URL 是有區(qū)別的。如果將數(shù)據(jù)保存為文本格式,那么數(shù)據(jù)不會得到任何特殊處理。
??而如果將數(shù)據(jù)保存為 URL,瀏覽器會將其當(dāng)成網(wǎng)頁中的鏈接。換句話說,如果你把它放置到另一個瀏覽器窗口中,瀏覽器就會打開該 URL。
??Firefox 在其第 5 個版本之前不能正確地將 "url" 和 "text" 映射為 "text/uri-list" 和 "text/plain"。但是卻能把"Text"(T 大寫)映射為"text/plain"。為了更好地在跨瀏覽器的情況下從 dataTransfer 對象取得數(shù)據(jù),最好在取得 URL 數(shù)據(jù)時檢測兩個值,而在取得文本數(shù)據(jù)時使用"Text"。
var dataTransfer = event.dataTransfer;
// 讀取 URL
var url = dataTransfer.getData("url") || dataTransfer.getData("text/uri-list");
// 讀取文本
var text = dataTransfer.getData("Text");
??注意,一定要把短數(shù)據(jù)類型放在前面,因為 IE 10 及之前的版本仍然不支持?jǐn)U展的 MIME 類型名,而它們在遇到無法識別的數(shù)據(jù)類型時,會拋出錯誤。
2.4、 dropEffect 與 effectAllowed
??利用 dataTransfer 對象,可不光是能夠傳輸數(shù)據(jù),還能通過它來確定被拖動的元素以及作為放置目標(biāo)的元素能夠接收什么操作。為此,需要訪問 dataTransfer 對象的兩個屬性:dropEffect 和 effectAllowed。
??其中,通過 dropEffect 屬性可以知道被拖動的元素能夠執(zhí)行哪種放置行為。這個屬性有下列 4 個可能的值。
- "none":不能把拖動的元素放在這里。這是除文本框之外所有元素的默認(rèn)值。
- "move":應(yīng)該把拖動的元素移動到放置目標(biāo)。
- "copy":應(yīng)該把拖動的元素復(fù)制到放置目標(biāo)。
- "link":表示放置目標(biāo)會打開拖動的元素(但拖動的元素必須是一個鏈接,有 URL)。
??在把元素拖動到放置目標(biāo)上時,以上每一個值都會導(dǎo)致光標(biāo)顯示為不同的符號。然而,要怎樣實現(xiàn)光標(biāo)所指示的動作完全取決于你。換句話說,如果你不介入,沒有什么會自動地移動、復(fù)制,也不會打
開鏈接??傊?,瀏覽器只能幫你改變光標(biāo)的樣式,而其他的都要靠你自己來實現(xiàn)。
??要使用 dropEffect 屬性,必須在 ondragenter 事件處理程序中針對放置目標(biāo)來設(shè)置它。
??dropEffect 屬性只有搭配 effectAllowed 屬性才有用。effectAllowed 屬性表示允許拖動元素的哪種 dropEffect,effectAllowed 屬性可能的值如下。
- "uninitialized":沒有給被拖動的元素設(shè)置任何放置行為。
- "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 屬性。
??假設(shè)你想允許用戶把文本框中的文本拖放到一個<div>元素中。首先,必須將 dropEffect 和 effectAllowed 設(shè)置為"move"。但是,由于<div>元素的放置事件的默認(rèn)行為是什么也不做,所以文本不可能自動移動。重寫這個默認(rèn)行為,就能從文本框中移走文本。然后你就可以自己編寫代碼將文本插入到<div>中,這樣整個拖放操作就完成了。
??如果你將 dropEffect 和 effectAllowed 的值設(shè)置為"copy",那就不會自動移走文本框中的文本。
??Firefox 5 及之前的版本在處理 effectAllowed 屬性時有一個問題,即如果你在代碼中設(shè)置了這個屬性的值,那不一定會觸發(fā) drop 事件。
2.5、 可拖動
??默認(rèn)情況下,圖像、鏈接和文本是可以拖動的,也就是說,不用額外編寫代碼,用戶就可以拖動它們。
??文本只有在被選中的情況下才能拖動,而圖像和鏈接在任何時候都可以拖動。
??讓其他元素可以拖動也是可能的。HTML5 為所有 HTML 元素規(guī)定了一個 draggable 屬性,表示元素是否可以拖動。圖像和鏈接的 draggable 屬性自動被設(shè)置成了 true,而其他元素這個屬性的默認(rèn)值都是 false。要想讓其他元素可拖動,或者讓圖像或鏈接不能拖動,都可以設(shè)置這個屬性。示例:
<!-- 讓這個圖像不可以拖動 -->
<img src="smile.gif" draggable="false" alt="Smiley face">
<!-- 讓這個元素可以拖動 -->
<div draggable="true">...</div>
??支持 draggable 屬性的瀏覽器有 IE 10+、Firefox 4+、Safari 5+ 和 Chrome。Opera 11.5 及之前的版本都不支持 HTML5 的拖放功能。
??另外,為了讓 Firefox 支持可拖動屬性,還必須添加一個 ondragstart 事件處理程序,并在 dataTransfer 對象中保存一些信息。
??在 IE9 及更早版本中,通過 mousedown 事件處理程序調(diào)用 dragDrop() 能夠讓任何元素可拖動。而在 Safari 4 及之前版本中,必須額外給相應(yīng)元素設(shè)置 CSS 樣式 –khtml-user-drag: element。
2.6、 其他成員
??HTML5 規(guī)范規(guī)定 dataTransfer 對象還應(yīng)該包含下列方法和屬性。
- addElement(element):為拖動操作添加一個元素。添加這個元素只影響數(shù)據(jù)(即增加作為拖動源而響應(yīng)回調(diào)的對象),不會影響拖動操作時頁面元素的外觀。在寫作本書時,只有 Firefox 3.5+ 實現(xiàn)了這個方法。
- clearData(format):清除以特定格式保存的數(shù)據(jù)。實現(xiàn)這個方法的瀏覽器有 IE、Fireforx 3.5+、Chrome 和 Safari 4+。
- setDragImage(element, x, y):指定一幅圖像,當(dāng)拖動發(fā)生時,顯示在光標(biāo)下方。這個方法接收的三個參數(shù)分別是要顯示的 HTML 元素和光標(biāo)在圖像中的 x、y 坐標(biāo)。其中,HTML 元素可以是一幅圖像,也可以是其他元素。是圖像則顯示圖像,是其他元素則顯示渲染后的元素。實現(xiàn)這個方法的瀏覽器有 Firefox 3.5+、Safari 4+和 Chrome。
- types:當(dāng)前保存的數(shù)據(jù)類型。這是一個類似數(shù)組的集合,以"text"這樣的字符串形式保存著數(shù)據(jù)類型。實現(xiàn)這個屬性的瀏覽器有 IE10+、Firefox 3.5+和 Chrome。
3、 媒體元素
??隨著音頻和視頻在 Web 上的迅速流行,大多數(shù)提供富媒體內(nèi)容的站點為了保證跨瀏覽器兼容性,不得不選擇使用 Flash。
??HTML5 新增了兩個與媒體相關(guān)的標(biāo)簽,讓開發(fā)人員不必依賴任何插件就能在網(wǎng)頁中嵌入跨瀏覽器的音頻和視頻內(nèi)容。這兩個標(biāo)簽就是<audio>和<video>。
??這兩個標(biāo)簽除了能讓開發(fā)人員方便地嵌入媒體文件之外,都提供了用于實現(xiàn)常用功能的 JavaScriptAPI,允許為媒體創(chuàng)建自定義的控件。這兩個元素的用法如下。
<!-- 嵌入視頻 -->
<video src="conference.mpg" id="myVideo">Video player not available.</video>
<!-- 嵌入音頻 -->
<audio src="song.mp3" id="myAudio">Audio player not available.</audio>
??使用這兩個元素時,至少要在標(biāo)簽中包含 src 屬性,指向要加載的媒體文件。
??還可以設(shè)置 width 和 height 屬性以指定視頻播放器的大??;
??而為 poster 屬性指定圖像的 URI 可以在加載視頻內(nèi)容期間
顯示一幅圖像。
??另外,如果標(biāo)簽中有 controls 屬性,則意味著瀏覽器應(yīng)該顯示 UI 控件,以便用戶直接操作媒體。
??位于開始和結(jié)束標(biāo)簽之間的任何內(nèi)容都將作為后備內(nèi)容,在瀏覽器不支持這兩個媒體元素的情況下顯示。
??因為并非所有瀏覽器都支持所有媒體格式,所以可以指定多個不同的媒體來源。為此,不用在標(biāo)簽中指定 src 屬性,而是要像下面這樣使用一或多個<source>元素。
<!-- 嵌入視頻 -->
<video id="myVideo">
<source src="conference.webm" type="video/webm; codecs='vp8, vorbis'">
<source src="conference.ogv" type="video/ogg; codecs='theora, vorbis'">
<source src="conference.mpg">
Video player not available.
</video>
<!-- 嵌入音頻 -->
<audio id="myAudio">
<source src="song.ogg" type="audio/ogg">
<source src="song.mp3" type="audio/mpeg">
Audio player not available.
</audio>
??關(guān)于視頻和音頻編解碼器的內(nèi)容超出了本書討論的范圍。作者在此只想告訴大家,不同的瀏覽器支持不同的編解碼器,因此一般來說指定多種格式的媒體來源是必需的。支持這兩個媒體元素的瀏覽器有IE9+、Firefox 3.5+、Safari 4+、Opera 10.5+、Chrome、iOS 版 Safari 和 Android 版 WebKit。
3.1、屬性
??<video> 和 <audio> 元素都提供了完善的 JavaScript 接口。下表列出了這兩個元素共有的屬性,通過這些屬性可以知道媒體的當(dāng)前狀態(tài)。
| 屬 性 | 數(shù)據(jù)類型 | 說 明 |
|---|---|---|
| autoplay | 布爾值 | 取得或設(shè)置 autoplay 標(biāo)志 |
| buffered | 時間范圍 | 表示已下載的緩沖的時間范圍的對象 |
| bufferedBytes | 字節(jié)范圍 | 表示已下載的緩沖的字節(jié)范圍的對象 |
| bufferingRate | 整數(shù)???? | 下載過程中每秒鐘平均接收到的位數(shù) |
| bufferingThrottled | 布爾值 | 表示瀏覽器是否對緩沖進(jìn)行了節(jié)流 |
| controls | 布爾值 | 取得或設(shè)置controls屬性,用于顯示或隱藏瀏覽器內(nèi)置的控件 |
| currentLoop | 整數(shù) | 媒體文件已經(jīng)循環(huán)的次數(shù) |
| currentSrc | 字符串 | 當(dāng)前播放的媒體文件的URL |
| currentTime | 浮點數(shù) | 已經(jīng)播放的秒數(shù) |
| defaultPlaybackRate | 浮點數(shù) | 取得或設(shè)置默認(rèn)的播放速度。默認(rèn)值為1.0秒 |
| duration | 浮點數(shù) | 媒體的總播放時間(秒數(shù)) |
| ended | 布爾值 | 表示媒體文件是否播放完成 |
| loop | 布爾值 | 取得或設(shè)置媒體文件在播放完成后是否再從頭開始播放 |
| muted | 布爾值 | 取得或設(shè)置媒體文件是否靜音 |
| networkState | 整數(shù) | 表示當(dāng)前媒體的網(wǎng)絡(luò)連接狀態(tài):0表示空,1表示正在加載,2表示正在加載元數(shù)據(jù),3表示已經(jīng)加載了第一幀,4表示加載完成 |
| paused | 布爾值 | 表示播放器是否暫停 |
| playbackRate | 浮點數(shù) | 取得或設(shè)置當(dāng)前的播放速度。用戶可以改變這個值,讓媒體播放速度變快或變慢,這與defaultPlaybackRate只能由開發(fā)人員修改的defaultPlaybackRate不同 |
| played | 時間范圍 | 到目前為止已經(jīng)播放的時間范圍 |
| readyState | 整數(shù) | 表示媒體是否已經(jīng)就緒(可以播放了)。0表示數(shù)據(jù)不可用,1表示可以顯示當(dāng)前幀,2表示可以開始播放,3表示媒體可以從頭到尾播放 |
| seekable | 時間范圍 | 可以搜索的時間范圍 |
| seeking | 布爾值 | 表示播放器是否正移動到媒體文件中的新位置 |
| src | 字符串 | 媒體文件的來源。任何時候都可以重寫這個屬性 |
| start | 浮點數(shù) | 取得或設(shè)置媒體文件中開始播放的位置,以秒表示 |
| totalBytes | 整數(shù) | 當(dāng)前資源所需的總字節(jié)數(shù) |
| videoHeight | 整數(shù) | 返回視頻(不一定是元素)的高度。只適用于<video> |
| videoWidth | 整數(shù) | 返回視頻(不一定是元素)的寬度。只適用于<video> |
| volume | 浮點數(shù) | 取得或設(shè)置當(dāng)前音量,值為0.0到1.0 |
??其中很多屬性也可以直接在<audio>和<video>元素中設(shè)置。
3.2、事件
??除了大量屬性之外,這兩個媒體元素還可以觸發(fā)很多事件。這些事件監(jiān)控著不同的屬性的變化,這些變化可能是媒體播放的結(jié)果,也可能是用戶操作播放器的結(jié)果。下表列出了媒體元素相關(guān)的事件。
| 事 件 | 觸發(fā)時機 |
|---|---|
| abort | 下載中斷 |
| canplay | 可以播放時;readyState值為 2 |
| canplaythrough | 播放可繼續(xù),而且應(yīng)該不會中斷;readyState值為 3 |
| canshowcurrentframe | 當(dāng)前幀已經(jīng)下載完成;readyState值為 1 |
| dataunavailable | 因為沒有數(shù)據(jù)而不能播放;readyState值為 0 |
| durationchange | duration 屬性的值改變 |
| emptied | 網(wǎng)絡(luò)連接關(guān)閉 |
| empty | 發(fā)生錯誤阻止了媒體下載 |
| ended | 媒體已播放到末尾,播放停止 |
| error | 下載期間發(fā)生網(wǎng)絡(luò)錯誤 |
| load | 所有媒體已加載完成。這個事件可能會被廢棄,建議使用 canplaythrough |
| loadeddata | 媒體的第一幀已加載完成 |
| loadedmetadata | 媒體的元數(shù)據(jù)已加載完成 |
| loadstart | 下載已開始 |
| pause | 播放已暫停 |
| play | 媒體已接收到指令開始播放 |
| playing | 媒體已實際開始播放 |
| progress | 正在下載 |
| ratechange | 播放媒體的速度改變 |
| seeked | 搜索結(jié)束 |
| seeking | 正移動到新位置 |
| stalled | 瀏覽器嘗試下載,但未接收到數(shù)據(jù) |
| timeupdate | currentTime 被以不合理或意外的方式更新 |
| volumechange | volume 屬性值或 muted 屬性值已改變 |
| waiting | 播放暫停,等待下載更多數(shù)據(jù) |
??這些事件之所以如此具體,就是為了讓開發(fā)人員只使用少量 HTML 和 JavaScript(與創(chuàng)建 Flash 影片相比)即可編寫出自定義的音頻/視頻播放器。
3.3、自定義媒體播放器
??使用<audio>和<video>元素的 play() 和 pause() 方法,可以手工控制媒體文件的播放。
??組合使用屬性、事件和這兩個方法,很容易創(chuàng)建一個自定義的媒體播放器,如下面的例子所示。
<div class="mediaplayer">
<div class="video">
<video id="player" src="movie.mov" poster="mymovie.jpg" width="300" height="200">
Video player not available.
</video>
</div>
<div class="controls">
<input type="button" value="Play" id="video-btn">
<span id="curtime">0</span>/<span id="duration">0</span>
</div>
</div>
??以上基本的 HTML 再加上一些 JavaScript 就可以變成一個簡單的視頻播放器。以下就是 JavaScript 代碼。
// 取得元素的引用
var player = document.getElementById("player"),
btn = document.getElementById("video-btn"),
curtime = document.getElementById("curtime"),
duration = document.getElementById("duration");
// 更新播放時間
duration.innerHTML = player.duration;
// 為按鈕添加事件處理程序
EventUtil.addHandler(btn, "click", function(event){
if (player.paused){
player.play();
btn.value = "Pause";
} else {
player.pause();
btn.value = "Play";
}
});
// 定時更新當(dāng)前時間
setInterval(function(){
curtime.innerHTML = player.currentTime;
}, 250);
??以上 JavaScript 代碼給按鈕添加了一個事件處理程序,單擊它能讓視頻在暫停時播放,在播放時暫停。
??通過<video>元素的 load 事件處理程序,設(shè)置了加載完視頻后顯示播放時間。
??最后,設(shè)置了一個計時器,以更新當(dāng)前顯示的時間。
??你可以進(jìn)一步擴展這個視頻播放器,監(jiān)聽更多事件,利用更多屬性。而同樣的代碼也可以用于<audio>元素,以創(chuàng)建自定義的音頻播放器。
3.4、檢測編解碼器的支持情況
??如前所述,并非所有瀏覽器都支持<video>和<audio>的所有編解碼器,而這基本上就意味著你必須提供多個媒體來源。不過,也有一個 JavaScript API 能夠檢測瀏覽器是否支持某種格式和編解碼器。
??這兩個媒體元素都有一個 canPlayType() 方法,該方法接收一種格式/編解碼器字符串,返回"probably"、"maybe"或""( 空字符串)。空字符串是假值,因此可以像下面這樣在 if 語句中使用 canPlayType():
if (audio.canPlayType("audio/mpeg")){
// 進(jìn)一步處理
}
??而"probably"和"maybe"都是真值,因此在 if 語句的條件測試中可以轉(zhuǎn)換成 true。
??如果給 canPlayType() 傳入了一種 MIME 類型,則返回值很可能是"maybe"或空字符串。這是因為媒體文件本身只不過是音頻或視頻的一個容器,而真正決定文件能否播放的還是編碼的格式。
??在同時傳入 MIME 類型和編解碼器的情況下,可能性就會增加,返回的字符串會變成"probably"。下面來看幾個例子。
var audio = document.getElementById("audio-player");
// 很可能"maybe"
if (audio.canPlayType("audio/mpeg")){
// 進(jìn)一步處理
}
// 可能是"probably"
if (audio.canPlayType("audio/ogg; codecs=\"vorbis\"")){
// 進(jìn)一步處理
}
??注意,編解碼器必須用引號引起來才行。下表列出了已知的已得到支持的音頻格式和編解碼器。
| 音 頻 | 字 符 串 | 支持的瀏覽器 |
|---|---|---|
| AAC | audio/mp4; codecs="mp4a.40.2" | IE9+、Safari 4+、iOS版Safari |
| MP3 | audio/mpeg | IE9+、Chrome |
| Vorbis | audio/ogg; codecs="vorbis" | Firefox 3.5+、Chrome、Opera 10.5+ |
| WAV | audio/wav; codecs="1" | Firefox 3.5+、Opera 10.5+、Chrome |
??當(dāng)然,也可以使用 canPlayType() 來檢測視頻格式。下表列出了已知的已得到支持的視頻格式和編解碼器。
| 視 頻 | 字 符 串 | 支持的瀏覽器 |
|---|---|---|
| H.264 | video/mp4; codecs="avc1.42E01E, mp4a.40.2" | IE9+、Safari 4+、iOS版Safari、Android 版WebKit |
| Theora | video/ogg; codecs="theora" | Firefox 3.5+、Opera 10.5、Chrome |
| WebM | video/webm; codecs="vp8, vorbis" | Firefox 4+、Opera 10.6、Chrome |
3.5、Audio 類型
??<audio>元素還有一個原生的 JavaScript 構(gòu)造函數(shù) Audio,可以在任何時候播放音頻。從同為 DOM 元素的角度看,Audio 與 Image 很相似,但 Audio 不用像 Image 那樣必須插入到文檔中。只要創(chuàng)建一個新實例,并傳入音頻源文件即可。
var audio = new Audio("sound.mp3");
EventUtil.addHandler(audio, "canplaythrough", function(event){
audio.play();
});
??創(chuàng)建新的 Audio 實例即可開始下載指定的文件。下載完成后,調(diào)用 play() 就可以播放音頻。
??在 iOS 中,調(diào)用 play() 時會彈出一個對話框,得到用戶的許可后才能播放聲音。如果想在一段音頻播放后再播放另一段音頻,必須在 onfinish 事件處理程序中調(diào)用 play() 方法。
4、歷史狀態(tài)管理
??歷史狀態(tài)管理是現(xiàn)代 Web 應(yīng)用開發(fā)中的一個難點。在現(xiàn)代 Web 應(yīng)用中,用戶的每次操作不一定會打開一個全新的頁面,因此“后退”和“前進(jìn)”按鈕也就失去了作用,導(dǎo)致用戶很難在不同狀態(tài)間切換。
??要解決這個問題,首選使用 hashchange 事件。HTML5 通過更新 history 對象為管理歷史狀態(tài)提供了方便。
??通過 hashchange 事件,可以知道 URL 的參數(shù)什么時候發(fā)生了變化,即什么時候該有所反應(yīng)。而通過狀態(tài)管理 API ,能夠在不加載新頁面的情況下改變?yōu)g覽器的 URL 。
??為此,需要使用 history.pushState() 方法,該方法可以接收三個參數(shù):狀態(tài)對象、新狀態(tài)的標(biāo)題和可選的相對 URL。例如:
history.pushState({name:"Nicholas"}, "Nicholas' page", "nicholas.html");
??執(zhí)行 pushState() 方法后,新的狀態(tài)信息就會被加入歷史狀態(tài)棧,而瀏覽器地址欄也會變成新的相對 URL。
??但是,瀏覽器并不會真的向服務(wù)器發(fā)送請求,即使?fàn)顟B(tài)改變之后查詢 location.href 也會返回與地址欄中相同的地址。
??另外,第二個參數(shù)目前還沒有瀏覽器實現(xiàn),因此完全可以只傳入一個空字符串,或者一個短標(biāo)題也可以。
??而第一個參數(shù)則應(yīng)該盡可能提供初始化頁面狀態(tài)所需的各種信息。
??因為 pushState() 會創(chuàng)建新的歷史狀態(tài),所以你會發(fā)現(xiàn)“后退”按鈕也能使用了。按下“后退”按鈕,會觸發(fā) window 對象的 popstate 事件。
??popstate 事件發(fā)生后,事件對象中的狀態(tài)對象(event.state)是當(dāng)前狀態(tài)。
??popstate 事件的事件對象有一個 state 屬性,這個屬性就包含著當(dāng)初以第一個參數(shù)傳遞給 pushState() 的狀態(tài)對象。
EventUtil.addHandler(window, "popstate", function(event){
var state = event.state;
if (state){ // 第一個頁面加載時 state 為空
processState(state);
}
});
??得到這個狀態(tài)對象后,必須把頁面重置為狀態(tài)對象中的數(shù)據(jù)表示的狀態(tài)(因為瀏覽器不會自動為你做這些)。
??記住,瀏覽器加載的第一個頁面沒有狀態(tài),因此單擊“后退”按鈕返回瀏覽器加載的第一個頁面時,event.state 值為 null。
??要更新當(dāng)前狀態(tài),可以調(diào)用 replaceState(),傳入的參數(shù)與 pushState() 的前兩個參數(shù)相同。
??調(diào)用這個方法不會在歷史狀態(tài)棧中創(chuàng)建新狀態(tài),只會重寫當(dāng)前狀態(tài)。
history.replaceState({name:"Greg"}, "Greg's page");
??支持 HTML5 歷史狀態(tài)管理的瀏覽器有 Firefox 4+、Safari 5+、Opera 11.5+和 Chrome。
??在 Safari 和 Chrome 中,傳遞給 pushState() 或 replaceState() 的狀態(tài)對象中不能包含 DOM 元素。而 Firefox 支持在狀態(tài)對象中包含 DOM 元素。
??Opera 還支持一個 history.state 屬性,它返回當(dāng)前狀態(tài)的狀態(tài)對象。
??在使用 HTML5 的狀態(tài)管理機制時,請確保使用 pushState() 創(chuàng)造的每一個“假”URL,在 Web 服務(wù)器上都有一個真的、實際存在的 URL 與之對應(yīng)。否則,單擊“刷新”按鈕會導(dǎo)致 404 錯誤。
小結(jié)
??HTML5 除了定義了新的標(biāo)記規(guī)則,還定義了一些 JavaScript API。這些 API 是為了讓開發(fā)人員創(chuàng)建出更好的、能夠與桌面應(yīng)用媲美的用戶界面而設(shè)計的。本章討論了如下 API。
- 跨文檔消息傳遞 API 能夠讓我們在不降低同源策略安全性的前提下,在來自不同域的文檔間傳遞消息。
- 原生拖放功能讓我們可以方便地指定某個元素可拖動,并在操作系統(tǒng)要放置時做出響應(yīng)。還可以創(chuàng)建自定義的可拖動元素及放置目標(biāo)。
- 新的媒體元素<audio>和<video>擁有自己的與音頻和視頻交互的 API。并非所有瀏覽器支持所有的媒體格式,因此應(yīng)該使用 canPlayType() 檢查瀏覽器是否支持特定的格式。
- 歷史狀態(tài)管理讓我們不必卸載當(dāng)前頁面即可修改瀏覽器的歷史狀態(tài)棧。有了這種機制,用戶就可以通過“后退”和“前進(jìn)”按鈕在頁面狀態(tài)間切換,而這些狀態(tài)完全由 JavaScript 進(jìn)行控制。