65.9K
CodeProject 正在变化。 阅读更多。
Home

HTML5 快速入门 Web 应用程序

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.91/5 (105投票s)

2013 年 2 月 15 日

CPOL

11分钟阅读

viewsIcon

390294

downloadIcon

11843

HTML5 自学中心 - 探索和理解新特性!

背景

从去年 12 月开始,我开始更多地学习 HTML5。由于它是新的、被讨论过的并且具有非常好的功能,所以我开始考虑在我们的项目中实际使用它,并可能在我对 HTML5 有一些深入了解后,向团队进行一次关于 HTML5 的演示。在学习 HTML5 的过程中,我开始开发一个演示 Web 应用程序,该应用程序完全基于 HTML5,并包含我们将在本文中讨论的所有主要新功能。

一旦我想到了快速简便地分享这些功能的想法,我就尝试开发一个支持 HTML5 的 ASP.NET 应用程序,它能够充分解释和展示新的 HTML5 功能——有点像一个自启动工具包,可以在下载后立即查看和体验功能实现。

目录

   

引言

HTML5 是 HTML 标准的第五个修订版,也是最新的版本。之前的版本(HTML 4.01)于 1999 年发布。2006 年,WHATWG(Web 超文本应用程序技术工作组)和 W3C(万维网联盟)共同定义了新版本的 HTML。早些时候,WHATWG 致力于 Web 表单和应用程序,而 W3C 则致力于 XHTML 2.0。

他们讨论并制定了HTML 设计原则。这些原则旨在为用户提供更好的体验,并便于开发人员采用,同时独立于设备和插件,并具有优雅的后备机制。HTML5 规范于去年 12 月 17 日定稿。

工具

HTML5 仍在开发中。浏览器根据自己的计划实现 HTML5 规范,因此我们在不同浏览器上具有不同级别的支持。

为了了解特定浏览器支持哪些功能,一些网站详细分享了 HTML5 与不同浏览器版本兼容性的信息。

  • HTML5 Test - 提供 HTML5 分数以及可以使用哪些功能的详细信息。
  • Can I Use - 提供不同功能的主要浏览器之间的表格比较。

目前,不同浏览器的支持分数如下。

   

为了针对各种浏览器进行开发,我们可以使用**用户代理切换器 (User Agent Switcher)**。

为了在开发时拥有一个后备计划,我们需要首先检查正在使用的浏览器是否支持某个 HTML5 功能。创建 JavaScript 中的动态元素并检查其任何属性是否存在之类的传统方法就足够了。另一种选择是使用开源的JavaScript 库 Modernizr(MIT 许可)。它的使用方式如下:

<script src="modernizr.min.js"></script>
<script type="text/javascript">
if (Modernizr.canvas) {
        // Congrats! Canvas is supported on your browser.
} else {
        // Sorry! No native canvas support available on your browser.
}
</script>

为了查看网页的内部细节,探索和理解浏览器客户端正在发生什么,浏览器的**开发者工具**非常有用。

   

结构

HTML5 的设计使其不基于 SGML(标准通用标记语言),因此不需要引用 DTD(文档类型定义)。因此,要将 HTML 版本指令传递给 Web 浏览器,以下方法有效:

<!DOCTYPE HTML>

网页中声明字符编码的新简写方式。

<meta charset="utf-8">

继续前进,HTML5 具有新的网页元素,这些元素为页面增加了语义。例如:

  • 标题
  • Nav
  • 页脚
  • 文章

以下是与 HTML4 设计相比的示例网页结构比较。

  

这里是使用新的结构语义的示例代码片段。

<hgroup>
    <h1>Penning Everything Interesting…</h1>
</hgroup>
<article id="MyBlog">
    <header>
        <h2>My first blog entry!</h2>
    </header>
    <p>I was thinking what to write about. I read few articles at times that I feel like sharing across, probably ...</p>
    <br/>
    <footer>
    <section>
        <h4>One Thought on 'My first blog entry…'</h4>
        <article id="comment_1">
            <link href="#comment_1" />
            <h5>Comment by: Guruji</h5>
            <p>Very well written. Love the enthu and energy you have for sharing your knowledge...</p>
        </article>
        <article id="comment_2">
            <link href="#comment_2" />
            <h5>Comment by: SuperStar</h5>
            <p>Looks like a good start...Good going!</p>
        </article>
    </section>
    </footer>
</article>

除了新增内容,一些元素和属性已被弃用,并且根据规范不再存在。

开发者不应使用上述标签。但是,已决定用户代理必须支持它们。

表单

HTML5 中定义了各种新的基于表单的标签和属性,例如:

  • 输入类型和属性
    • email
    • url
    • number
    • 日期时间
    • search
    • range
    • color
  • 控件元素
    • datalist
    • keygen
    • 输出
  • 属性
    • 拼写检查
    • 自动对焦
    • 列表
    • required
    • placeholder
    • 最小和最大
    • multiple
  • 事件
    • 方向更改
    • resize
    • 在线
    • 离线
  






使用它们的一些示例。

email: 	  <input type="email" multiple required autofocus placeholder="sample@sample.com" />                
url: 	  <input type="url" />
number:    <input type="number" />
date time: <input type="date"/>
	  <input type="time"/>
search: 	  <input type="search"/>
range: 	  <input type="range" max="100" min="80" step="2" title="Your AES score" name="rscore"/>
	  <output name="score">90</output>
color: 	  <input type="color"/>
datalist:  <input type="text" id="somenames" list="fullList" />
	  <datalist id="fullList">
	      <option value="Sandeep">
	      <option value="Deepak">
	      <option value="Sandy">
	      <option value="Deepu">                                            
	  </datalist>
spellcheck:<input type="text" spellcheck="false" placeholder="Without Spellcheck">
	  <input type="text" spellcheck="true" placeholder="With Spellcheck">

此外,HTML5 通过新的选择器改进了查找控件的方式。就像 jQuery 选择器一样,HTML5 有两个新的选择器,有助于快速找到所需的控件。

  • document.querySelector() - 返回页面中与指定选择器规则匹配的第一个元素。
    // Returns the first element in the document with the class "myclass"                              
    var el = document.querySelector(".myclass");
  • document.querySelectorAll() - 返回所有与指定规则匹配的元素。
    // Returns the list of all the element in the document with the class "myclass"
    var els = document.querySelectorAll(".myclass");

拖放

HTML5 已将拖放功能作为标准的一部分,任何元素都可以被设置为可拖动,然后拖放到指定位置。与之相关的属性、方法和事件是:

  
  • 可拖动
  • ondragstart
  • setData
  • getData
  • ondragover
  • ondrop

上述 logo 拖放的示例代码如下:

function allowDropDragOver(e) {
    e.preventDefault();
}

function dragStart(e) {
    e.dataTransfer.setData("dragItemID", e.target.id);
}

function dropIt(e) {
    e.preventDefault();
    var dataId = e.dataTransfer.getData("dragItemID");
    e.target.appendChild( document.getElementById(dataId));
}

<table>
    <tr>
        <td>
            <div id="bin1" ondrop="dropIt(event)" ondragover="allowDropDragOver(event)" class="dragDropBin">
                <img id="dragImage" src="../Images/HTML5LogoSmall.png" 
                     draggable="true" ondragstart="dragStart(event)" class="dragImage">
            </div>
        </td>
        <td><div id="bin2" ondrop="dropIt(event)" ondragover="allowDropDragOver(event)" class="dragDropBin"></div></td>
    </tr>
    <tr>
        <td><div id="bin3" ondrop="dropIt(event)" ondragover="allowDropDragOver(event)" class="dragDropBin"></div></td>
        <td><div id="bin4" ondrop="dropIt(event)" ondragover="allowDropDragOver(event)" class="dragDropBin"></div></td>
    </tr>
</table>

音频和视频

这是 HTML5 的主要功能之一,它在网页中的音频或视频功能方面带来了巨大的变化。它们将取代基于性能的插件(如 Flash 或 Silverlight)的使用。猜猜看,实现起来非常简单,只需几行代码。与之相关的属性、方法和事件是:

  • 属性和方法
    • play():播放媒体。
    • pause():暂停媒体。
    • canPlayType():检查浏览器是否能播放某种媒体资源类型。
    • src:媒体文件的路径。也可以使用 `<source>` 元素。
    • autoplay:自动播放媒体。
    • controls:在媒体上显示控件(播放/暂停/音量等)。
    • loop:循环播放。
    • muted:静音。
    • playbackRate:改变媒体的播放速度。
  • 事件
    • play:媒体播放时触发。
    • pause:媒体暂停时触发。
    • timeupdate:当前播放位置更改时触发。
    • volumechange:媒体音量更改时触发。
    • ratechange:媒体播放速率更改时触发。
    • ended:媒体播放完毕时触发。
    • seeking:当最终用户在媒体中进行查找时触发。
    • seeked:当查找属性变为 false 时触发。
  

目前,不同浏览器支持的格式已定义。基于 HTML5 的支持格式是:

  • 音频:MP3、Wav 和 Ogg。
  • 视频:MP4、WebM 和 Ogg。

音频和视频实现的示例代码如下:

<audio id="sampleAudio" controls>
    <source src="../Files/AudioSample.mp3">
    <source src="../Files/AudioSample.wav">
    <source src="../Files/AudioSample.ogg">  
    Your browser does not support the audio element.          
</audio>

<video id="sampleVideo" controls src="../Files/VideoSample.mp4">
     <track src="../Files/sampleSubtitles.vtt" srclang="en" kind="subtitles" label="English subtitles">
    Your browser does not support the audio element.
</video>

<!-- For runtime button based operations -->
function CMute(controlId) {
    $(controlId).attr('muted', true);
}

function CUnMute(controlId) {
    $(controlId).attr('muted', false);
}

function CPause(controlId) {
    if (!($(controlId).get(0).paused))
        $(controlId).get(0).pause();
}

function CPlay(controlId) {
    $(controlId).get(0).play();
}

function CFullScreen(controlId) {
    // Only Chrome code currently. 
    $(controlId)[0].webkitRequestFullScreen();
}

<input type="button" value="Mute" onclick="CMute(sampleAudio);"/>
<input type="button" value="Unmute" onclick="CUnMute(sampleAudio);"/>
<input type="button" value="Pause" onclick="CPause(sampleAudio);"/>
<input type="button" value="Play" onclick="CPlay(sampleAudio);"/>

地理位置

HTML5 Geolocation 用于定位用户的地理位置。由于这与特定个人使用相关,可能会损害用户隐私。因此,定位位置需要用户许可。为了使用 Geolocation,我们需要了解 `navigator.geolocation` 对象、它的方法和参数。

  • 方法
    • getCurrentPosition:只获取一次位置。
    • watchPosition:定期轮询。
    • clearWatch:停止监视位置。
  • 参数
    • 位置
      • coords(坐标):包含用户位置信息。
        • 纬度
        • 经度
        • 海拔
        • 精度
        • 海拔精度
        • 速度
        • heading
      • timestamp(DOMTimeStamp):获取用户位置的时间。
    • PositionError:错误回调对象参数。
      • UNKNOWN_ERROR = 0
      • PERMISSION_DENIED = 1
      • POSITION_UNAVAILABLE = 2
      • TIMEOUT = 3
      • code
      • message
    • PositionOptions:指定获取用户位置时的选项。
      • enableHighAccuracy
      • timeout
      • maximumAge
  



地理位置 - 纬度与经度的示例代码如下:

// navigator.geolocation.getCurrentPosition(success_callback, error_callback, {other parameters}); 
// The watchPosition() function has the same structure as getCurrentPosition()
function getLocation() {
    if (navigator.geolocation) {
        navigator.geolocation.getCurrentPosition(updateLocation, handleLocationError, {
            timeout: 1000000,
            maximumAge: 1000,
            enableHighAccuracy: false
        });
    }
    else {
        document.getElementById("supportstatus").innerHTML = "HTML5 Geolocation is not supported in your browser.";
    }
}

function updateLocation(position) {
    var latitude = position.coords.latitude;
    var longitude = position.coords.longitude;
    var timeTaken = new Date(position.timestamp);
    ...
}

function handleLocationError(error) {
    switch(error.code)
    {
        ...
    }
}

地理位置 - 地图显示的示例代码如下:

<div id="mapView" class="mapDimension1"></div>

// include the Maps API JavaScript using a script tag.
<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>

// navigator.geolocation.getCurrentPosition
function showMap() {
    if (navigator.geolocation) {
        navigator.geolocation.getCurrentPosition(showMapLocation, handleLocationError, {
            timeout: 1000000,
            maximumAge: 1000,
            enableHighAccuracy: true
        });
    }
}

// create an object literal to hold map properties and create a "map" object & "marker" objects
function showMapLocation(position) {
    var latlng = new google.maps.LatLng(position.coords.latitude, position.coords.longitude);
    var myOptions = {
        zoom: 16,
        center: latlng,
        mapTypeControl: false,
        navigationControlOptions: { style: google.maps.NavigationControlStyle.SMALL },
        mapTypeId: google.maps.MapTypeId.ROADMAP
    };
    var map = new google.maps.Map(document.getElementById("mapView"), myOptions);

    var marker = new google.maps.Marker({
            position: latlng,
            map: map,
            title: "You are here! (Accuracy: " + position.coords.accuracy + " meter radius)"
        });
}

Web 存储

HTML5 在浏览器内提供了更好的本地存储。目前,我们使用 cookie 在客户端浏览器中存储信息,但存在一些限制,例如它们速度慢、未加密、限制在约 4KB 并且需要包含在每个 HTTP 请求中。Web Storage 更安全、更快速。它在客户端具有良好的存储空间(约 5MB),而不会影响网站的性能。它会持久化,并且仅在需要时才传输到服务器。目前,它支持两种类型的存储:

  • localStorage:存储没有到期日的数据。
    • 键/值对存储系统。
    • 对于同一域,在窗口/选项卡之间共享。
    • 数据在用户显式删除之前一直可用。
  • sessionStorage:存储一个会话的数据。
    • 键/值对存储系统。
    • 特定于特定窗口/选项卡。

Web Storage 遵循存储接口,该接口公开 API 以在客户端创建、读取、更新或删除数据。

  • getItem(DOMString key):获取由其键标识的项目的值。
  • setItem(DOMString key, any data):用于创建或修改项目。
  • removeItem(DOMString key):删除由其键标识的项目。
  • clear():删除所有项目。
  • length:项目数量。
  • key(unsigned long index):获取具有零基索引的项目键。
  

实现 Web Storage 的示例代码如下。

function SaveToSessionStorage() {
    window.sessionStorage.mySessionKeyX = 'Saved Directly in Session';
    window.sessionStorage.setItem("mySessionKeyY" ,"Saved Using Set Item");
}

function SaveToLocalStorage() {
    window.localStorage.myLocalKeyA = 'Saved Directly in Local';
    window.localStorage.setItem("myLocalKeyB", "Saved Using Set Item");
}

function GetFromSessionStorage() {
    alert("Value X: " + window.sessionStorage.getItem("mySessionKeyX"));
}

function GetFromLocalStorage() {
    alert("Value B: " + window.localStorage.getItem("myLocalKeyB"));
}

function RemoveFromSessionStorage() {
    window.sessionStorage.removeItem("mySessionKeyX");
}

function RemoveFromLocalStorage() {
    window.localStorage.removeItem("myLocalKeyB");
}

function clearAll() {
    localStorage.clear();
    sessionStorage.clear();
}

Web Workers

HTML5 Web Workers 是一个 JavaScript 脚本,在从网页执行后在后台运行。它独立于其他用户脚本运行,而不会影响页面的性能。

在 HTML 页面中执行脚本时,页面将保持无响应状态,直到脚本完成。Web Workers 允许在主浏览器进程的并行后台进程中执行任务。因此,即使在运行 Worker 时,页面也保持响应。由于 Web Workers 在外部文件中,它们无法访问以下 JavaScript 对象:

  • window 对象。
  • document 对象。
  • parent 对象。
  

实现 Worker 的示例代码如下。

// create a new Worker object 
var worker = new Worker('WebWorkerSample.js');

// start it by calling the postMessage() method
worker.postMessage(); 

// Communication between a worker and its parent page is done using an event model via postMessage() 
// Main script:
worker.addEventListener('message', function(e) {
    console.log('Worker said: ', e.data);
}, false);

//Internal Script (the worker):
self.addEventListener('message', function(e) {
    self.postMessage(e.data);
}, false);

Web Socket

HTML5 Web Socket 通过一个 TCP 连接(套接字)提供双向通信。WebSocket API 标准由 W3C(万维网联盟)定义,WebSocket 协议已由 IETF(互联网工程任务组)标准化。为此,引入了新协议:`ws:` 和 `wss:`。

它简化了客户端-服务器通信的复杂性。为了建立客户端和服务器之间的连接,第一次将请求发送到服务器时,它将作为来自 HTTP 调用的升级请求发送。一旦升级并成功握手,客户端和服务器就可以使用帧(2 字节)进行通信。服务器会通知客户端新消息。它的通用用例是:

  • 创建 Web Socket。
  • 定义事件监听器。
  • 发送消息。

与之相关的属性、方法和事件是:

  
  • readyState
  • bufferedAmount
  • onopen
  • onmessage
  • onclose
  • onerror
  • send()
  • close()

与此功能相关的示例代码如下。

function WebSocketTest() {
    if ("WebSocket" in window) {
        // Open a Web Socket
        var ws = new WebSocket("ws://myServer/HTML5Demo/");
        ws.onopen = function () {
            // Send data
            ws.send("Message sent to server");
            alert("Message was sent to server.");
        };
        ws.onmessage = function (evt) {
            //Recieve data
            var receivedmsg = evt.data;
            alert("Message is received from server"+ receivedmsg);
        };
        ws.onclose = function () {
            // Close a Websocket
            alert("Connection is closed.");
        };
        ws.onerror = function (evt) {
            // Log Error
            alert("ERROR!!! " + evt.message);
        };
    }
}

它高效、低延迟(几乎瞬时)且体积小。可以轻松基于它构建可扩展的实时应用程序。

画布

Canvas 是 HTML5 的另一项功能,用于在运行时在网页上绘制图形。Canvas 只是图形的容器,整个绘图通过 JavaScript 完成。因此,必须定义 canvas 的 ID、高度和宽度。它具有通过路径绘制 2D 形状、文本、图像的方法。

它的通用用例是:

  • 定义 `canvas` 元素。
  • 获取 2D `context`。
  • 绘制。

Canvas 提供了允许修改变换矩阵的方法。

  • save() 和 `restore()` - 保存和恢复不同的变换状态。
  • translate() - 平移 HTML5 Canvas context。
  • rotate() - 旋转 HTML5 Canvas。
  • scale() - 缩放 HTML5 Canvas。
  

SVG(可缩放矢量图形)是另一种选择。它是基于矢量的,依赖于文件,而 Canvas 操作像素,使用纯脚本。由于 SVG 集成到 DOM 中,因此 SVG 的渲染速度较慢。因此,对于游戏等动态应用程序来说,它可能不是一个好选择。根据工作类型,需要选择使用哪一个。

与此相关的示例代码如下。

// Define Canvas tag
<canvas id="myCanvas" width="200" height="100"></canvas>

// Access in JavaScript to draw in it at runtime
var canvas = document.getElementById("myCanvas");
var context = canvas.getContext("2d");
context.fillStyle='Red';
context.fillRect(0,0,150,75);
context.beginPath();
context.moveTo(150, 10);
context.arcTo(80, 45, 3, 200, 90);
context.fillStyle = 'Yellow';
context.fill();
context.strokeStyle = 'Purple';
context.stroke();
context.closePath();

离线应用程序

HTML5 离线是另一项主要功能,有助于在没有网络连接的情况下浏览 Web 应用程序。Web 应用程序引用一个清单文件,该文件包含需要缓存的文件、不应缓存的文件以及非缓存文件的回退规则的信息。本地副本会被缓存并保留,没有任何到期规则。如果应用程序在在线时发现清单有任何更改,本地文件将被更新以使其保持最新。在没有网络连接的情况下(DOM 中存在的 `onLine` 标志指示这一点),网页和相关文件将从本地缓存副本提供。

示例缓存清单文件如下:

CACHE MANIFEST
# Cache manifest – version & date
# version 2.1.24
# 02.01.2013


# Cache – resources to cache with html page
CACHE:
Cache-Control: no-cache, private
Styles/Site.css
Images/HTML5.png
Scripts/jquery-1.4.1.min.js 
Scripts/jquery-1.4.1-vsdoc.js 
Scripts/Utilities.js 
Scripts/OfflineApp.js
offline.manifest
# Images/HTML5LogoSmall.png



# Network – files that will not be cached and will always be online
# Following resources require network connection.
# * - it’s a special flag (meaning all resources that are not cached will require a connection.)
NETWORK:
*


# Fallback – display for all uncached pages/resources [precedence below Network]
# FALLBACK:
Images/ShowFullscreen.png
#site/images/ images/offline.jpg
#*.html offline.html

ApplicationCache 有各种状态,根据清单文件指示缓存的状态。

  • 0 UNCACHED:缓存为空。
  • 1 IDLE:缓存是最新版本。
  • 2 CHECKING:正在检查缓存的新更新版本。
  • 3 DOWNLOADING:正在下载新缓存。
  • 4 UPDATEREADY:已准备就绪,可供使用。
  • 5 OBSOLETE:缓存被标记为废弃。

跟踪离线功能(Offline capability)的示例代码如下:

// offline.appcache manifest file reference in the webpage
// <html lang="en" manifest="<%=ResolveClientUrl("offline.appcache")%>">

var cacheStatusMessages = [];
cacheStatusMessages[0] = 'uncached';
cacheStatusMessages[1] = 'idle';
cacheStatusMessages[2] = 'checking';
cacheStatusMessages[3] = 'downloading';
cacheStatusMessages[4] = 'updateready';
cacheStatusMessages[5] = 'obsolete';


var appCache = window.applicationCache;
appCache.addEventListener('cached', logEvent, false);
appCache.addEventListener('checking', logEvent, false);
appCache.addEventListener('noupdate', logEvent, false);

appCache.addEventListener('downloading', logEvent, false);
appCache.addEventListener('progress', logEvent, false);
appCache.addEventListener('updateready', function() {
    window.applicationCache.swapCache();
    console.log('Update was ready. Swap Cache done.');
}, false);

appCache.addEventListener('obsolete', logEvent, false);
appCache.addEventListener('error', logEvent, false);

function logEvent(e) {
    var online, status, type, message;
    online = (navigator.onLine) ? 'yes' : 'no';
    status = cacheStatusMessages[appCache.status];
    type = e.type;
    
    message = 'online: ' + online;
    message += ', event: ' + type;
    message += ', status: ' + status;

    console.log(message);

    if (type == 'obsolete')
        alert('Manifest got deleted. Offline will not work anymore.');
    if (type == 'error')
        alert('Error:' + e.message);
}

设备支持

HTML5 定义了支持多种设备的方式。这是移动页面优化的关键。

  • Viewport Meta Tag - 它通过告诉浏览器内容应如何适应设备屏幕来控制浏览器页面显示。此外,它通知浏览器该网站针对移动设备进行了优化。示例用法:
    <meta name="viewport" content="width=device-width, height=device-height, initial-scale=1.0, user-scalable=yes"/>
  • CSS Media Queries - 这些是基于媒体的样式表,无需更改内容本身即可根据特定范围的输出设备定制内容的呈现。
    • 外部样式表。
      <link rel="stylesheet"  media="handheld, only screen and (max-device-width: 320px)" href="phone.css">
      <link rel="stylesheet"  media="only screen and (min-width: 641px) and (max-width: 800px)" href="ipad.css">
    • 在 `<style>` 元素内(或在 css 文件中)作为 media 规则。
      @media only screen and (max-width: 480px){
         /* rules defined inside here are only applied to browsers that support CSS media queries */ 
         /* and the browser window is 480px or smaller */
      }
    • 在 `<style>` 元素内导入的样式表。
      @import "smallscreen.css" only screen and (max-width: 480px);

关注点

在开发入门套件的过程中,我学到了很多东西。阅读是可以的,但实现是我发现更有趣也更麻烦的地方。一旦了解了某个功能,我建议任何人尝试自己实现它以学到更多。我有一个关于开发过程中发现的所有内容的列表,并计划将它们发布在我的博客上。

参考文献

感谢以下链接中分享的所有知识库文章,它们帮助我学习和开发了这个快速入门应用程序:
http://www.w3.org/[^]
https://w3schools.org.cn/[^]
https://caniuse.cn/[^]
http://html5test.com/[^]
http://www.modernizr.com/[^]
http://html5doctor.com/[^]
http://msdn.microsoft.com/en-us/hh969243.aspx[^]
http://marakana.com/static/bookshelf/html5_tutorial/index.html[^]
http://www.whatwg.org/specs/web-apps/current-work/multipage/offline.html[^]
http://www.javascriptkit.com/dhtmltutors/cssmediaqueries.shtml[^]

历史

版本 1:2013 年 2 月 16 日 初稿!

 

© . All rights reserved.