網頁設計

第 4 章    媒體查詢:支援不同的視域

∗ 媒體查詢(Media query)

▸ 為 CSS3 的模組之一,用來查詢使用者媒體 (Media) 的特性,以便調整 CSS 的樣式

▸ 媒體特性:視域寬度、螢幕寬高比 (Aspect ratio)、橫向或縱向 (Landscape or portrait)

(1) 媒體查詢語法

∗ 範例:依據媒體查詢結果變換背景顏色

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

▸ 以上 CSS3 在使用現代的瀏覽器下,變換頁面尺寸背景顏色會跟著變化

@media screen:以螢幕來顯示網頁,選項有 all, screen, 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">
✶ 例如:依據條件設定 h1 元素為綠色
@media screen and (max-device-width: 400px) {
  h1 {
    color: green;
  }
}

∗ 可供查詢的媒體特性

▸ 在建構回應式網頁時,最常查詢的是視域寬度 (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)

▸ 以上名稱除了 scangrid 外,都可以在前面加上 minmax 來產生範圍值,例如:<link rel="stylesheet" media="screen and (min-width:200px) and (max- width:360px)"href="phone.css">

(2) 範例 1:依據視域呈現不同的選單結構

∗ 設計頁面的 6 個導航 (Navigation) 的依視域寬度有不同的排列方式

(資料來源:Thoriq Firdaus, "How to Create a Responsive Navigation")

▸ 視域由寬到窄設定清單選項的排列格式:1 列 → 3 列 2 行 → 3 列 2 行下拉式清單→ 6 列 1 行下拉式清單

1Column
3Column
3ColumnMenu
6Column

▸ 下拉式清單由連結控制:<a id="pull" href="#">清單</a>

∗ 建立 nav.html 檔案:

▸ 在 <body> 之下建立 <nav> 區塊標籤,內含 <ul>, <li>, 及 <a> 之導航連結按鈕

▸ 在 <nav> 之後加上清單標籤 <a> 作為下拉式選單按鈕,並設其 idpull

▸ 在 </body> 之前加上連結 screenSize.js 及之 JavaScript 檔案,以顯示視域尺寸及滑動清單

<!doctype html>
<html>
<head>
<title>回應式導航範例</title>
<meta charset="utf-8">
<link rel="stylesheet" href="css/nav.css">
</head>
<body>
<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="#">Web 2.0</a></li>
    <li><a href="#">工具</a></li>
  </ul>
  <a id="pull" href="#">清單</a>
  <div style="clear: both"></div>
</nav>
<br><br>

<h3>
  Viewport width: <span id="width"></span>
</h3>

<h3>
  Viewport height: <span id="height"></span>
</h3>

<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>
js/screenSize.js
$(document).ready(function() {

  // ScreenSize
  var $window = $(window);
  showViewportSize();
  $window.resize(function() {
    showViewportSize();
  });

  function showViewportSize() {
    $('#width').text($window.width());
    $('#height').text($window.height());
  }

});
註: $(document).ready(... 目的在於確認文件均就緒才執行 jQuery 程式
screenSize

∗ 在 <head> 元素中加入 meta viewport 標籤,用來在不同的視域中適當縮放頁面

<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">

▸ 基本格式: <meta name="viewport" content=...>

▸ 可在 content= 做許多設定,例如專為 320px 視域寬度所設計的頁面: <metaname="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.html<head> 元素中加入連結 <link rel="stylesheet" href="nav.css">

▸ 建立新檔 css/nav.css

▸ 設定 bodynav

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  padding: 0;
  background-color: lightgray;
}

nav {
  background-color: #455868;
}

nav ul {
  margin: 0;
  padding: 0;
}

nav li {
  list-style: none;
  width: 10%;
  min-width: 90px;
  float: left;
  /* border: 1px solid red; */
}
box-sizing:所有方框的邊線寬度均為內含
body:清除邊距與填充,寬度 100%,背景淺灰色
nav:背景顏色 #455868
nav ul:清除邊距與填充
nav li:清除清單樣式,設定寬度及最小寬度,向左流動
✶ 結果:
screenSize2

▸ 設定導航按鈕

nav a:文字白色、粗體、無底線、置中,以區塊方式呈現,高寬度都是 100%,向左流動,設定適當填充,設定右邊線、滑鼠縈繞效果
✶ 隱藏 nav a#pull
...

nav li {
  ...
}

nav a {
  color: white;
  font-weight: bold;
  text-decoration: none;
  text-align: center;
  display: block;
  width: 100%;
  height: 100%;
  float: left;
  padding: 10px 0;
  border-right: 2px solid #576979;
}

nav li:last-child a {
  border-right: none;
}

nav a:hover {
  background-color: #8c99a4;
}

nav a#pull {
  display: none;
}
# 設定 100% 寬度高度:佔滿 <li> 區域
# 向左流動:當 <li> 出現時, <a> 可以排在最後
✶ 結果:
screenSize3

∗ 利用媒體查詢及 jQuery 設計回應式導航:建立視域門檻

▸ 6 個導航最小總寬度為 540px (6×90px),故設此為第一門檻:視域小於 540px 時進行變化:

nav li:設寬度為 50% 當 (每列 2 個導航)
nav a:設右、下邊線,設定適當文字位置
...

nav 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;
  }
}
✶ 結果:
3Column

▸ 第 2 個門檻設為 480px ,導航以清單按鈕控制

nav ul:隱藏
nav a#pull:出現( display: block; ),設背景顏色,向左流動,位置相對
✶ 利用 :after 設定清單按鈕後方的屬性
# 沒有文字內容 (content: "";),背景採用影像 nav-icon.png nav-icon.png,不重複
# 高寬各為 30px,右方距離 15px ,上方距離 8px
...

@media screen and (max-width: 540px) {
  ...
}

@media screen and (max-width: 480px) {
  nav ul {
    display: none;
  }
  nav a#pull {
    display: block;
    background-color: #283744;
    position: relative;
  }
  nav a#pull:after {
    content:"";
    background: url("../img/nav-icon.png") no-repeat;
    width: 30px;
    height: 30px;
    position: absolute;
    right: 15px;
    top: 8px;
  }
}
✶ 結果:
menu
✶ 在 nav.js 裡再加上 jQuery 程式
nav.js
$(document).ready(function() {

  var $menu = $('nav ul');
  
  $(document).on('click', '#pull', function() {
    $menu.slideToggle();
    return false;
  });

});
✶ 結果:
3ColumnmMenu

▸ 第 3 個門檻設為 360px ,導航點選後以單欄排列

✶ 選項設為單欄,亦即寬度 100%
...

@media screen and (max-width: 480px) {
  ...
}

@media screen and (max-width : 360px) {
  nav li {
    width: 100%;
  }
}
✶ 結果:
6Column

(3) 範例 2:「 得獎者不是 ... 」 網頁

∗ 製作類似下圖的網頁:

theWinnerIsNot

∗所包含的結構元素:

▸ 頁首 (Header)

▸ 上方導航 (Navigation)

▸ 側邊欄 (Sidebar)

▸ 內容 (Content)

▸ 註腳 (Footer)

structure

∗ 練習:

▸ 建立新檔 oscar4-1.html,輸入以下內容:

<!doctype html>
<html>
<head>
<title>獎者不是...</title>
<meta charset="utf-8">
<link rel="stylesheet" href="css/normalize.css">
<link rel="stylesheet" href="css/oscar4-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>

<aside>
  <p>無名英雄 ...</p>
  <p>言過其實的廢話</p>
</aside>

<div id="content">
  <p>每年我觀賞奧斯卡金像獎頒獎典禮時,都覺得很生氣 ...</p>
  <p>像金剛、紅磨坊、慕尼黑等電影都得獎,但真正的電影英雄卻都沒得獎,一點也沒有好萊塢精神,不是嗎?</p>
  <p>在這裡,我們要將事情導正。</p>
  <p>這些才應該贏得獎項 <span>&raquo;</span></p>
</div>  <!-- content -->

<footer>
  <p>注意,我們的意見是絕對正確的,你們是錯的,即使你們認為自己正確。這是事實,接受吧。</p>
</footer>

</div>  <!-- wrapper -->

</body>
</html>

∗ 練習:

▸ 建立新檔 oscar4-1.css ,輸入以下設定,並上色以資識別:

✶ 網頁佈局及導航設定
# 所有元素:box-sizing: border-box; margin: 0;
# body:清除內填充
# #wrapper:寬 960px,置中
# header:填充
# nav:填充,將清單設定為導航連結
# aside:向左流動,寬 240px,上邊距,填充
# #content:向左流動,寬 720px,上邊距,填充
# footer:兩邊淨空,上邊距,填充
* {
  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 {
  color: black;
  padding: 10px 20px;
  text-decoration: none;
}

/* Aside */
aside {
  float: left;
  width: 240px;
  margin-top: 50px;
  padding: 20px;
  background-color: #fe9c00;
}

/* Content */
div#content {
  float: left;
  width: 720px;
  margin-top: 50px;
  padding: 20px;  
  background-color: #dedede;
}

/* Footer */
footer {
  clear: both;
  margin-top: 20px;
  padding: 20px;
  background-color: #996600;
}

▸ 結果

theWinnerIsNotStructure

∗ 影像繪製:影像應該越經濟越好

▸ 頁面上下方的旗幟不應該全部繪製,而應該畫一個基礎單元然後重複背景圖 (如右下圖), headerfooter 再加上填充及雙線邊線

buntingFW buntingFWinvert

▸ 練習:將 oscar4-1.htmloscar4-1.css 分別另存新檔為 oscar4-2.htmloscar4-2.css,並如下修改:

oscar4-2.html
...
<link rel="stylesheet" href="css/oscar4-2.css">
...
oscar4-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;
}
✶ 結果
theWinnerIsNotStructure2

∗ 設定各類字型、圖片效果

▸ 練習:將 oscar4-2.htmloscar4-2.css 分別另存新檔為 oscar4-3.htmloscar4-3.css,並如下修改:

oscar4-3.html
<link rel="stylesheet" href="css/oscar4-3.css">
✶ 設定「得獎者不是...」樣式與連結:
<p id="logo"><a href="/">得獎者<span>不是...</span></a></p>
oscar4-3.css
/* Header */
header {
  ...
}

/* Logo */
p#logo {
  font-size: 3em;
}

p#logo a {
  text-decoration: none;
}

p#logo span {
  color: lightgray;
}

▸ 設定左側欄:

✶ 在 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>
# 圖片:
midnightRun.jpg   wyattEarp.jpg   moulinRouge.jpg   kingKong.jpg
✶ CSS:在 aside 之後加上字體及影像尺寸之設定
/* Aside */
aside {
  ...
}

aside h1 {
  font-size: 1.2em;
}

aside img.poster {
  width: 90px;
  height: 120px;
}

#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>
# 奧斯卡金人影像
oscar
✶ 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;
  height: 500px;
}
✶ 結果:
isnot

▸ 當視域寬度小於 960px 時,右方內容會被截斷

(4) 防止行動瀏覽器自動調整頁面大小

∗ 覆蓋預設之視域設定

▸ 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;
}

∗ 利用媒體查詢,對於小視域改變元素呈現之先後順序

▸ 練習:將 oscar4-3.htmloscar4-3.css 分別另存新檔為 oscar4-4.htmloscar4-4.css,並如下修改:

▸ HTML:

✶ 修改 CSS 連結
<link rel="stylesheet" href="css/oscar4-4.css">
✶ 控制頁面之尺寸,使頁面無法縮放,因此可以將頁面設定固定下來:在 HTML的 <head> 元素中加入以下:
<meta name="viewport" content="initial-scale=1.0, width=device-width, user-scalable=no">

▸ CSS:刪除各區塊背景顏色

▸ 進行回應式設計

✶ 理念:在小視域的狀況下,應該讓重要的資訊先出現,因此,在 HTML 中 #content 應放在 aside 之前
# HTML:調換 #contentaside 的順序,但桌上型頁面就會變成 aside 在右邊、 #content 在左邊
解決方案:將 #content 往右流動
/* Content */
div#content {
  float: right;
  ...
}
✶ CSS 加上媒體查詢:設定門檻為 800px,設定 #wrapper 寬 800px, #content 設定無流動,寬 100%
/* Footer */
footer {
  ...
}

/* Responsive design */
/* At 800px */
@media screen and (max-width: 800px) {
  div#wrapper {
    width: 800px;
  }
  
  div#content {
    float: none;
    width: 100%;
  }
}
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 寬度設為 100% 並停止流動,h1 設定為 inline-block , 且第一個 h1 設定右邊距,放大海報尺寸
/* Responsive design */
/* At 800px */
@media screen and (max-width: 800px) {
  ...
  div#content {
    ...
  }
  
  aside {
    float: none;
    width: 100%;
    text-align: center;
  }
  
  aside h1 {
    display: inline-block;
  }
  
  aside h1#first {
    margin-right: 50px;
  }
  
  aside img.poster {
    width: 120px;
    height: 180px;
  }
}
✶ 測試:因小金人影像過高,超出 content 區塊範圍, 海報影像被向右擠壓,解決方案:加上淨空區塊,讓 content 區塊包含小金人影像
<div id=content>
  ...
  <p>這些才應該贏得獎項 <span>»</span></p>
  <div style="clear:both"></div>
</div> <!--content -->
✶ 結果:
isnot2

∗ 媒體查詢機制解決了所有問題?沒有!

▸ 目前媒體查詢所設定的視域門檻還不夠,低於 800px 視域會有截斷情形,在 800px ~ 960px 之間的視域也會有截斷情形

▸ 問題原因:當視域到達媒體查詢門檻,頁面佈局會變化,但當視域在媒體查詢的門檻之間,佈局是維持固定的

▸ 解決方案:將固定佈局改為流動佈局

上一章       下一章