1. 程式人生 > >H5的新特性及部分API詳解

H5的新特性及部分API詳解

h5新特性總覽

移除的元素

純表現的元素: basefont、big、center、font等
對可用性產生負面影響的元素: frame、frameset、noframes

新增的API

語義: 能夠讓你更恰當地描述你的內容是什麼。
連通性: 能夠讓你和伺服器之間通過創新的新技術方法進行通訊(web sockets等)。
離線 & 儲存:能夠讓網頁在客戶端本地儲存資料以及更高效地離線執行(離線資源、線上和離線事件、DOM儲存、IndexDB、自web應用程式中使用檔案[FileReader])。
多媒體:使 video 和 audio 成為了在所有 Web 中的一等公民。
2D/3D 繪圖 & 效果:

提供了一個更加分化範圍的呈現選擇(canvas、webGL)。
效能 & 整合:提供了非常顯著的效能優化和更有效的計算機硬體使用(WebWorkers、XMLHttpRequest2、HistoryAPI、拖放、requestAnimationFrame、全屏API、指標鎖定API、線上和離線事件)。
裝置訪問 Device Access:能夠處理各種輸入和輸出裝置(觸控事件touch、使用地理位置定位、檢測裝置方向)。

部分API詳述

web儲存機制

Web Storage的目的是克服由cookie帶來的一些限制,當資料需要被嚴格控制在客戶端上時,無需持續地將資料發回伺服器。Web Storage的兩個主要目標是:提供一種在cookie之外儲存會話資料的途徑;提供一種儲存大量可以跨會話存在的資料機制。最初的Web Storage規範包含了兩種物件的定義:sessionStorage和globalStorage。這兩個物件在支援的瀏覽器中都是以windows物件屬性的形式存在的。

sessionStorage物件

sessionStorage物件儲存特定於某個會話的資料,也就是該資料只保持到瀏覽器關閉。這個物件就像會話cookie,也會在瀏覽器關閉後消失。儲存在sessionStorage中的資料可以跨越頁面重新整理而存在,同時如果瀏覽器支援,瀏覽器崩潰並重啟之後依然可用(FireFox和WebKit都支援,IE不支援)
因為sessionStorage物件綁定於某個伺服器會話,所以當檔案在本地執行的時候是不可用的,儲存在sessionStorage中的資料只能由最初給物件儲存資料的野蠻訪問到,所以對多頁面應用有限制。
sessionStorage物件可以使用setItem()或者直接設定新的屬性來儲存資料

//使用sessionStorage方法儲存資料
sessionStorage.setitem('name','Nicholas');
//使用屬性儲存資料
sessionStorage.book = 'Profession JavaScript';

不同瀏覽器寫入資料方面略有不同。FireFox和WebKit實現了同步寫入,所以新增到儲存空間中的資料時立刻被提交的。而IE的實現則是非同步寫入資料,所以在設定資料和將資料實際寫入磁碟之間可能有一些延遲。對於少量資料而言,這個差異是可以忽略的。對於大量資料,IE要比其他瀏覽器更快的恢復執行,因為它會跳過實際的磁碟寫入過程
在IE8中可以強制把資料寫入磁碟:在設定新資料之前使用begin()方法,並且在所有設定完成後呼叫commit()方法

sessionStorage.begin();//確保在這段程式碼執行的時候不會發生其他磁碟寫入操作
sessionStorage.setitem('name','Nicholas');
sessionStorage.book = 'Profession JavaScript';
sessionStorage.commit();

sessionStorage中有資料時,可以使用getItem()或者通過直接訪問屬性名來獲取資料。

//使用方法讀取資料
var name = sessionStorage.getItem('name');
//使用屬性讀取資料
var book = sessionStorage.book;

還可以通過結合length屬性和key()方法來迭代sessionStorage的值。

for(var i = 0,len = sessionStorage.length; i < len; i++){
    var key = sessionStorage.key(i);
    var value = sessionStorage.getItem(key);
    alert(key + "=" + value);
}

要從sessionStorage中刪除資料可以使用delete操作符刪除物件屬性,也可以呼叫removeItem()方法。

delete sessionStorage.name;
sessionStorge.removeItem('book');

globalStorage物件

sessionStorage物件應該主要用於針對會話的小段資料的儲存。如果需要跨越花花儲存資料,那麼globalStorage或者localStorage更為合適
要使用globalStorage,首先要制定哪些域可以訪問該資料。可以通過方括號標記使用屬性來實現。

//儲存資料
globalStorage['wrox.com'].name = 'Nicholas';
//獲取資料
var name = globalStorage['wrox.com].name;

在這裡,訪問的是針對域名wrox.com的儲存空間。這個儲存空間對於wrox.com及其所有子域都是可以訪問的。
對globalStorage空間的訪問,是依據發起請求的頁面的域名、協議和埠來限制的(類似於ajax請求的同源策略)。如果實現不能確定域名,那麼使用location.host作為屬性名比較安全

globalStorage[location.host].name = 'Nicholas';
var book = globalStorage[location.host].getItem('book');

如果不使用removeItem()或者delete刪除,或者使用者為清除瀏覽器快取,儲存在globalStorage屬性中的資料會一直保留在磁碟上。這讓globalStorage非常適合在客戶端儲存文件或者長期儲存使用者偏好設定

localStorage物件

localStorage物件在修訂過的HTML5規範中作為持久儲存客戶端資料的方案取代了globalStorage。與globalStorage不同,不能給localStorage指定任何訪問規則;規則實現就設定好了。要訪問同一個localStorage物件,頁面必須來自同一個域名,使用同一種協議,在同一個埠上。這相當於globalStorage[location.host]
由於localStorage是Storage的例項,所以可以像使用sessionStorage一樣來使用它。

//使用方法儲存資料
localStorage.setItem('name','Nichoalas');
//使用屬性儲存資料
localStorage.book = 'Professional JavaScript';
//使用方法讀取資料
var name = localStorage.getItem('name')
//使用屬性讀取資料
var book = localStorage.book;

儲存在localStorage中的資料和儲存在globalStorage中的資料一樣,都遵循相同的規則:資料保留到通過JavaScript
刪除或者是使用者清除瀏覽器快取

File API

File API在表單中的檔案輸入欄位的基礎上,又添加了一些直接訪問檔案資訊的介面。H5在DOM中為檔案輸入元素添加了一個files集合,在通過文字輸入欄位選擇了一或多個檔案時,files集合中將包含一組File物件,每個File物件對應著一個檔案。每個File物件都有下列只讀屬性

  • name: 本地檔案系統的檔名
  • size: 檔案的位元組大小
  • type:字串,檔案的MIME型別。
  • lastModifiedDate:字串,檔案上一次被修改的事件(只有chrome實現了這個屬性)

現在我們獲取id為‘files-list’的input為file的元素,將該元素中上傳的檔案輸出到控制檯

var filesList = document.getElementById('files-list');
        EventUtil.addHandler(filesList,'change',funciton(e){
            var files = EventUtil.getTarget(e).files,
                i = 0,
                len = files.length;
            while(i<len){
                console.log(files[i].name + '('+files[i].type+','+files[i].size +'bytes)');
                i++;

            }
        })

FileReader型別

FlieReader型別實現的是一種非同步檔案讀取機制。可以把FileReader想象成XMLHttpRequest,區別只是它讀取的是檔案心痛,而不是遠端伺服器。為了讀取檔案中的資料,FileReader提供瞭如下幾個方法:

  • readAsText(file, encoding):以純文字的形式讀取檔案,將讀取到的文字儲存在result屬性中。
  • readAsDataURL(file):讀取檔案並將檔案一資料URI的形式儲存在result屬性中
  • readAsBinaryString(file)(已廢棄):讀取檔案並將一個字串儲存在result屬性中,字串中的每一個字元表示一位元組
  • readAsArrayBuffer(file):讀取檔案並將一個包含檔案內容的ArrayBuffer儲存在result屬性中。

由於讀取過程是非同步的,因此FileReader也提供了幾個事件。其中最有用的三個事件是progress、error和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.readAsDataURL(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(event){
        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;
    };
});

讀取部分內容

File物件支援一個slice()方法以實現讀取檔案的一部分而不是全部內容,這個方法在FireFox中的實現叫mozSlice(),在chrome中的實現是webkitSlice(),Safiri的5.1及之前的版本不支援這個方法。Slice()方法接收兩個引數:起始位元組及要讀取的位元組數。這個方法返回一個Blob例項,Blob是File型別的父型別。下面是一個通用的函式,可以在不同實踐中使用slice()方法:

function blobSlice(blob,startByte,length){
    if(blob.slice){
        return blob.slice(startByte,length);
    } else if(blob.webkitSlice){
        return blob.webkitSlice(startByte,length);
    } else if(blob.webkitSlice){
        return blob.webkitSlice(startByte,length);
    } else {
     return null;
    }
}

blob型別有一個size屬性和一個type屬性,而且它也支援slice()方法,以便進一步切割資料。通過FileReader也可以從Blob中讀取資料。下面這個例子只讀取檔案的32B內容

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();
              blob = blobSlice(files[0],0,32);

          if (blob){
              reader.readAsText(blob);
              reader.onerror = function(){
               outpit.innerHTML = 'could not read file,error code is' + reader.error.code;
           }
           reader.onload = function(){
            output.innerHTML = reader.result;
        };
     } else {
         alert('your browser does not support slice()');

          }

          reader.onerror = function(){
              output.innerHTML = "Could not read file, error code is " + reader.error.code;
          };

只讀取檔案的一部分可以節省時間,非常適合只關注資料中某個特定部分(如請求頭部)的情況

物件URL


物件URL也被稱為blob URL,指的是引用儲存在File或Blob中資料的URL。使用物件URL的好處是可以不必把檔案內容讀取到JavaScript中而直接使用檔案內容。為此,只要在需要檔案內容的地方提供物件URL即可。要建立物件URL,可以使用window.URL.createObjectURL()方法,並傳入File或Blob物件。這個方法在Chrome中的實現叫window.webkitURL.createObjectURL(),因此可以通過如下函式來消除命名的差異:
function createObjectURL(blob){
    if(window.URL){
        return window.URL.createObjectURL(blob);
    } else if (window.webkitURL) {
        return window.webkitURL.createObjectURL(blob);
    } else {
        return null;
    }
}

這個函式的返回值是一個字串,指向一塊記憶體的地址。因為這個字串是URL,所以在DOM中也能使用。例如,以下程式碼可以在頁面中顯示一個影象檔案:

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();
          url = createObjectURL(files[0]);
      if(url) {


       if (/image/.test(files[0].type)){
           output.innerHTML="<img src =\"" + url + "\">"
       } else {
           output.innerHTML = 'not an image';
       }
    } else {
     output.innerHTML = "your browser doesn't support object URLs";
 }
});

      reader.onerror = function(){
          output.innerHTML = "Could not read file, error code is " + reader.error.code;
      };

      reader.onprogress = function(event){
          if (event.lengthComputable){
              progress.innerHTML = event.loaded + "/" + event.total;
          }
      };

讀取拖放的檔案

圍繞讀取檔案資訊,結合使用HTML5拖放API和檔案API,能夠創造出令人矚目的使用者介面:在頁面上建立了自定義的放置目標後,可以從桌面上把檔案拖放到該目標。與拖放一張圖片或者一個連結類似,從桌面上把檔案拖放到瀏覽器中也會觸發drop事件。而且可以在e.dataTransfer.files中讀到被放置的檔案,當然此時它是一個File物件,與童年過檔案輸入欄位取得的File物件一樣。

var droptarget = document.getElementById('droptarget');

function handleEvent(e){
    var info = '';
    var output = document.getElementById('output');
    var files, i, len;
    e.preventDefault();
    if(e.type == 'drop') {
        files = e.dataTransfer.files;
        i = 0;
        len = files.length;
        while(i < len){
            info += files[i].name + '(' + files[i].type + ',' + files[i].size + 'bytes<br>';
            i++;
        }
        output.innerHTML = info;
    }
}
droptarget.addHandler('dragenter',handleEvent,false);
droptarget.addHandler('drageover',handleEvent,false);
droptarget.addHandler('drop',handleEvent,false);

Web Workers

專用Web Worker提供可一個簡單的方法使的web內容能夠在後臺執行指令碼。一旦worker建立後,它可以向由它的建立者指定的事件監聽函式傳遞訊息,這樣改worker生成的所有任務就都會接收到這個訊息。worker執行緒能夠在不干擾UI的情況下執行任務。

生成worker

建立一個新的worker十分簡單。就是呼叫Worker()建構函式,指定一個要在worker執行緒內執行的指令碼的URI,如果希望能夠收到worker的通知,可以將worker的onmessage屬性設定成一個特定的事件處理函式,如果希望能夠傳送資訊到worker,可以使用postmessage方法

var myWorker = new Worker("my_task.js");

myWorker.onmessage = function (oEvent) {
  console.log("Called back by the worker!\n");
};

myWorker.postMessage(""); // start the worker.

傳遞資料

在主頁面與worker之間傳遞的資料是通過拷貝,而不是共享來完成的。傳遞給worker的物件需要經過序列化,接下來在另一端還需要反序列化。頁面與worker不會共享同一個例項,最終的結果就是在每次通訊結束時生成了資料的一個副本。大部分瀏覽器使用結構化拷貝來實現該特性。
example.html(主頁面)

var myWorker = new Worker("my_task.js");

myWorker.onmessage = function (oEvent) {
  console.log("Worker said : " + oEvent.data);
};

myWorker.postMessage("ali");

my_task.js(worker)

postMessage("I\'m working before postMessage(\'ali\').");

onmessage = function (oEvent) {
  postMessage("Hi " + oEvent.data);
};

Worker全域性作用域

關於Web Worker,最重要的是要知道它所執行的JavaScript程式碼完全在另一個作用域,與當前網頁中的程式碼不共享作用域。在Web Worker中,同樣有一個全域性物件和其他物件以及方法。但是Web Worker中的程式碼不能訪問DOM,也無法通過任何方式影響頁面的外觀
Web Worker中的全域性物件是worker物件本身。也就是說,在這個特殊的全域性作用域中,this和sele引用的都是worker物件。為便於處理資料,Web Worker本身也是一個最小化的執行環境

  • 最小化的navgator物件 : online、appName、appVersion、userAgent、platform
  • 只讀的location物件 : 所有屬性都是隻讀的
  • self : 指向全域性 worker 物件
  • 所有的ECMA物件,Object、Array、Date等
  • XMLHttpRequest構造器
  • setTimeout、setInterval、clearTimeout()和clearInterval()方法

在worker內部,呼叫close()方法也可以停止工作。Worker停止工作後就不會再有事件發生。
另外,Worker的全域性作用域中提供了importScripts()方法。這個方法接收一個或多個指向JavaScript檔案的URL。每個載入過程都是非同步進行的,因此素有的指令碼載入並執行完成之後,importScripts()才會執行

importScripts('file1.js','file2.js');

即使file2.js先於file1.js下載完,執行的時候仍然會按照先後順序實行。而且,這些指令碼是在Worker的全域性作用域中執行,如果指令碼中包含與頁面香瓜你的JavaScript程式碼,那麼指令碼可能無法正確執行。

history物件

history物件儲存著使用者上網的歷史記錄,從視窗被開啟的那一刻算起。
使用go()方法可以在使用者的歷史記錄中任意跳轉,可以向後也可以向前。這個方法接受一個引數,表示向後或向前跳轉的頁面數的一個整數值。負數表示向後跳轉(類似於單擊瀏覽器的‘後退’按鈕),正數表示向前跳轉(類似於單擊瀏覽器的“前進”按鈕)


history.go(-1);//後退一頁
history.go(1);//前進一頁
history.go(2);//前進兩頁

也可以給go()方法傳遞一個字串引數,此時瀏覽器會跳轉到歷史記錄中包含該字串的第一個位置–可能後退,也可能前進,具體看那個位置最近。如果歷史記錄中不包含該字串,那麼這個方法什麼也不做

history.go('wrox.com');//跳到最近的wrox.com頁面

另外,還可以使用兩個簡寫方法back()和forward()來代替go()。這兩個方法都可以模仿瀏覽器的‘後退’和‘前進’按鈕。

history.back();//後退一頁
history.forward();//前進一頁

history物件還有一個length屬性,儲存著歷史記錄的數量。這個數量包括所有的歷史記錄,即所有向後和向前的記錄。

history在h5中新增的屬性和方法

h5中的history物件新增了兩個新方法:history.pushState()和history.replaeState();
兩種方法都允許我們新增和更新歷史記錄,它們的工作原理相同並且可以新增數量相同的引數。但是pushState()是在history棧中新增一個新的條目,replaceState()是替換當前的記錄值。除了方法之外,還有popstate 事件
pushState(data,title[,url])和replaceState(data,title[,url])引數一樣,引數說明如下:

  • data:一個表示狀態的物件,json格式資料
  • title:一個string格式的標題(大多數瀏覽器不支援或忽略這個引數,最好用null代替)
  • url:一個url(用於替換當前URL)

當瀏覽會話記錄的時候,不管點選前進或者後退按鈕,還是使用history.go和history.back方法,popstate事件都會被觸發。當事件發生時,瀏覽器會從history中取出URL和對應的state物件替換當前的URL和history.state。通過event.state也可以獲取history.state
需要說明的是pushState只是將當前頁面儲存到history的歷史記錄中(並作為最近的一個記錄),並且將當前瀏覽器的位址列改為引數url指定的值,但並不會載入它。這點與普通的通過連結開啟或瀏覽器地址輸入url完全不一樣。所以如果想在url改變的時候需要監聽popstate事件。

利用history可以彌補ajax無法回退的缺陷。如下方法是模擬ajax操作的實現方法。

<input type="button" value="加1" onclick="add()" />
   <div id="info" style="border:red 1px solid;width:200px;padding:10px;">0</div>
<script>
    var info = document.getElementById('info');
    var i = 1;
    function add() {
        var data = {
            param : i,
            func : func
        };
        info.innerHTML = i;
        document.title = i;
        History.push(data);
        i++;
    }

    function func(i) {
        info.innerHTML = i;
        document.title = i;
    }

    History = function() {  
            var 
               list = [],
               index = 1,

               func, scope;

            function push(data) {
                if(typeof data !== 'object') return;

                if(typeof data.param == undefined || typeof data.func !== 'function') return;

                func = data.func;
                scope = data.scope;

                history.pushState({param: data.param}, index, '#' + index);
                index++;
            }

            window.onpopstate = function(e) {
                if(e.state) {
                    var state = e.state,
                        param = state.param;
                    if(param) {
                        func.call(scope, param);
                    }
                }
                else{
                    if(func){
                        func.call(scope, 0);
                    }

                }

            }

            return {
                push : push
            };
        }();
</script>

2D繪圖(canvas和svg)

SVG

SVG 是一種使用 XML 描述 2D 圖形的語言。
SVG 基於 XML,這意味著 SVG DOM 中的每個元素都是可用的。您可以為某個元素附加 JavaScript 事件處理器。
在 SVG 中,每個被繪製的圖形均被視為物件。如果 SVG 物件的屬性發生變化,那麼瀏覽器能夠自動重現圖形。

canvas

Canvas 通過 JavaScript 來繪製 2D 圖形。
Canvas 是逐畫素進行渲染的。
在 canvas 中,一旦圖形被繪製完成,它就不會繼續得到瀏覽器的關注。如果其位置發生變化,那麼整個場景也需要重新繪製,包括任何或許已被圖形覆蓋的物件。

Canvas 與 SVG 的比較

下表列出了 canvas 與 SVG 之間的一些不同之處。
Canvas

依賴解析度
不支援事件處理器
弱的文字渲染能力
能夠以 .png 或 .jpg 格式儲存結果影象
最適合影象密集型的遊戲,其中的許多物件會被頻繁重繪

SVG

不依賴解析度
支援事件處理器
最適合帶有大型渲染區域的應用程式(比如谷歌地圖)
複雜度高會減慢渲染速度(任何過度使用 DOM 的應用都不快)
不適合遊戲應用

h5的相容性問題

IE6/IE7/IE8支援通過document.createElement方法產生的標籤,可以利用這一特性讓這些瀏覽器支援HTML5新標籤。但是瀏覽器支援新標籤後,還需要新增標籤預設的樣式。