第 3 章 回應式網頁設計
∗ 網頁設計
▸ 早期
✶ 使用者利用桌上或筆記電腦瀏覽網頁
✶ 網頁寬度設為固定,使所有螢幕呈現相同風格
✶ 寬螢幕左右會有空白邊界
▸ 近期
✶ 使用者利用手機與平板電腦瀏覽網頁
✶ 針對桌上型電腦所設計的網頁在手機上瀏覽會有許多問題
▸ 解決方案:
✶ 利用 HTML5 與 CSS3 做回應式網頁設計 (Responsive web design,亦稱為「響應式網頁設計」)
✶ 網頁依據不同的視域 (Viewport) 尺寸自行調整結構
# 視域:瀏覽器中可以呈現內容的區域,不包括標題、工具列、頁籤等
# 視域尺寸與螢幕尺寸不同
✶ 伺服器後端不需要做任何處理
(1) 回應式網頁設計
∗ 手機瀏覽傳統網頁的問題
▸ 需要放大或縮小頁面才能看清楚相關訊息
▸ 資料延伸太寬,需要左右滑動才能看完
▸ 按鈕或連結太小難以操作,導致常常點錯
▸ 解決方案:相同的網頁內容,但依據視域的特性做頁面結構的調整,例如:
✶ 在小視域中,將較不重要的元素移到主要項目之下
✶ 或許在更糟的狀況下:乾脆將元素隱藏,變成另一個連結
✶ 將導覽按鈕大小調整為適合手指點選的尺寸,而非固定尺寸
✶ 網頁佈局依據可讀性來縮放
✶ 資料延伸的長度彈性調整,不需左右滑動螢幕
∗ 回應式網頁 (Responsive web) 定義
▸ 整合以下三種技術,稱為回應式網頁設計 (Responsive web design)
✶ 彈性網格佈局 (Flexible grid layout)
✶ 彈性影像 (Flexible image)
✶ 媒體及媒體查詢 (Media and media queries)
▸ 其他名稱:fluid design, elastic layout, rubber layout, liquid design, adaptive layout,
cross-device design, and flexible design
(2) 回應式網頁設計範例
▸ 在較寬的視域 (大於 1350px)

▸ 減少視域寬度 (小於 960px):影像格網改變

▸ 繼續減少視域寬度 (小於 720px):頁首的連結變成小圖示,讓使用者在觸控螢幕上方便點選,
且墨綠區域從右方移至上方

▸ 繼續減少視域寬度 (小於 480px):影像格網改變

▸ 繼續減少視域寬度 (小於 320px):影像尺寸改變

(3) 媒體查詢
∗ 媒體查詢 (Media query)
▸ 為 CSS3 的模組之一,用來查詢使用者媒體 (Media) 的特性,以便調整 CSS 的樣式
▸ 媒體特性:視域寬度、螢幕寬高比 (Aspect ratio)、橫向 (Landscape) 或縱向 (Portrait) 等
∗ 範例:依據媒體查詢結果變換背景顏色
bgChange.html
<!doctype html>
<html>
<head>
<title>Browser background changer</title>
<meta charset="utf-8">
<link rel="stylesheet" href="css/bgChange.css">
</head>
<body>
</body>
</html>
bgChange.css
body {
background-color: gray;
}
@media screen and (max-width: 900px) {
body {
background-color: red;
}
}
@media screen and (max-width: 700px) {
body {
background-color: orange;
}
}
@media screen and (max-width: 500px) {
body {
background-color: yellow;
}
}
▸ 變換視域寬度則背景顏色會跟著變化
✶ @media screen:網頁顯示媒介為螢幕 (其他選項:all,
speech, print)
✶ max-width:最大寬度
▸ CSS3 依據該裝置的能力 (Capability) 或特性 (Feature) 來決定其樣式
✶ 如果瀏覽器的特性符合條件,就使用所指定的樣式;否則就不使用
✶ 例如:依據條件載入某個樣式檔
<link rel="stylesheet" media="screen and (orientation: portrait)"
href="portrait-screen.css">
# 上述有兩個問題:(1) 詢問型態:是螢幕嗎?(2) 詢問特性:是縱向嗎?如果都符合,則載入
portrait-screen.css
✶ 可加上邏輯運算 not
<link rel="stylesheet" media="not screen and (orientation: portrait)"
href="portrait-screen.css">
✶ 可串接許多條件判斷
<link rel="stylesheet" media="screen and (orientation: portrait)
and (min-width: 800px)" href="800wide-portrait-screen.css">
∗ 媒體特性
▸ 在建構回應式網頁時,最常查詢的是裝置的視域寬度 (width) 與螢幕寬度
(device-width)
▸ 可供查詢的特性如下:
✶ width:視域寬度
✶ height:視域高度
✶ device-width:裝置的螢幕寬度
✶ device-height:裝置的螢幕高度
✶ orientation:螢幕方向
✶ aspect-ratio:視域寬高比 (例如: aspect-ratio:
16/9 )
✶ device-aspect-ratio:裝置螢幕寬高比
✶ color:表示一個顏色的位元數 (例如: min-color:
16 )
✶ color-index:色彩查詢表 (調色盤) 裡的顏色數量
✶ monochrome:一個像素在單色畫幀緩衝區的位元數
✶ resolution:螢幕或列印的解析度 (例如:min-resolution: 300dpi)
✶ scan:針對電視,漸進式 (Progressive) 或交錯式 (Interlace)
特性,例如:720p HDTV → scan:
progressive ,1080i HD TV → scan:
interlace
✶ grid:裝置屬於網格型 (Grid) 或位元圖型 (Bitmap)
▸ 以上名稱除了 scan 與 grid 外,
都可以在前面加上 min 或 max
來產生範圍值,例如:<link rel="stylesheet" media="screen and
(min-width:200px) and (max-width:360px)" href="phone.css">
(4) 範例:依據視域呈現不同的選單結構
▸ 依視域寬度調整導航按鈕的排列方式:1 列 → 3 列 2 行 →
3 列 2 行與下拉按鈕 → 6 列 1 行與下拉按鈕




∗ 建立 HTML 檔案
nav.html
<!doctype html>
<html>
<head>
<title>回應式導航按鈕範例</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<link rel="stylesheet" href="css/nav.css">
</head>
<body>
<nav>
<a id="pull" href="#"><img src="img/nav-icon.png"></a>
<ul>
<li><a href="#">首頁</a></li>
<li><a href="#">如何做</a></li>
<li><a href="#">圖示</a></li>
<li><a href="#">設計</a></li>
<li><a href="#">Web 2.0</a></li>
<li><a href="#">工具</a></li>
</ul>
</nav>
<h3>Viewport</h3>
<p>width: <span id="width"></span></p>
<p>height: <span id="height"></span></p>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="js/screenSize.js"></script>
<script src="js/nav.js"></script>
</body>
</html>
▸ 在 <head> 元素中加入 meta viewport
標籤,用來在不同的視域中適當縮放頁面
✶ 基本格式: <meta name="viewport" content=...>
✶ 可在 content= 做許多設定,例如專為 320px 視域寬度所設計的頁面:
<meta name="viewport" content="width=320">
✶ 最常用的設定:content="width=device-width, initial-scale=1,
maximum-scale=1"
# 設定頁面寬度等於裝置的寬度:width=device-width
# 確保在開啟頁面時,頁面是以 1:1 的比例呈現,沒有縮放:initial-scale=1
# 防止使用者縮放頁面: maximum-scale=1
▸ <nav> 導航區塊內含 <ul> 與
<li> 按鈕,下拉選單由 <a id="pull"
... > 控制,內含按鈕影像 nav-icon.png 
▸ <h3>Viewport</h3>:顯示視域尺寸
▸ 最後在 </body> 之前加上 jQuery、顯示視域尺寸、及清單滑動之 JavaScript 檔案連結
∗ 建立顯示視域尺寸之程式
▸ 建立 js 子目錄,並在其中建立 screenSize.js
檔案,用來顯示視域寬度:
$(document).ready(function() {
var $window = $(window);
showViewportSize();
$window.resize(function() {
showViewportSize();
});
function showViewportSize() {
$('#width').text($window.width());
$('#height').text($window.height());
}
});

∗ 建立 CSS 樣式
css/nav.css
* {
box-sizing: border-box;
}
body {
margin: 0;
padding: 0;
background-color: lightgray;
}
nav ul {
margin: 0;
padding: 0;
overflow: auto; /* 防止崩潰 */
background-color: #485463;
}
nav li {
list-style: none;
width: 90px;
float: left;
}
▸ 設定 body 與 nav 樣式
✶ box-sizing:所有方框的邊線寬度均為內含
✶ body:清除邊距與填充,背景淺灰色
✶ nav ul:清除邊距與填充,防止崩潰,設定背景顏色
✶ nav li:清除清單樣式,設定寬度,並且向左流動使其左右並排
▸ 結果:

▸ 設定連結樣式
...
nav li {
...
}
nav a {
display: block;
color: white;
font-weight: bold;
text-decoration: none;
padding: 0.8em 0;
width: 100%;
text-align: center;
border-right: 2px solid #576979;
}
nav li:last-child a {
border-right: none;
}
nav a:hover {
background-color: #8c99a4;
}
a#pull {
display: none;
}
✶ nav a:設定區塊顯示、白色、粗體、無裝飾、上下內填充、寬度佔滿、置中對齊、右邊線、縈繞效果
✶ 隱藏 a#pull
▸ 結果:

∗ 利用媒體查詢及 jQuery 設計回應式導航按鈕
▸ 建立視域門檻:6 個導航最小總寬度為 540px (6×90px),故設此為第一門檻,視域小於 540px 時進行變化
...
a#pull {
display: none;
}
@media screen and (max-width: 540px) {
nav li {
width: 50%;
}
nav a {
border-bottom: 1px solid #576979;
border-right: 1px solid #576979;
text-align: left;
text-indent: 30px;
}
}
✶ nav li:設寬度為 50% (每列 2 個導航)
✶ nav a:設右、下邊線,靠左對齊,縮排
▸ 結果:

▸ 第 2 個門檻設為 480px ,導航以按鈕控制
...
@media screen and (max-width: 540px) {
...
}
@media screen and (max-width: 480px) {
nav ul {
display: none;
}
a#pull {
display: block;
background-color: #283744;
}
}
✶ 隱藏 nav ul
✶ 顯示 a#pull (display: block;)
並設定背景顏色
▸ 結果:

▸ 在 nav.js 裡再加上 jQuery 程式
nav.js
$(document).ready(function() {
var $menu = $('nav ul');
$(document).on('click', '#pull', function() {
$menu.slideToggle();
return false;
});
});
▸ 結果:

▸ 第 3 個門檻設為 360px ,導航點選後以單欄排列,亦即寬度 100%
...
@media screen and (max-width: 480px) {
...
}
@media screen and (max-width : 360px) {
nav li {
width: 100%;
}
}
▸ 結果:

(5) 範例:「 得獎者不是 ... 」 網頁
∗ 製作類似下圖的網頁:

∗ 所包含的結構元素:
▸ 外包裝 (Wrapper)
▸ 頁首 (Header)
▸ 上方導航 (Navigation)
▸ 側邊欄 (Sidebar)
▸ 內容 (Content)
▸ 註腳 (Footer)

∗ HTML:
oscar3-1.html
<!doctype html>
<html>
<head>
<title>獎者不是...</title>
<meta charset="utf-8">
<link rel="stylesheet" href="css/normalize.css">
<link rel="stylesheet" href="css/oscar3-1.css">
</head>
<body>
<div id="wrapper">
<header>
<p>得獎者不是...</p>
<nav>
<ul>
<li><a href="#">為什麼?</a></li>
<li><a href="#">關於</a></li>
<li><a href="#">離線</a></li>
<li><a href="#">救贖</a></li>
<li><a href="#">影片</a></li>
<li><a href="#">小考</a></li>
<div style="clear: both;"></div>
</ul>
</nav>
</header>
<div id="container">
<aside>
<p>無名英雄 ...</p>
<p>言過其實的廢話 ...</p>
</aside>
<div id="content">
<p>每年我觀賞奧斯卡金像獎頒獎典禮時,都覺得很生氣 ...</p>
<p>像金剛、紅磨坊、慕尼黑等電影都得獎,但真正的電影英雄卻都沒得獎,一點也沒有好萊塢精神,不是嗎?</p>
<p>在這裡,我們要將事情導正。</p>
<p>這些才應該贏得獎項 <span>»</span></p>
</div> <!-- content -->
</div> <!-- container -->
<footer>
<p>注意,我們的意見是絕對正確的,你們是錯的,即使你們認為自己正確。這是事實,接受吧。</p>
</footer>
</div> <!-- wrapper -->
</body>
</html>
∗ CSS:
oscar3-1.css
* {
box-sizing: border-box;
margin: 0;
}
body {
padding: 0;
}
/* Wrapper */
div#wrapper {
width: 960px;
margin: 0 auto;
background-color: #ddddff;
}
/* Header */
header {
padding: 20px;
background-color: #779307;
}
/* Navigation */
nav {
padding: 20px 0;
}
nav li {
list-style: none;
float: left;
}
nav li a {
display: block;
color: black;
padding: 10px 20px;
text-decoration: none;
}
/* Container */
div#container {
display: grid;
grid-template-columns: 1fr 3fr;
}
/* Aside */
aside {
margin-top: 50px;
padding: 20px;
background-color: #fe9c00;
}
/* Content */
div#content {
margin-top: 50px;
padding: 20px;
background-color: #dedede;
}
/* Footer */
footer {
margin-top: 20px;
padding: 20px;
background-color: #996600;
}
▸ 網頁佈局及導航設定,並上色以資識別
✶ 所有元素:box-sizing: border-box; margin: 0;
✶ body:清除內填充
✶ #wrapper:寬 960px,置中
✶ header:填充
✶ nav:填充,將清單設定為導航連結
✶ #container:設定以 grid 佈局,aside 與 #content 寬度比例為 1:3
✶ aside:上邊距,填充
✶ #content:上邊距,填充
✶ footer:上邊距,填充
▸ 結果:

∗ 影像繪製:影像應該越經濟越好
▸ 頁面上下方的旗幟不應該全部繪製,而應該畫一個基礎單元然後重複背景圖 (如右下圖),
header 及 footer 再加上填充及雙線邊線
▸ 練習:將 oscar3-1.html 及 oscar3-1.css
分別另存新檔為 oscar3-2.html 及
oscar3-2.css,並如下修改:
oscar3-2.html
...
<link rel="stylesheet" href="css/oscar3-2.css">
...
oscar3-2.css
/* Header */
header {
padding: 20px;
padding: 60px 20px 20px 20px;
border-bottom: 4px double #bfbfbf;
background-image: url("../img/buntingFW.png");
background-repeat: repeat-x;
background-color: #779307;
}
/* Footer */
footer {
...
padding: 20px;
padding: 20px 20px 60px 20px;
border-top: 4px double #bfbfbf;
background-image: url("../img/buntingFWinvert.png");
background-repeat: repeat-x;
background-position: 0 bottom;
background-color: #996600;
}
✶ 結果:

∗ 設定各類字型、圖片效果
▸ 練習:將 oscar3-2.html 及 oscar3-2.css
分別另存新檔為oscar3-3.html 及
oscar3-3.css,並如下修改:
oscar3-3.html:
✶ <link rel="stylesheet" href="css/oscar3-3.css">
✶ 設定「得獎者不是...」樣式與連結:
<p id="logo"><a
href="/">得獎者<span>不是...
</span></a></p>
oscar3-3.css:
/* Header */
header {
...
}
/* Logo */
p#logo {
font-size: 3em;
}
p#logo a {
text-decoration: none;
}
p#logo span {
color: lightgray;
}
/* Navigation */
...
▸ 設定左側欄:
✶ 在 HTML 設定 <h1> 標題樣式並加入圖片
<aside>
<h1>無名英雄 ...</h1>
<a href="#"><img class="poster" src="img/midnightRun.jpg" alt="午夜狂奔"></a>
<a href="#"><img class="poster" src="img/wyattEarp.jpg" alt="執法捍將"></a>
<br>
<h1>言過其實的廢話 ...</h1>
<a href="#"><img class="poster" src="img/moulinRouge.jpg" alt="紅磨坊"></a>
<a href="#"><img class="poster" src="img/kingKong.jpg" alt="金剛"></a>
</aside>
# 圖片:
✶ CSS:在 aside 之後加上字體及影像寬度之設定
/* Aside */
aside {
...
}
aside h1 {
font-size: 1.2em;
}
aside img.poster {
width: 90px;
}
/* Content */
...
▸ #content 設定:
✶ 在 HTML 加入影像、文字環繞、設定字型效果
<div id="conten"t>
<img id="oscar" src="img/oscar.png" alt="奧斯卡金人">
<h1>每年<span>我觀賞奧斯卡金像獎頒獎典禮時,
都覺得很生氣 ...</span></h1>
<p>像<b>金剛</b>、
<b>紅磨坊</b>、
<b>慕尼黑</b>等電影都得獎,
但真正的電影英雄卻都沒得獎,一點也沒有好萊塢精神,不是嗎?</p>
<p>在這裡,我們要將事情導正。</p>
<p><a href="#">這些才應該贏得獎項 <span>»</span></a></p>
<div style="clear: both"></div>
</div>
# 奧斯卡金人影像
✶ CSS:設定 h1 字體尺寸及顏色,設定小金人影像之位置及尺寸:
/* Content */
div#content {
...
}
div#content h1 {
font-size: 4em;
}
div#content h1 span {
display: block;
color: #757474;
font-size: 0.6em;
}
div#content img#oscar {
float: left;
margin-top: -50px;
width: 200px;
}
✶ 結果:

▸ 當視域寬度小於 960px 時,右方內容會被截斷
(6) 防止行動瀏覽器自動調整頁面大小
∗ 覆蓋預設之視域設定
▸ iOS 與 Android 瀏覽器均奠基在 Webkit 技術上,這些瀏覽器 (以及 Opera) 允許使用特定的 <meta> 視域元素 (置於 <head> 元素中) 來覆蓋原先的設定,例如:
✶ 可設定固定寬度或者縮放的比例:設定視域寬度為裝置的寬度,而且頁面以 2 倍大小呈現
<meta name="viewport" content="initial-scale=2.0, width=device-width">
✶ 可設定使用者可縮放的程度:設定縮放範圍為 0.5 到 3 倍
<meta name="viewport" content="width=device-width, maximum-scale=3, minimum-scale=0.5">
✶ 可禁止縮放:
<meta name="viewport" content="initial-scale=1.0, user-scalable=no">
✶ 註:亦可將 <meta> 設定以下列格式放在 CSS 檔中,而不要放在 HTML 檔中
@viewport {
width: 320px;
}
∗ 利用媒體查詢,對於小視域改變元素呈現之格式
▸ 練習:將 oscar3-3.html 及 oscar3-3.css
分別另存新檔為 oscar3-4.html 及
oscar3-4.css,並如下修改:
▸ HTML:
✶ 修改 CSS 連結
<link rel="stylesheet" href="css/oscar3-4.css">
✶ 控制頁面之尺寸,使頁面無法縮放,因此可以將頁面設定固定下來:在 HTML 的
<head> 元素中加入以下:
<meta name="viewport" content="initial-scale=1.0,
width=device-width, user-scalable=no">
▸ CSS:刪除各區塊背景顏色
▸ 進行回應式設計
✶ 理念:在小視域的狀況下,將並列的區塊改為直列
✶ CSS 再加上媒體查詢:設定門檻為 800px,設定 #wrapper 寬 800px,
#container 設定以區塊顯示 (不再顯示 grid)
/* Footer */
footer {
...
}
/* Responsive design */
/* At 800px */
@media screen and (max-width: 800px) {
div#wrapper {
width: 800px;
}
div#container {
display: block;
}
}
✶ aside:讓影像改為左右並列
# HTML:改為兩個 h1 區塊,以便在 aside
裡設定文字置中,第一個 h1 加上 id
以便設定右邊距
<aside>
<h1 id="first">
<p>無名英雄 ...</p>
<a href="#"><img class="poster" src="img/midnightRun.jpg" alt="午夜狂奔"></a>
<a href="#"><img class="poster" src="img/wyattEarp.jpg" alt="執法捍將"></a>
</h1>
<h1>
<p>言過其實的廢話</p>
<a href="#"><img class="poster" src="img/moulinRouge.jpg" alt="紅磨坊"></a>
<a href="#"><img class="poster" src="img/kingKong.jpg" alt="金剛"></a>
</h1>
</aside>
# CSS:將 aside h1 設定為
inline-block ,且第一個 h1
設定右邊距,放大海報尺寸
/* Responsive design */
/* At 800px */
@media screen and (max-width: 800px) {
...
div#container {
...
}
aside {
text-align: center;
}
aside h1 {
display: inline-block;
}
aside h1#first {
margin-right: 50px;
}
aside img.poster {
width: 120px;
height: 180px;
}
}
✶ 測試:因小金人影像太大了,擠到 footer 區塊,同時也和文字黏在一起,需要縮小影像並加上邊距
/* Responsive design */
/* At 800px */
@media screen and (max-width: 800px) {
...
aside img.poster {
...
}
img#oscar {
display: block;
height: 350px;
margin: 2em;
}
}
✶ 結果:

∗ 媒體查詢機制解決了所有問題?沒有!
▸ 目前媒體查詢所設定的視域門檻還不夠,低於 800px 視域會有截斷情形,在 800px ~ 960px
之間的視域也會有截斷情形
▸ 問題原因:當視域到達媒體查詢門檻,頁面佈局會變化,但當視域在媒體查詢的門檻之間,
佈局是維持固定的
▸ 解決方案:將固定佈局改為流動佈局