1. 程式人生 > >HTML5新特性淺談

HTML5新特性淺談

這裡寫圖片描述

轉載請註明出處:

2014年10月29日,W3C宣佈,經過接近8年的艱苦努力,HTML5標準規範終於制定完成。

HTML5將會取代1999年制定的HTML 4.01、XHTML 1.0標準,以期能在網際網路應用迅速發展的時候,使網路標準達到符合當代的網路需求,為桌面和移動平臺帶來無縫銜接的豐富內容。

作為2010年入坑IT的程式設計師來說,可以說一步一步見證著HTML5的發展。這些年為了相容IE6放棄了很多HTML5的新特性。但是今時不同以往,移動裝置的流行,天然支援HTML5,以及桌面端IE最終被使用者和微軟唾棄,更多支援HTML5瀏覽器的受歡迎,我要重新研究一下HTML5帶來的這些新特性。

HTML5 的新特性

① 語義特性(Semantic)

HTML5賦予網頁更好的意義和結構。

② 本地儲存特性(OFFLINE & STORAGE)

基於HTML5開發的網頁APP擁有更短的啟動時間,更快的聯網速度,這些全得益於HTML5 APP Cache,以及本地儲存功能。

③ 裝置訪問特性 (DEVICE ACCESS)

從Geolocation功能的API文件公開以來,HTML5為網頁應用開發者們提供了更多功能上的優化選擇,帶來了更多體驗功能的優勢。HTML5提供了前所未有的資料與應用接入開放介面。使外部應用可以直接與瀏覽器內部的資料直接相連,例如視訊影音可直接與microphones及攝像頭相聯。

④ 連線特性(CONNECTIVITY)

更有效的連線工作效率,使得基於頁面的實時聊天,更快速的網頁遊戲體驗,更優化的線上交流得到了實現。HTML5擁有更有效的伺服器推送技術,Server-Sent Event和WebSockets就是其中的兩個特性,這兩個特效能夠幫助我們實現伺服器將資料“推送”到客戶端的功能。

⑤ 網頁多媒體特性(MULTIMEDIA)

支援網頁端的Audio、Video等多媒體功能, 與網站自帶的APPS,攝像頭,影音功能相得益彰。

⑥ 三維、圖形及特效特性(3D, Graphics & Effects)

基於SVG、Canvas、WebGL及CSS3的3D功能,使用者會驚歎於在瀏覽器中,所呈現的驚人視覺效果。

⑦ 效能與整合特性(Performance & Integration)

沒有使用者會永遠等待你的Loading——HTML5會通過XMLHttpRequest2等技術,解決以前的跨域等問題,幫助您的Web應用和網站在多樣化的環境中更快速的工作。

下面分別對這七個新特性進行研究。

① 語義特性(Semantic)

這裡寫圖片描述

HTML5增加了新的內容標籤,這些標籤帶有一定的語義,使搜尋引擎爬取你的網站資訊更高效。

這裡寫圖片描述

HTML4中的內容標籤級別相同,無法區分各部分內容。而HTML5中的內容標籤互相獨立,級別不同,搜尋引擎以及統計軟體等均可快速識別各部分內容。

這些標籤在新聞類網站,部落格類網站很有用。

最大的問題就是當使用這些新的語義元素時,那些不支援的瀏覽器如何處理這些元素。

見過的最多的解決方法是這樣的。

<section class="section">

    <!-- content --> 

</section>
.section 
{
     color: blue;
}

② 本地儲存特性(OFFLINE & STORAGE)

這裡寫圖片描述

HTML5提供了網頁儲存的API,方便Web應用的離線使用。除此之外,新的API相對於cookie也有著高安全性,高效率,更大空間等優點。

先看W3C對離線儲存的介紹。

Web Apps can start faster and work even if there is no internet connection, thanks to the HTML5 App Cache, as well as the Local Storage, Indexed DB, and the File API specifications.

HTML5離線儲存包含 應用程式快取本地儲存索引資料庫檔案介面

下面依次展開介紹。

(1)應用程式快取(Application Cache)

這裡寫圖片描述

使用 HTML5,通過建立 cache manifest 檔案,可以輕鬆地建立 web 應用的離線版本。

HTML5引入了應用程式快取,這意味著 web 應用可進行快取,並可在沒有因特網連線時進行訪問。

應用程式快取為應用帶來三個優勢:

  • 離線瀏覽 – 使用者可在應用離線時使用它們
  • 速度 – 已快取資源載入得更快
  • 減少伺服器負載 – 瀏覽器將只從伺服器下載更新過或更改過的資源。

甭廢話,先來感受一下Application Cache的魅力。Shut up,show me the demo!

1.開啟這個網頁,第一次等待載入完成之後,頁面和普通的網頁沒有區別。

2.點選重新整理按鈕,或者強制重新整理按鈕,看一下第二次開啟的速度。有沒有快到爆。(速度

3.現在我要求你拔掉網線,斷開WiFi,再次點選重新整理按鈕,或者強制重新整理按鈕,看一下第三次開啟的速度。有沒有快到爆。注意,現在並沒有聯網,和伺服器失去連線,依然秒開網頁,還能正常操作網頁。(離線瀏覽,減少伺服器負載

看完了效果,看一下App Cache的原理。

這裡寫圖片描述

當我們第一次正確配置cache manifest後,瀏覽器會將清單檔案中的資源快取下來。當我們再次訪問該應用時,瀏覽器會直接返回快取中的資源,然後檢查manifest檔案是否有變動,如果有變動就會把相應的變動更新下來,同時改變瀏覽器裡面的app cache。

使用方法

頁面宣告使用App Cache

 <!DOCTYPE HTML> 
 <html manifest="index.manifest"> 

清單檔案中寫明資源。

CACHE MANIFEST
theme.css 
logo.gif 
main.js

NETWORK:
login.asp

FALLBACK:
/html5/ /404.html

manifest 檔案可分為三個部分:

CACHE MANIFEST - 在此標題下列出的檔案將在首次下載後進行快取
NETWORK - 在此標題下列出的檔案需要與伺服器的連線,且不會被快取
FALLBACK - 在此標題下列出的檔案規定當頁面無法訪問時的回退頁面(比如 404 頁面)

CACHE MANIFEST,是必需的
NETWORK 規定檔案 “login.asp” 永遠不會被快取,且離線時是不可用的
FALLBACK 規定如果無法建立因特網連線,則用 “404.html” 替代 /html5/ 目錄中的所有檔案

一旦應用被快取,它就會保持快取直到發生下列情況:

  • 使用者清空瀏覽器快取 ,使用者怎麼做,頁面左右不了,說了等於沒說。
  • manifest 檔案被修改,請注意:更新清單中列出的某個檔案並不意味著瀏覽器會重新快取該資源。清單檔案本身必須進行更改,一般是加一個註釋,註釋的內容是日期,想更新了,改一下日期。
  • 由程式來更新應用快取,這個稍微靠譜一點。

需要注意的是,更新後的資源需要下次開啟頁面才能生效,本次開啟的頁面在更新資源之前就已經從快取中拿到資源並載入完畢了。

由程式來更新,需要依賴manifest檔案被修改這一條,因為呼叫的是瀏覽器提供的介面,檢測 window.applicationCache.status 的值,如果是 UPDATEREADY,說明瀏覽器比較manifest檔案完畢,可以更新快取了。window.applicationCache.swapCache()。更新完了,不會立即生效,window.location.reload();重新載入一下頁面。

快取有這麼多狀態。

var appCache = window.applicationCache; 
switch (appCache.status) 
{ 
    case appCache.UNCACHED: // UNCACHED == 0 
        return 'UNCACHED'; 
        break; 
    case appCache.IDLE: // IDLE == 1 
        return 'IDLE'; 
        break; 
    case appCache.CHECKING: // CHECKING == 2 
        return 'CHECKING'; 
        break; 
    case appCache.DOWNLOADING: // DOWNLOADING == 3 
        return 'DOWNLOADING'; 
        break; 
    case appCache.UPDATEREADY: // UPDATEREADY == 4 
        return 'UPDATEREADY'; 
        break; 
    case appCache.OBSOLETE: // OBSOLETE == 5 
        return 'OBSOLETE'; 
        break; 
    default: 
        return 'UKNOWN CACHE STATUS'; 
        break; 
}; 

程式更新快取的方法。

// Check if a new cache is available on page load. 
window.addEventListener('load', function(e) 
{ 
    window.applicationCache.addEventListener('updateready', function(e) 
    { 
        if (window.applicationCache.status == window.applicationCache.UPDATEREADY) 
        { 
            // Browser downloaded a new app cache. 
            // Swap it in and reload the page to get the new hotness. 
            window.applicationCache.swapCache(); 
            if (confirm('A new version of this site is available. Load it?')) 
            { 
                window.location.reload(); 
            } 
        } 
        else 
        { 
            // Manifest didn't changed. Nothing new to server. 
        }                                                                                       
    }, false); 
}, false); 

總的來說,App Cache的三個優點非常明顯。但是有幾個坑,卻會導致沒人願意使用這個新特性。

1.使用了App Cache的頁面在清單檔案更新之後去更新頁面資源,但是隻在下次開啟頁面才能生效,這意味著,我們需要使用程式碼判斷是不是最新版本,不是的話,重新整理一次頁面。這種體驗很不好。

2.使用了App Cache的頁面也會被快取,這對於需要動態更新的頁面來說,幾乎是個噩夢。使用者訪問到的頁面不是最新的,會導致非常多的問題。

3.App cache與browser cache混合在一起會使更新機制變得更加複雜,主要有以下幾個因素:
1) App cache在各瀏覽器平臺實現上存在差異;
2) 各瀏覽器又提供了不同的頁面重新整理機制;
3) app cache還與傳統的browser cache有著千絲萬縷的聯絡;對於它倆如何協同工作,HTML5的相關規範沒有對app cache的細節給出非常明確的規定; 瀏覽器官方文件有沒有給出非常明確的說明。
4) browser cache的更新機制本身就已經很複雜。
5) 如果manifest檔案本身就有快取時間,或設定為永遠都可用,那你的網頁永遠都不會被更新了。

從驚歎於App Cache的強大,到填坑,W3C花了這麼長時間,就弄出來這麼個東西,真是令人失望。學會使用App Cache只用了不到一小時,填坑填了一下午。每個頁面都要有manifest,每個頁面都要加程式碼去判斷去更新快取,我可以罵人嗎。

(2)本地儲存(Local Storage)

這裡寫圖片描述

本地儲存發展歷史.

這裡寫圖片描述

最早的Cookies自然是大家都知道,問題主要就是太小,大概也就4KB的樣子,而且IE6只支援每個域名20個cookies,太少了。優勢就是大家都支援,而且支援得還蠻好。很早以前那些禁用cookies的使用者也都慢慢的不存在了,就好像以前禁用javascript的使用者不存在了一樣。

userData是IE的東西,垃圾。現在用的最多的是Flash吧,空間是Cookie的25倍,基本夠用。再之後Google推出了Gears,雖然沒有限制,但不爽的地方就是要裝額外的外掛(沒具體研究過)。到了HTML5把這些都統一了,官方建議是每個網站5MB,非常大了,就存些字串,足夠了。比較詭異的是居然所有支援的瀏覽器目前都採用的5MB,儘管有一些瀏覽器可以讓使用者設定,但對於網頁製作者來說,目前的形勢就5MB來考慮是比較妥當的。

首先自然是檢測瀏覽器是否支援本地儲存。在HTML5中,本地儲存是一個window的屬性,包括localStorage和sessionStorage,從名字應該可以很清楚的辨認二者的區別,前者是一直存在本地的,後者只是伴隨著session,視窗一旦關閉就沒了。二者用法完全相同,這裡以localStorage為例。

if(window.localStorage)
{
     alert('This browser supports localStorage');
}
else
{
     alert('This browser does NOT support localStorage');
}

儲存資料的方法就是直接給window.localStorage新增一個屬性,例如:window.localStorage.a 或者 window.localStorage[“a”]。它的讀取、寫、刪除操作方法很簡單,是以鍵值對的方式存在的,如下:

localStorage.a = 3;//設定a為"3"
localStorage["a"] = "sfsf";//設定a為"sfsf",覆蓋上面的值
localStorage.setItem("b","isaac");//設定b為"isaac"
var a1 = localStorage["a"];//獲取a的值
var a2 = localStorage.a;//獲取a的值
var b = localStorage.getItem("b");//獲取b的值
localStorage.removeItem("c");//清除c的值

這裡最推薦使用的自然是getItem()和setItem(),清除鍵值對使用removeItem()。如果希望一次性清除所有的鍵值對,可以使用clear()。另外,HTML5還提供了一個key()方法,可以在不知道有哪些鍵值的時候使用,如下:

var storage = window.localStorage;
function showStorage()
{
     for(var i=0;i<storage.length;i++)
     {
          //key(i)獲得相應的鍵,再用getItem()方法獲得對應的值
          document.write(storage.key(i)+ " : " + storage.getItem(storage.key(i)) + "<br>");
     }
}

寫一個最簡單的,利用本地儲存的計數器:

    var storage = window.localStorage;
    if (!storage.getItem("pageLoadCount")) 
    {
        storage.setItem("pageLoadCount",0);
    }
    storage.pageLoadCount = parseInt(storage.getItem("pageLoadCount")) + 1;//必須格式轉換
    document.getElementById("count").innerHTML = storage.pageLoadCount;
    showStorage();

不斷重新整理就能看到數字在一點點上漲,如下圖所示:

這裡寫圖片描述

需要注意的是,HTML5本地儲存只能存字串,任何格式儲存的時候都會被自動轉為字串,所以讀取的時候,需要自己進行型別的轉換。這也就是上一段程式碼中parseInt必須要使用的原因。

(3)索引資料庫(Indexed DB)

這裡寫圖片描述

從本質上說,IndexedDB允許使用者在瀏覽器中儲存大量的資料。任何需要傳送大量資料的應用都可以得益於這個特性,可以把資料儲存在使用者的瀏覽器端。當前這只是IndexedDB的其中一項功能,IndexedDB也提供了強大的基於索引的搜尋api功能以獲得使用者所需要的資料。

使用者可能會問:IndexedDB是和其他以前的儲存機制(如cookie,session)有什麼不同?

Cookies是最常用的瀏覽器端儲存資料的機制,但其儲存資料的大小有限制並且有隱私問題。Cookies並且會在每個請求中來回傳送資料,完全沒辦法發揮客戶端資料儲存的優勢。

再來看下Local Storage本地儲存機制的特點。Local Storage在HTML 5中有不錯的支援,但就總的儲存量而言依然是有所限制的。Local Storage並不提供真正的“檢索API”,本地儲存的資料只是通過鍵值對去訪問。Local Storage對於一些特定的需要儲存資料的場景是很適合的,例如,使用者的喜好習慣,而IndexedDB則更適合儲存如廣告等資料(它更象一個真正的資料庫)。

一般來說,有兩種不同型別的資料庫:關係型和文件型(也稱為NoSQL或物件)。關係資料庫如SQL Server,MySQL,Oracle的資料儲存在表中。文件資料庫如MongoDB,CouchDB,Redis將資料集作為個體物件儲存。IndexedDB是一個文件資料庫,它在完全內置於瀏覽器中的一個沙盒環境中(強制依照(瀏覽器)同源策略)。

對資料庫的每次操作,描述為通過一個請求開啟資料庫,訪問一個object store,再繼續。

開啟資料庫的請求生命週期

這裡寫圖片描述

IndexedDB是否適合我的應用程式?

現在最關鍵的問題:“IndexedDB是否適合我的應用程式?“像往常一樣,答案是肯定的:“視情況而定。“首先當你試圖在客戶端儲存資料時,你會考慮HTML5本地儲存。本地儲存得到廣泛瀏覽器的支援,有非常易於使用的API。簡單有其優勢,但其劣勢是無法支援複雜的搜尋策略,儲存大量的資料,並提供事務支援。

IndexedDB是一個數據庫。所以,當你想為客戶端做出決定,考慮你如何在服務端選擇一個持久化介質的資料庫。你可能會問自己一些問題來幫助決定客戶端資料庫是否適合您的應用程式,包括:

  • 你的使用者通過瀏覽器訪問您的應用程式,(瀏覽器)支援IndexedDB API嗎 ?
  • 你需要儲存大量的資料在客戶端?
  • 你需要在一個大型的資料集合中快速定位單個數據點?
  • 你的架構在客戶端需要事務支援嗎?

    如果你對其中的任何問題回答了“是的”,很有可能,IndexedDB是你的應用程式的一個很好的候選。

IndexedDB用法

現在,你已經有機會熟悉了一些的整體概念,下一步是開始實現基於IndexedDB的應用程式。第一個步驟需要統一IndexedDB在不同瀏覽器的實現。您可以很容易地新增各種廠商特性的選項的檢查,同時在window物件上把它們設定為官方物件相同的名稱。下面的清單展示了window.indexedDB,window.IDBTransaction,window.IDBKeyRange的最終結果是如何都被更新,它們被設定為相應的瀏覽器的特定實現。

        window.indexedDB = window.indexedDB ||
                           window.mozIndexedDB ||
                           window.webkitIndexedDB ||
                           window.msIndexedDB;

        window.IDBTransaction = window.IDBTransaction ||
                           window.webkitIDBTransaction ||
                           window.msIDBTransaction;

        window.IDBKeyRange = window.IDBKeyRange ||
                           window.webkitIDBKeyRange ||
                           window.msIDBKeyRange;

現在,每個資料庫相關的全域性物件持有正確的版本,應用程式可以準備使用IndexedDB開始工作。

開啟資料庫

// 開啟我們的資料庫,資料庫名稱,版本號
var request = indexedDB.open("MyTestDatabase", 3);

indexedDB的三個事件

//資料庫開啟成功執行
request.onsuccess = function (event) 
{
      // Better use "this" than "req" to get the result to avoid problems with
      // garbage collection.
      // db = request.result;
      db = this.result;
      console.debug("initDb DONE");
};
//資料庫開啟失敗執行
request.onerror = function (event) 
{
      console.error("initDb:", evt.target.errorCode);
};
//在資料庫第一次被開啟時或者當指定的版本號高於當前被持久化的資料庫的版本號時,觸發此事件,可以在這個地方建立物件儲存空間結構,更新結構,加索引等.
request.onupgradeneeded = function (event) 
{
      console.debug("initDb.onupgradeneeded");
      var store = event.currentTarget.result.createObjectStore(
        DB_STORE_NAME, { keyPath: 'id', autoIncrement: true });
};

新增資料或更新資料

// 我們的客戶資料看起來像這樣。
const customerData = [
  { ssn: "444-44-4444", name: "Bill", age: 35, email: "[email protected]" },
  { ssn: "555-55-5555", name: "Donna", age: 32, email: "[email protected]" }
];

var transaction = db.transaction(["customers"], "readwrite");

// 當所有的資料都被增加到資料庫時執行一些操作
transaction.oncomplete = function(event) {
  alert("All done!");
};

transaction.onerror = function(event) {
  // 不要忘記進行錯誤處理!
};

var objectStore = transaction.objectStore("customers");
for (var i in customerData) 
{
  var request = objectStore.add(customerData[i]);
  request.onsuccess = function(event) 
  {
    // event.target.result == customerData[i].ssn
  };
}

刪除資料

var request = db.transaction(["customers"], "readwrite")
                .objectStore("customers")
                .delete("444-44-4444");
request.onsuccess = function(event) {
  // 刪除資料成功!
};

獲取資料

var transaction = db.transaction(["customers"]);
var objectStore = transaction.objectStore("customers");
var request = objectStore.get("444-44-4444");
request.onerror = function(event) 
{
  // 錯誤處理!
};
request.onsuccess = function(event) 
{
  // 對 request.result 做些操作!
  alert("Name for SSN 444-44-4444 is " + request.result.name);
};

對於一個“簡單”的提取這裡的程式碼有點多了。下面看我們怎麼把它再縮短一點,假設你在資料庫的級別上來進行的錯誤處理:

db.transaction("customers").objectStore("customers").get("444-44-4444").onsuccess = function(event) 
{
  alert("Name for SSN 444-44-4444 is " + event.target.result.name);
};

使用遊標獲取資料

使用 get() 要求你知道你想要檢索哪一個鍵。如果你想要遍歷物件儲存空間中的所有值,那麼你可以使用遊標。看起來會像下面這樣:

var customers = [];

var objectStore = db.transaction("customers").objectStore("customers");

objectStore.openCursor().onsuccess = function(event) 
{
  var cursor = event.target.result;
  if (cursor) 
  {
    customers.push(cursor.value);
    cursor.continue();
  }
  else 
  {
    alert("Got all customers: " + customers);
  }
};

這裡有一個封裝好的完整的例子,簡化了這些操作。

<script type="text/javascript">

        window.indexedDB = window.indexedDB ||
                           window.mozIndexedDB ||
                           window.webkitIndexedDB ||
                           window.msIndexedDB;

        window.IDBTransaction = window.IDBTransaction ||
                           window.webkitIDBTransaction ||
                           window.msIDBTransaction;

        window.IDBKeyRange = window.IDBKeyRange ||
                           window.webkitIDBKeyRange ||
                           window.msIDBKeyRange;

        (function(window){

            'use strict';

            var db = {

                version: 1, // important: only use whole numbers!

                objectStoreName: 'tasks',

                instance: {},

                upgrade: function (e) {

                    var
                        _db = e.target.result,
                        names = _db.objectStoreNames,
                        name = db.objectStoreName;

                    if (!names.contains(name)) {

                        _db.createObjectStore(
                            name,
                            {
                                keyPath: 'id',
                                autoIncrement: true
                            });
                    }
                },

                errorHandler: function (error) {
                    window.alert('error: ' + error.target.code);
                    debugger;
                },

                open: function (callback) {

                    var request = window.indexedDB.open(
                        db.objectStoreName, db.version);

                    request.onerror = db.errorHandler;

                    request.onupgradeneeded = db.upgrade;

                    request.onsuccess = function (e) {

                        db.instance = request.result;

                        db.instance.onerror =
                            db.errorHandler;

                        callback();
                    };
                },

                getObjectStore: function (mode) {

                    var txn, store;

                    mode = mode || 'readonly';

                    txn = db.instance.transaction(
                        [db.objectStoreName], mode);

                    store = txn.objectStore(
                        db.objectStoreName);

                    return store;
                },

                save: function (data, callback) {

                    db.open(function () {

                        var store, request,
                            mode = 'readwrite';

                        store = db.getObjectStore(mode),

                        request = data.id ?
                            store.put(data) :
                            store.add(data);

                        request.onsuccess = callback;
                    });
                },

                getAll: function (callback) {

                    db.open(function () {

                        var
                            store = db.getObjectStore(),
                            cursor = store.openCursor(),
                            data = [];

                        cursor.onsuccess = function (e) {

                            var result = e.target.result;

                            if (result &&
                                result !== null) {

                                data.push(result.value);
                                result.continue();

                            } else {

                                callback(data);
                            }
                        };

                    });
                },

                get: function (id, callback) {

                    id = parseInt(id);

                    db.open(function () {

                        var
                            store = db.getObjectStore(),
                            request = store.get(id);

                        request.onsuccess = function (e){
                            callback(e.target.result);
                        };
                    });
                },

                'delete': function (id, callback) {

                    id = parseInt(id);

                    db.open(function () {

                        var
                            mode = 'readwrite',
                            store, request;

                        store = db.getObjectStore(mode);

                        request = store.delete(id);

                        request.onsuccess = callback;
                    });
                },

                deleteAll: function (callback) {

                    db.open(function () {

                        var mode, store, request;

                        mode = 'readwrite';
                        store = db.getObjectStore(mode);
                        request = store.clear();

                        request.onsuccess = callback;
                    });

                }
            };

            window.app = window.app || {};
            window.app.db = db;

        }(window));


        //將資料顯示在頁面上的方法
        var bindData = function (data) {

            $("#IndexedDB").html('');

            if(data.length === 0){
                $("#IndexedDB").html("沒有資料");
                return;
            }

            data.forEach(function (note) {
                $("#IndexedDB").append(note.id+","+note.title+","+note.text);
            });
        };

        //一個新資料
        var note = 
        {
                id:1,
                title: "資料1",
                text: "資料1的另一個欄位"
        };
        //又一個新資料
        var note2 = 
        {
                id:2,
                title: "資料2",
                text: "資料2的另一個欄位"
        };
        //插入或更新資料(insert or update)
        window.app.db.save(note,function()
        {
            //window.app.db.getAll(bindData);
        });
        window.app.db.save(note2);

        //獲取資料
        window.app.db.get(1,function(item)
        {
            //alert("window.app.db.get:"+item.id+","+item.title+","+item.text);
        });

        //刪除資料
        window.app.db.delete(1,function()
        {
            //alert("window.app.db.get:"+item.id+","+item.title+","+item.text);
        }); 

        //刪除所有
        window.app.db.deleteAll(function()
        {
            //alert("window.app.db.get:"+item.id+","+item.title+","+item.text);
        });

</script>

想對IndexedDB更多用法進行了解的可以參考這兩篇文章。

除了IndexedDB以外,還有一種已經被W3C放棄的Web SQL。

瀏覽器支援本地資料庫並不是從IndexedDB才開始實現,它是在Web SQL實現之後的一種新方法。類似IndexedDB,Web SQL是一個客戶端資料庫,但它作為一個關係資料庫的實現,使用結構化查詢語言(SQL)與資料庫通訊。Web SQL的歷史充滿了曲折,但底線是沒有主流的瀏覽器廠商對Web SQL繼續支援。

如果Web SQL實際上是一個廢棄的技術,為什麼還要提它呢?有趣的是,Web SQL在瀏覽器裡得到穩固的支援。Chrome, Safari, iOS Safari, and Android 瀏覽器都支援。另外,並不是這些瀏覽器的最新版本才提供支援,許多這些最新最好的瀏覽器之前的版本也可以支援。有趣的是,如果你為Web SQL新增支援來支援IndexedDB,你突然發現,許多瀏覽器廠商和版本成為支援瀏覽器內建資料庫的某種化身。

因此,如果您的應用程式真正需要一個客戶端資料庫,你想要達到的最高級別的採用可能,當IndexedDB不可用時,也許您的應用程式可能看起來需要選擇使用Web SQL來支援客戶端資料架構。雖然文件資料庫和關係資料庫管理資料有鮮明的差別,但只要你有正確的抽象,就可以使用本地資料庫構建一個應用程式。

(4)檔案介面(File API)

這裡寫圖片描述

在之前我們操作本地檔案都是使用flash、silverlight或者第三方的activeX外掛等技術,由於使用了這些技術後就很難進行跨平臺、或者跨瀏覽器、跨裝置等情況下實現統一的表現,從另外一個角度來說就是讓我們的web應用依賴了第三方的外掛,而不是很獨立,不夠通用。在HTML5標準中,預設提供了操作檔案的API讓這一切直接標準化。有了操作檔案的API,讓我們的Web應用可以很輕鬆的通過JS來控制檔案的讀取、寫入、資料夾、檔案等一系列的操作。

先看一個demo。之前我們操作一個圖片檔案,都是先將圖片上傳到伺服器端,然後再使用一個img標籤指向到伺服器的url地址,然後再進行一個使用第三方外掛進行圖片處理,而現在這一切都不需要伺服器端了,因為FileReader物件提供的幾個讀取檔案的方法變得異常簡單,而且全部是客戶端js的操作。

現在我們自己來使用這些API。

先寫好我們的HTML頁面。

    <input type="file" multiple="multiple" name="fileDemo" id="fileDemo" />
    <br />
    <input type="button" value="獲取檔案的名字" id="btnGetFile" />
    <input type="button" value="readAsDataURL" id="readAsDataURL" onclick="showDataByURL();" />
    <input type="button" value="readAsBinaryString" id="readAsBinaryString" onclick="showDataByBinaryString();" />
    <input type="button" value="readAsText" id="readAsText" onclick="showDataByText();" />
    <div id="result"></div>

獲取檔名

            $("#btnGetFile").click(function(e)
            {
                var fileList = document.getElementById("fileDemo").files;
                for (var i = 0; i < fileList.length; i++)
                {
                    if (!(/image\/\w+/.test(fileList[i].type)))
                    {
                        $("#result").append("<span>type:" + fileList[i].type + "--******非圖片型別*****--name:" + fileList[i].name + "--size:" + fileList[i].size + "</span><br />");
                    }
                    else
                    {
                        $("#result").append("<span>type:" + fileList[i].type + "--name:" + fileList[i].name + "--size:" + fileList[i].size + "</span><br />");
                    }
                }
            });

readAsDataURL()

開始讀取指定的Blob物件或File物件中的內容. 當讀取操作完成時,readyState屬性的值會成為DONE,如果設定了onloadend事件處理程式,則呼叫之.同時,result屬性中將包含一個data: URL格式的字串以表示所讀取檔案的內容.

這個方法很有用,比如,可以實現圖片的本地預覽.

        function showDataByURL()
        {
            var resultFile = document.getElementById("fileDemo").files[0];
            if (resultFile)
            {
                var reader = new FileReader();

                reader.readAsDataURL(resultFile);
                reader.onload = function(e)
                {
                    var urlData = this.result;
                    document.getElementById("result").innerHTML += "<img src='" + urlData + "' alt='" + resultFile.name + "' />";
                };

            }

        }

readAsBinaryString()

開始讀取指定的Blob物件或File物件中的內容. 當讀取操作完成時,readyState屬性的值會成為DONE,如果設定了onloadend事件處理程式,則呼叫之.同時,result屬性中將包含所讀取檔案的原始二進位制資料.


        function showDataByBinaryString()
        {
            var resultFile = document.getElementById("fileDemo").files[0];
            if (resultFile)
            {
                var reader = new FileReader();
                //非同步方式,不會影響主執行緒
                reader.readAsBinaryString(resultFile);

                reader.onload = function(e)
                {
                    var urlData = this.result;
                    document.getElementById("result").innerHTML += urlData;
                };
            }
        }

readAsText()

開始讀取指定的Blob物件或File物件中的內容. 當讀取操作完成時,readyState屬性的值會成為DONE,如果設定了onloadend事件處理程式,則呼叫之.同時,result屬性中將包含一個字串以表示所讀取的檔案內容.


        function showDataByText()
        {
            var resultFile = document.getElementById("fileDemo").files[0];
            if (resultFile)
            {
                var reader = new FileReader();

                reader.readAsText(resultFile, 'gb2312');
                reader.onload = function(e)
                {
                    var urlData = this.result;
                    document.getElementById("result").innerHTML += urlData;
                };
            }
        }

事件處理程式

onabort
當讀取操作被中止時呼叫.
onerror
當讀取操作發生錯誤時呼叫.
onload
當讀取操作成功完成時呼叫.
onloadend
當讀取操作完成時呼叫,不管是成功還是失敗.該處理程式在onload或者onerror之後呼叫.
onloadstart
當讀取操作將要開始之前呼叫.
onprogress
在讀取資料過程中週期性呼叫.

在檔案上傳時,HTML5還支援拖拽功能。

這裡寫圖片描述

<div id="holder"></div> 

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

holder.ondrop = function (e) 
{
  e.preventDefault();

  var file = e.dataTransfer.files[0],
      reader = new FileReader();

  reader.onload = function (event) 
  {
    console.log(event.target);
    holder.style.background = 'url(' + event.target.result + ') no-repeat center';
  };

  console.log(file);
  reader.readAsDataURL(file);

  return false;
};

想對HTML5檔案介面操作檔案瞭解更多請參考下面的文章:

③ 裝置訪問特性 (DEVICE ACCESS)

這裡寫圖片描述

來一段W3C對這個特性的介紹。

Beginning with the Geolocation API, Web Applications can present rich, device-aware features and experiences. Incredible device access innovations are being developed and implemented, from audio/video input access to microphones and cameras, to local data such as contacts & events, and even tilt orientation.

大致包含 地理位置API媒體訪問API訪問聯絡人及事件裝置方向

下面分別進行研究。

(1)地理位置API(Geolocation API)

這裡寫圖片描述

HTML5 Geolocation API 用於獲得使用者的地理位置。
鑑於該特性可能侵犯使用者的隱私,除非使用者同意,否則使用者位置資訊是不可用的。一般網頁在呼叫此資訊時,會彈出許可權申請視窗。

如果電腦獲取不到位置資訊的話,就在手機上試一下吧。

getCurrentPosition()

getCurrentPosition() 方法來獲得使用者的位置。

標準用法如下:

<script>
    var x=document.getElementById("demo");
    function getLocation()
    {
      if (navigator.geolocation)
      {
          navigator.geolocation.getCurrentPosition(showPosition);
      }
      else
      {
          x.innerHTML="Geolocation is not supported by this browser.";
      }
    }
    function showPosition(position)
    {
        x.innerHTML="Latitude: " + position.coords.latitude + "<br />Longitude: " + position.coords.longitude;
    }
</script>

-檢測是否支援地理定位
-如果支援,則執行 getCurrentPosition() 方法。如果不支援,則向用戶顯示一段訊息。
-如果getCurrentPosition()執行成功,則向引數showPosition中規定的函式返回一個coordinates物件
-showPosition() 函式獲得並顯示經度和緯度

上面的例子是一個非常基礎的地理定位指令碼,不含錯誤處理。

完整的處理應該是這樣的.

    <script>
        var x = document.getElementById("demo");
        function getLocation()
        {
            if (navigator.geolocation)
            {
                navigator.geolocation.getCurrentPosition(showPosition, showError);
            }
            else
            {
                x.innerHTML = "Geolocation is not supported by this browser.";
            }
        }
        function showPosition(position)
        {
            x.innerHTML = "Latitude: " + position.coords.latitude + "<br />Longitude: " + position.coords.longitude;
        }
        function showError(error)
        {
            switch (error.code)
            {
                case error.PERMISSION_DENIED:
                    x.innerHTML = "User denied the request for Geolocation."
                    break;
                case error.POSITION_UNAVAILABLE:
                    x.innerHTML = "Location information is unavailable."
                    break;
                case error.TIMEOUT:
                    x.innerHTML = "The request to get user location timed out."
                    break;
                case error.UNKNOWN_ERROR:
                    x.innerHTML = "An unknown error occurred."
                    break;
            }
        }
    </script>

錯誤程式碼:
-Permission denied - 使用者不允許地理定位
-Position unavailable - 無法獲取當前位置
-Timeout - 操作超時
-Unknown error - 未知錯誤