HTML5指令碼程式設計

出版圈郭志敏發表於2012-03-20

本章內容

  • 使用跨文件訊息傳遞
  • 拖放API
  • 音訊與視訊

本書前面討論過,HTML5規範定義了很多新HTML標記。為了配合這些標記的變化,HTML5規範也用顯著篇幅定義了很多JavaScript API。定義這些API的用意就是簡化此前實現起來困難重重的任務,最終簡化建立動態Web介面的工作。

跨文件訊息傳遞

跨文件訊息傳送(cross-document messaging),有時候簡稱為XDM,指的是在來自不同域的頁面間傳遞訊息。例如,www.wrox.com域中的頁面與位於一個內嵌框架中的p2p.wrox.com域中的頁面通訊。在XDM機制出現之前,要穩妥地實現這種通訊需要花很多工夫。XDM把這種機制規範化,讓我們能既穩妥又簡單地實現跨文件通訊。

XDM的核心是postMessage()方法。在HTML5規範中,除了XDM部分之外的其他部分也會提到這個方法名,但都是為了同一個目的:向另一個地方傳遞資料。對於XDM而言,“另一個地方”指的是包含在當前頁面中的元素,或者由當前頁面彈出的視窗。

postMessage()方法接收兩個引數:一條訊息和一個表示訊息接收方來自哪個域的字串。第二個引數對保障安全通訊非常重要,可以防止瀏覽器把訊息傳送到不安全的地方。來看下面的例子。

//注意:所有支援XDM的瀏覽器也支援iframe的contentWindow屬性

var iframeWindow = document.getElementById("myframe").contentWindow;
iframeWindow.postMessage("A secret", "http://www.wrox.com");

最後一行程式碼嘗試向內嵌框架中傳送一條訊息,並指定框架中的文件必須來源於"http:// www.wrox.com"域。如果來源匹配,訊息會傳遞到內嵌框架中;否則,postMessage()什麼也不做。這一限制可以避免視窗中的位置在你不知情的情況下發生改變。如果傳給postMessage()的第二個引數是"*",則表示可以把訊息傳送給來自任何域的文件,但我們不推薦這樣做。

接收到XDM訊息時,會觸發window物件的message事件。這個事件是以非同步形式觸發的,因此從傳送訊息到接收訊息(觸發接收視窗的message事件)可能要經過一段時間的延遲。觸發message事件後,傳遞給onmessage處理程式的事件物件包含以下三方面的重要資訊。

  • data:作為postMessage()第一個引數傳入的字串資料。
  • origin:傳送訊息的文件所在的域,例如"http://www.wrox.com"。
  • source:傳送訊息的文件的window物件的代理。這個代理物件主要用於在傳送上一條訊息的視窗中呼叫postMessage()方法。如果傳送訊息的視窗來自同一個域,那這個物件就是window。

接收到訊息後驗證傳送視窗的來源是至關重要的。就像給postMessage()方法指定第二個引數,以確保瀏覽器不會把訊息傳送給未知頁面一樣,在onmessage處理程式中檢測訊息來源可以確保傳入的訊息來自已知的頁面。基本的檢測模式如下。

EventUtil.addHandler(window, "message", function(event){

//確保傳送訊息的域是已知的域
if (event.origin == "http://www.wrox.com"){

    //處理接收到的資料
    processMessage(event.data);

    //可選:向來源視窗傳送回執
    event.source.postMessage("Received!", "http://p2p.wrox.com");
}

});

還是要提醒大家,event.source大多數情況下只是window物件的代理,並非實際的window物件。換句話說,不能通過這個代理物件訪問window物件的其他任何資訊。記住,只通過這個代理呼叫postMessage()就好,這個方法永遠存在,永遠可以呼叫。

XDM還有一些怪異之處。首先,postMessage()的第一個引數最早是作為“永遠都是字串”來實現的。但後來這個引數的定義改了,改成允許傳入任何資料結構。可是,並非所有瀏覽器都實現了這一變化。為保險起見,使用postMessage()時,最好還是隻傳字串。如果你想傳入結構化的資料,最佳選擇是先在要傳入的資料上呼叫JSON.stringify(),通過postMessage()傳入得到的字串,然後再在onmessage事件處理程式中呼叫JSON.parse()。

在通過內嵌框架載入其他域的內容時,使用XDM是非常方便的。因此,在混搭(mashup)和社交網路應用中,這種傳遞訊息的方法極為常用。有了XDM,包含的頁面可以確保自身不受惡意內容的侵擾,因為它只通過XDM與嵌入的框架通訊。而XDM也可以在來自相同域的頁面間使用。

支援XDM的瀏覽器有IE8+、Firefox 3.5+、Safari 4+、Opera、Chrome、iOS版Safari及Android版WebKit。XDM已經作為一個規範獨立出來,現在它的名字叫Web Messaging,官方頁面是http://dev.w3.org/html5/postmsg/。

原生拖放

最早在網頁中引入JavaScript拖放功能的是IE4。當時,網頁中只有兩種物件可以拖放:影像和某些文字。拖動影像時,把滑鼠放在影像上,按住滑鼠不放就可以拖動它。拖動文字時,要先選中文字,然後可以像拖動影像一樣拖動被選中的文字。在IE 4中,唯一有效的放置目標是文字框。到了IE5,拖放功能得到擴充套件,新增了新的事件,而且幾乎網頁中的任何元素都可以作為放置目標。IE5.5更進一步,讓網頁中的任何元素都可以拖放。(IE6同樣也支援這些功能。)HTML5以IE的例項為基礎制定了拖放規範。Firefox 3.5、Safari 3+和Chrome也根據HTML5規範實現了原生拖放功能。

說到拖放,最有意思的恐怕就是能夠在框架間、視窗間,甚至在應用間拖放網頁元素了。瀏覽器對拖放的支援為實現這些功能提供了便利。

拖放事件

通過拖放事件,可以控制拖放相關的各個方面。其中最關鍵的地方在於確定哪裡發生了拖放事件,有些事件是在被拖動的元素上觸發的,而有些事件是在放置目標上觸發的。拖動某元素時,將依次觸發下列事件:

(1) dragstart

(2) drag

(3) dragend

按下滑鼠鍵並開始移動滑鼠時,會在被拖放的元素上觸發dragstart事件。此時游標變成“不能放”符號(圓環中有一條反斜線),表示不能把元素放到自己上面。拖動開始時,可以通過ondragstart事件處理程式來執行JavaScript程式碼。

觸發dragstart事件後,隨即會觸發drag事件,而且在元素被拖動期間會持續觸發該事件。這個事件與mousemove事件相似,在滑鼠移動過程中,mousemove事件也會持續發生。當拖動停止時(無論是把元素放到了有效的放置目標,還是放到了無效的放置目標上),會觸發dragend事件。

上述三個事件的目標都是被拖動的元素。預設情況下,瀏覽器不會在拖動期間改變被拖動元素的外觀,但你可以自己修改。不過,大多數瀏覽器會為正被拖動的元素建立一個半透明的副本,這個副本始終跟隨著游標移動。

當某個元素被拖動到一個有效的放置目標上時,下列事件會依次發生: (1) dragenter (2) dragover (3) dragleave或drop

只要有元素被拖動到放置目標上,就會觸發dragenter事件(類似於mouseover事件)。緊隨其後的是dragover事件,而且在被拖動的元素還在放置目標的範圍內移動時,就會持續觸發該事件。如果元素被拖出了放置目標,dragover事件不再發生,但會觸發dragleave事件(類似於mouseout事件)。如果元素被放到了放置目標中,則會觸發drop事件而不是dragleave事件。上述三個事件的目標都是作為放置目標的元素。

自定義放置目標

在拖動元素經過某些無效放置目標時,可以看到一種特殊的游標(圓環中有一條反斜線),表示不能放置。雖然所有元素都支援放置目標事件,但這些元素預設是不允許放置的。如果拖動元素經過不允許放置的元素,無論使用者如何操作,都不會發生drop事件。不過,你可以把任何元素變成有效的放置目標,方法是重寫dragenter和dragover事件的預設行為。例如,假設有一個ID為"droptarget"的

元素,可以用如下程式碼將它變成一個放置目標。
var droptarget = document.getElementById("droptarget");

EventUtil.addHandler(droptarget, "dragover", function(event){ 
    EventUtil.preventDefault(event);
});

EventUtil.addHandler(droptarget, "dragenter", function(event){
    EventUtil.preventDefault(event);
});

以上程式碼執行後,你就會發現當拖動著元素移動到放置目標上時,游標變成了允許放置的符號。當然,釋放滑鼠也會觸發drop事件。

在Firefox 3.5+中,放置事件的預設行為是開啟被放到放置目標上的URL。換句話說,如果是把影像拖放到放置目標上,頁面就會轉向影像檔案;而如果是把文字拖放到放置目標上,則會導致無效URL錯誤。因此,為了讓Firefox支援正常的拖放,還要取消drop事件的預設行為,阻止它開啟URL:

EventUtil.addHandler(droptarget, "drop", function(event){
    EventUtil.preventDefault(event); 
});

dataTransfer物件

只有簡單的拖放而沒有資料變化是沒有什麼用的。為了在拖放操作時實現資料交換,IE 5引入了dataTransfer物件,它是事件物件的一個屬性,用於從被拖動元素向放置目標傳遞字串格式的資料。因為它是事件物件的屬性,所以只能在拖放事件的事件處理程式中訪問dataTransfer物件。在事件處理程式中,可以使用這個物件的屬性和方法來完善拖放功能。目前,HTML5規範草案也收入了dataTransfer物件。

dataTransfer物件有兩個主要方法:getData()和setData()。不難想象,getData()可以取得由setData()儲存的值。setData()方法的第一個引數,也是getData()方法唯一的一個引數,是一個字串,表示儲存的資料型別,取值為"text"或"URL",如下所示:

//設定和接收文字資料
event.dataTransfer.setData("text", "some text");
var text = event.dataTransfer.getData("text");

//設定和接收URL
event.dataTransfer.setData("URL", "http://www.wrox.com/");
var url = event.dataTransfer.getData("URL");

IE只定義了"text"和"URL"兩種有效的資料型別,而HTML5則對此加以擴充套件,允許指定各種MIME型別。考慮到向後相容,HTML5也支援"text"和"URL",但這兩種型別會被對映為"text/plain"和"text/uri-list"。 實際上,dataTransfer物件可以為每種MIME型別都儲存一個值。換句話說,同時在這個物件中儲存一段文字和一個URL不會有任何問題。不過,儲存在dataTransfer物件中的資料只能在drop事件處理程式中讀取。如果在ondrop處理程式中沒有讀到資料,那就是dataTransfer物件已經被銷燬,資料也丟失了。

在拖動文字框中的文字時,瀏覽器會呼叫setData()方法,將拖動的文字以"text"格式儲存在dataTransfer物件中。類似地,在拖放連結或影像時,會呼叫setData()方法並儲存URL。然後,在這些元素被拖放到放置目標時,就可以通過getData()讀到這些資料。當然,作為開發人員,你也可以在dragstart事件處理程式中呼叫setData(),手工儲存自己要傳輸的資料,以便將來使用。

將資料儲存為文字和儲存為URL是有區別的。如果將資料儲存為文字格式,那麼資料不會得到任何特殊處理。而如果將資料儲存為URL,瀏覽器會將其當成網頁中的連結。換句話說,如果你把它放置到另一個瀏覽器視窗中,瀏覽器就會開啟該URL。

Firefox在其第5個版本之前不能正確地將"url"和"text"對映為"text/uri-list"和"text/plain"。但是卻能把"Text"(T大寫)對映為"text/plain"。為了更好地在跨瀏覽器的情況下從dataTransfer物件取得資料,最好在取得URL資料時檢測兩個值,而在取得文字資料時使用"Text"。

var dataTransfer = event.dataTransfer;

//讀取URL
var url = dataTransfer.getData("url") ||dataTransfer.getData("text/uri-list");

//讀取文字
var text = dataTransfer.getData("Text");

DataTransferExample01.htm

注意,一定要把短資料型別放在前面,因為IE 10及之前的版本仍然不支援擴充套件的MIME型別名,而它們在遇到無法識別的資料型別時,會丟擲錯誤。

dropEffect與effectAllowed

利用dataTransfer物件,可不光是能夠傳輸資料,還能通過它來確定被拖動的元素以及作為放置目標的元素能夠接收什麼操作。為此,需要訪問dataTransfer物件的兩個屬性:dropEffect和effectAllowed。 其中,通過dropEffect屬性可以知道被拖動的元素能夠執行哪種放置行為。這個屬性有下列4個可能的值。

  • "none":不能把拖動的元素放在這裡。這是除文字框之外所有元素的預設值。
  • "move":應該把拖動的元素移動到放置目標。
  • "copy":應該把拖動的元素複製到放置目標。
  • "link":表示放置目標會開啟拖動的元素(但拖動的元素必須是一個連結,有URL)。

在把元素拖動到放置目標上時,以上每一個值都會導致游標顯示為不同的符號。然而,要怎樣實現游標所指示的動作完全取決於你。換句話說,如果你不介入,沒有什麼會自動地移動、複製,也不會開啟連結。總之,瀏覽器只能幫你改變游標的樣式,而其他的都要靠你自己來實現。要使用dropEffect屬性,必須在ondragenter事件處理程式中針對放置目標來設定它。

dropEffect屬性只有搭配effectAllowed屬性才有用。effectAllowed屬性表示允許拖動元素的哪種dropEffect,effectAllowed屬性可能的值如下。

  • "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事件處理程式中設定effectAllowed屬性。

假設你想允許使用者把文字框中的文字拖放到一個

元素中。首先,必須將dropEffect和effectAllowed設定為"move"。但是,由於
元素的放置事件的預設行為是什麼也不做,所以文字不可能自動移動。重寫這個預設行為,就能從文字框中移走文字。然後你就可以自己編寫程式碼將文字插入到
中,這樣整個拖放操作就完成了。如果你將dropEffect和effectAllowed的值設定為"copy",那就不會自動移走文字框中的文字。

Firefox 5及之前的版本在處理effectAllowed屬性時有一個問題,即如果你在程式碼中設定了這個屬性的值,那不一定會觸發drop事件 。

可拖動

預設情況下,影像、連結和文字是可以拖動的,也就是說,不用額外編寫程式碼,使用者就可以拖動它們。文字只有在被選中的情況下才能拖動,而影像和連結在任何時候都可以拖動。

讓其他元素可以拖動也是可能的。HTML5為所有HTML元素規定了一個draggable屬性,表示元素是否可以拖動。影像和連結的draggable屬性自動被設定成了true,而其他元素這個屬性的預設值都是false。要想讓其他元素可拖動,或者讓影像或連結不能拖動,都可以設定這個屬性。例如:

<!-- 讓這個影像不可以拖動 -->
<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事件處理程式呼叫dragDrop()能夠讓任何元素可拖動。而在Safari 4及之前版本中,必須額外給相應元素設定CSS樣式–khtml-user-drag: element。

其他成員

HTML5規範規定dataTransfer物件還應該包含下列方法和屬性。

  • addElement(element):為拖動操作新增一個元素。新增這個元素隻影響資料(即增加作為拖動源而響應回撥的物件),不會影響拖動操作時頁面元素的外觀。在寫作本書時,只有Firefox 3.5+實現了這個方法。
  • clearData(format):清除以特定格式儲存的資料。實現這個方法的瀏覽器有IE、Fireforx 3.5+、Chrome和Safari 4+。
  • setDragImage(element, x, y):指定一幅影像,當拖動發生時,顯示在游標下方。這個方法接收的三個引數分別是要顯示的HTML元素和游標在影像中的x、y座標。其中,HTML元素可以是一幅影像,也可以是其他元素。是影像則顯示影像,是其他元素則顯示渲染後的元素。實現這個方法的瀏覽器有Firefox 3.5+、Safari 4+和Chrome。
  • types:當前儲存的資料型別。這是一個類似陣列的集合,以"text"這樣的字串形式儲存著資料型別。實現這個屬性的瀏覽器有IE10+、Firefox 3.5+和Chrome。

媒體元素

隨著音訊和視訊在Web上的迅速流行,大多數提供富媒體內容的站點為了保證跨瀏覽器相容性,不得不選擇使用Flash。HTML5新增了兩個與媒體相關的標籤,讓開發人員不必依賴任何外掛就能在網頁中嵌入跨瀏覽器的音訊和視訊內容。這兩個標籤就是和。

這兩個標籤除了能讓開發人員方便地嵌入媒體檔案之外,都提供了用於實現常用功能的JavaScript API,允許為媒體建立自定義的控制元件。這兩個元素的用法如下。

<!-- 嵌入視訊 -->
<video src="conference.mpg" id="myVideo">Video player not available.</video>

<!-- 嵌入音訊 -->
<audio src="song.mp3" id="myAudio">Audio player not available.</audio>

使用這兩個元素時,至少要在標籤中包含src屬性,指向要載入的媒體檔案。還可以設定width和height屬性以指定視訊播放器的大小,而為poster屬性指定影像的URI可以在載入視訊內容期間顯示一幅影像。另外,如果標籤中有controls屬性,則意味著瀏覽器應該顯示UI控制元件,以便使用者直接操作媒體。位於開始和結束標籤之間的任何內容都將作為後備內容,在瀏覽器不支援這兩個媒體元素的情況下顯示。

因為並非所有瀏覽器都支援所有媒體格式,所以可以指定多個不同的媒體來源。為此,不用在標籤中指定src屬性,而是要像下面這樣使用一或多個元素。

<!-- 嵌入視訊 -->
<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>

關於視訊和音訊編解碼器的內容超出了本書討論的範圍。作者在此只想告訴大家,不同的瀏覽器支援不同的編解碼器,因此一般來說指定多種格式的媒體來源是必需的。支援這兩個媒體元素的瀏覽器有IE9+、Firefox 3.5+、Safari 4+、Opera 10.5+、Chrome、iOS版Safari和Android版WebKit。

屬性

和元素都提供了完善的JavaScript介面。下表列出了這兩個元素共有的屬性,通過這些屬性可以知道媒體的當前狀態。

enter image description here

事件

除了大量屬性之外,這兩個媒體元素還可以觸發很多事件。這些事件監控著不同的屬性的變化,這些變化可能是媒體播放的結果,也可能是使用者操作播放器的結果。下表列出了媒體元素相關的事件。

enter image description here

這些事件之所以如此具體,就是為了讓開發人員只使用少量HTML和JavaScript(與建立Flash影片相比)即可編寫出自定義的音訊/視訊播放器。

自定義媒體播放器

使用和元素的play()和pause()方法,可以手工控制媒體檔案的播放。組合使用屬性、事件和這兩個方法,很容易建立一個自定義的媒體播放器,如下面的例子所示。

<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>

VideoPlayerExample01.htm

以上基本的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";
    }
});

//定時更新當前時間
setInterval(function(){
    curtime.innerHTML = player.currentTime;
}, 250);

VideoPlayerExample01.htm

以上JavaScript程式碼給按鈕新增了一個事件處理程式,單擊它能讓視訊在暫停時播放,在播放時暫停。通過元素的load事件處理程式,設定了載入完視訊後顯示播放時間。最後,設定了一個計時器,以更新當前顯示的時間。你可以進一步擴充套件這個視訊播放器,監聽更多事件,利用更多屬性。而同樣的程式碼也可以用於元素,以建立自定義的音訊播放器。

檢測編解碼器的支援情況

如前所述,並非所有瀏覽器都支援和的所有編解碼器,而這基本上就意味著你必須提供多個媒體來源。不過,也有一個JavaScript API能夠檢測瀏覽器是否支援某種格式和編解碼器。這兩個媒體元素都有一個canPlayType()方法,該方法接收一種格式/編解碼器字串,返回"probably"、"maybe"或""( 空字串)。空字串是假值,因此可以像下面這樣在if語句中使用canPlayType():

if (audio.canPlayType("audio/mpeg")){
    //進一步處理
}

而"probably"和"maybe"都是真值,因此在if語句的條件測試中可以轉換成true。 如果給canPlayType()傳入了一種MIME型別,則返回值很可能是"maybe"或空字串。這是因為媒體檔案本身只不過是音訊或視訊的一個容器,而真正決定檔案能否播放的還是編碼的格式。在同時傳入MIME型別和編解碼器的情況下,可能性就會增加,返回的字串會變成"probably"。下面來看幾個例子。

var audio = document.getElementById("audio-player");

//很可能"maybe"
if (audio.canPlayType("audio/mpeg")){
    //進一步處理
}

//可能是"probably"
if (audio.canPlayType("audio/ogg; codecs=\"vorbis\"")){
    //進一步處理
}

注意,編解碼器必須用引號引起來才行。下表列出了已知的已得到支援的音訊格式和編解碼器。

enter image description here

Audio型別

元素還有一個原生的JavaScript建構函式Audio,可以在任何時候播放音訊。從同為DOM元素的角度看,Audio與Image很相似,但Audio不用像Image那樣必須插入到文件中。只要建立一個新例項,並傳入音訊原始檔即可。

var audio = new Audio("sound.mp3");
EventUtil.addHandler(audio, "canplaythrough", function(event){
    audio.play();
}); 

建立新的Audio例項即可開始下載指定的檔案。下載完成後,呼叫play()就可以播放音訊。 在iOS中,呼叫play()時會彈出一個對話方塊,得到使用者的許可後才能播放聲音。如果想在一段音訊播放後再播放另一段音訊,必須在onfinish事件處理程式中呼叫play()方法。

歷史狀態管理

歷史狀態管理是現代Web應用開發中的一個難點。在現代Web應用中,使用者的每次操作不一定會開啟一個全新的頁面,因此“後退”和“前進”按鈕也就失去了作用,導致使用者很難在不同狀態間切換。要解決這個問題,首選使用hashchange事件(第13章曾討論過)。HTML5通過更新history物件為管理歷史狀態提供了方便。

通過hashchange事件,可以知道URL的引數什麼時候發生了變化,即什麼時候該有所反應。而通過狀態管理API,能夠在不載入新頁面的情況下改變瀏覽器的URL。為此,需要使用history.pushState()方法,該方法可以接收三個引數:狀態物件、新狀態的標題和可選的相對URL。例如:

history.pushState({name:"Nicholas"}, "Nicholas' page", "nicholas.html");

執行pushState()方法後,新的狀態資訊就會被加入歷史狀態棧,而瀏覽器位址列也會變成新的相對URL。但是,瀏覽器並不會真的向伺服器傳送請求,即使狀態改變之後查詢location.href也會返回與位址列中相同的地址。另外,第二個引數目前還沒有瀏覽器實現,因此完全可以只傳入一個空字串,或者一個短標題也可以。而第一個引數則應該儘可能提供初始化頁面狀態所需的各種資訊。

因為pushState()會建立新的歷史狀態,所以你會發現“後退”按鈕也能使用了。按下“後退”按鈕,會觸發window物件的popstate事件 。popstate事件的事件物件有一個state屬性,這個屬性就包含著當初以第一個引數傳遞給pushState()的狀態物件。

EventUtil.addHandler(window, "popstate", function(event){
    var state = event.state;
    if (state){   //第一個頁面載入時state為空
        processState(state);
    }
});

得到這個狀態物件後,必須把頁面重置為狀態物件中的資料表示的狀態(因為瀏覽器不會自動為你做這些)。記住,瀏覽器載入的第一個頁面沒有狀態,因此單擊“後退”按鈕返回瀏覽器載入的第一個頁面時,event.state值為null。

要更新當前狀態,可以呼叫replaceState(),傳入的引數與pushState()的前兩個引數相同。呼叫這個方法不會在歷史狀態棧中建立新狀態,只會重寫當前狀態。

history.replaceState({name:"Greg"}, "Greg's page");

支援HTML5歷史狀態管理的瀏覽器有Firefox 4+、Safari 5+、Opera 11.5+和Chrome。在Safari和Chrome中,傳遞給pushState()或replaceState()的狀態物件中不能包含DOM元素。而Firefox支援在狀態物件中包含DOM元素。Opera還支援一個history.state屬性,它返回當前狀態的狀態物件。

enter image description here

小結

HTML5除了定義了新的標記規則,還定義了一些JavaScript API。這些API是為了讓開發人員建立出更好的、能夠與桌面應用媲美的使用者介面而設計的。本章討論瞭如下API。

  • 跨文件訊息傳遞API能夠讓我們在不降低同源策略安全性的前提下,在來自不同域的文件間傳遞訊息。
  • 原生拖放功能讓我們可以方便地指定某個元素可拖動,並在作業系統要放置時做出響應。還可以建立自定義的可拖動元素及放置目標。
  • 新的媒體元素和擁有自己的與音訊和視訊互動的API。並非所有瀏覽器支援所有的媒體格式,因此應該使用canPlayType()檢查瀏覽器是否支援特定的格式。
  • 歷史狀態管理讓我們不必解除安裝當前頁面即可修改瀏覽器的歷史狀態棧。有了這種機制,使用者就可以通過“後退”和“前進”按鈕在頁面狀態間切換,而這些狀態完全由JavaScript進行控制。

本文摘自《JavaScript高階程式設計》(第3版)

相關文章