:::
顯示具有 Programming/CSS 標籤的文章。 顯示所有文章

為什麼列印網頁時頁首與頁尾消失了? /  Page Margin Setting causes Headers and Footers to Disappear when Printing

布丁布丁吃布丁

為什麼列印網頁時頁首與頁尾消失了? /  Page Margin Setting causes Headers and Footers to Disappear when Printing

27-Page_Margin_Setting_causes_Headers.jpg

嗨,大家最近在吃什麼呢?這裡是為臺灣祈福的布丁,最近我苦於處理Blog網頁列印版本的各種問題。其中一個令我困擾已久的難題,就是列印版的頁首和頁尾消失了。研究後才發現,這是因為我在CSS語法中設定了頁面邊界「@page {margin: 0.5cm}」,導致Google Chrome瀏覽器在列印版本中隱藏了頁首與頁尾。接下來我們就一起來看看網頁列印版本的頁首和頁尾,以及造成它們消失的CSS設定細節吧。

(more...)

2020年臺灣工程師喜愛的程式語言與資料庫竟然跟全球流行不一樣 / The Popular Script Language and Database Technology in Taiwan in 2020

2020年臺灣工程師喜愛的程式語言與資料庫竟然跟全球流行不一樣 / The Popular Script Language and Database Technology in Taiwan in 2020

23-2020_ENG.png

Pui Pui大家午安啊,我是在寒冷又溼漉漉的地方裡,期望下雨降在集水區的布丁。在這個寒冷的天氣裡,今天就跟大家聊聊臺灣工程師最受歡迎的程式語言跟資料庫技術吧!

全世界工程師最後的救星Stack Overflow在2020年二月發佈了2020年開發者調查結果。統計5萬多位工程師最喜歡的程式語言中,JavaScript以67.7%名列第一。年初時Easy Website的站長網站哥以這份開發者調查資料為基礎,將臺灣開發者所流行的程式語言與資料庫繪製成統計圖表,結果竟然跟世界潮流不太一樣。至於差異在哪裡,就讓我們看下去吧。

(more...)

解決動態載入CSS失敗的問題:不要修改<link>標籤的屬性 / Why Loading CSS Dynamically Fail: Don't Modify <link> While Loading

解決動態載入CSS失敗的問題:不要修改<link>標籤的屬性 / Why Loading CSS Dynamically Fail: Don't Modify <link> While Loading

image

我在KALS專案中有用到以jQuery動態載入CSS的技巧,但是最近在Chrome上卻意外不能執行。研究之後才發現這是因為我在載入CSS途中去修改CSS載入標籤的title屬性,造成CSS載入失敗。解決方法是應該在建立載入CSS的標籤時就加入title屬性,而不要事後修改。

(more...)

如何閱讀JavaScript/CSS壓縮程式碼?快使用程式碼格式化工具 / How to Maxify/Parsing/Format Minified Code

如何閱讀JavaScript/CSS壓縮程式碼?快使用程式碼格式化工具 / How to Maxify/Parsing/Format Minified Code

image

為了閱讀被壓縮(minify)的JavaScript與CSS程式碼,我們可以使用線上工具Code Formatter或是NetBeans的format指令來為程式碼進行排版。

You can use some tools to maxify/parse/format the minified JavaScript or CSS codes, for example, Online Code Formatter or NetBeans.


關於壓縮程式碼 / Minify Code

JavaScript與CSS程式碼的特性在於必須下載到使用者客戶端才能執行,因此降低程式碼的大小是很常見的方式。JavaScript跟CSS通常是以壓縮(minify)的形態發佈給大家使用。被壓縮的程式碼檔案名稱通常會加上「.min」,而原始碼通常會加上「.src」,舉例來說:

image

這是jQuery 1.11.1原始碼的樣子。檔案大小是276KB。

image

這是jQuery 1.11.1壓縮程式碼的樣子。檔案大小是93.5KB,幾乎是原始碼的1/3大小。

壓縮程式碼中會移除註解、刪除空白與換行、縮短變數名稱(例如變數用了var book,會縮短成var a)。我之前也介紹過使用YUI CompressorMinify壓縮程式碼的方法。而現在壓縮JavaScript跟CSS已經是主流用法,像是Fat-Free Framework中也直接內建了壓縮工具,非常方便。

為什麼要閱讀被壓縮的程式碼? / To Read Minified Code

雖然像是jQuery這種知名的函式庫會提供非壓縮的原始碼供大家閱讀研究,但是還是很多專案並沒有提供解壓縮的原始碼,而只有被壓縮的程式碼可以看。

我最近在研究如何撰寫CKeditor的plugin,可惜他的教學跟文件寫的並不是很好。後來我覺得比起研究文件,不如直接修改程式碼,說不定還比較快。

2014-09-05_164627

可惜的是,大部分CKeditor中plugin的程式碼都是壓縮後的結果,閱讀起來不太容易,我們需要一些解壓縮(maxify)工具來輔助我們閱讀這些程式碼。

在此介紹兩種格式化被壓縮程式碼的方便工具:NetBeans的format指令跟線上工具


NetBeans的格式化指令 / “Format” in NetBeans

我使用的是NetBeansIDE 8.0。要格式化被壓縮的JavaScript程式碼的做法如下:

  1. 開啟JavaScript檔案
    2014-09-05_170730
  2. 開啟Source > Format 
    2014-09-05_170801
  3. 變成了漂亮的排版
    2014-09-05_170855

不過你可以注意到下面還是有一團程式碼沒有解壓縮到,這似乎是NetBeans的限制。此外,NetBeans也不能解壓縮CSS程式碼,使用範圍有限。

使用Code Formatter解壓縮 / Maxify Code on Online Code Formatter

這個工具名稱叫做「Format, Beautify, Maxify, Unpack or Deobfuscate JavaScript/jQuery/HTML/JSON/CSS Codes」,有點長,還是用網址上的Code Formatter來稱呼好了。

image

這工具用法很簡單。首先先把壓縮的程式碼貼在框框中,然後按下下面的按鈕「Click here to Format/Beautify/Maxify Your JavaScript/jQuery/JSON//HTML Codes」。

image

程式碼就以漂亮的版面排版好了,而且NetBeans沒解開的後面部分也排版的漂漂亮亮,這樣就能夠更輕易地閱讀程式碼了,真是令人開心呢。

希望這些工具能夠幫助程式開發者更容易閱讀程式碼。學程式的第一步就是模仿別人怎麼寫,加油!

(more...)

使用Minify壓縮JavaScript跟CSS範例

使用Minify壓縮JavaScript跟CSS範例

image

Minify是用來壓縮JavaScritp跟CSS的PHP 5應用程式。跟我之前介紹的YUI Compressor壓縮器功能類似,但是YUI Compressor是Java程式,沒有Minify原生整合PHP來得方便。Minify目前最新版本為2.1.5,你可以在GitHub找到它全部的原始碼。

Minify功能很多很複雜,但是它自己的使用手冊中卻沒有提到怎樣簡單地在PHP使用。這篇就是簡單地寫個範例,讓大家知道要怎麼用Minify來壓縮JavaScript跟CSS。


下載檔案

image

你可以在GitHub找到Minify的所有原始碼,請按下「ZIP」下載檔案。

壓縮範例

我這篇的用法中,JavaScript跟CSS都是以PHP變數的形式儲存再來進行壓縮。Minify主要的用法是壓縮既有的JavaScript與CSS檔案,但是我比較常將各個檔案整理、組合、排列,然後再壓縮,這也是這兩個範例的主要作法。

壓縮JavaScript
<?php

/**
* --------
* JavaScript Compressor
* --------
*/

$js = "
/**
* This is a demo script
*/
function test() {
var _test_ms = '1';
alert(_i);
}
"
;

require_once './min/lib/JSMinPlus.php';

$packed_js = JSMinPlus::minify($js);

echo $packed_js; //function test(){var _test_ms='1';alert(_i)}

?>

壓縮CSS
<?php

/**
* --------
* CSS Compressor
* --------
*/

$css = "
/**
* This is a demo css
*/
body hi {
font-size: 3em;
/* font-weight: bold; */
color: #666666;
}
"
;

require_once './min/lib/CSSmin.php';

$cssmin = new CSSmin();
$packed_css = $cssmin->run($css);

echo $packed_css; //body hi{font-size:3em;color:#666}

?>

注意:不是任何JavaScript都能壓縮

然而,Minify壓縮法跟YUI Compressor還是不一樣。如果JavaScript已經是壓縮好的程式,那麼Minify再壓縮之後通常就會發生錯誤。

另外,有些程式碼是專門寫好給YUI Compressor壓縮的,所以用Minify壓縮時就會發生錯誤。這時候要嘛就是找線上的YUI壓縮服務,要嘛就是自己裝個YUI Compressor來壓縮。


結語:原生的Minify比Java的YUI Compressor好用

我之前改用YUI Compressor壓縮的時候常常會發生錯誤,這是由於PHP並非多線程設計,一旦網站大量對PHP請求的時候,PHP就會傻傻地去呼叫YUI Compressor的Java程式,然後Java就會快速地吃光伺服器的CPU跟記憶體,最後伺服器當機。即使我之前用PHPLock跟設計快取機制,這個問題還是很難根治。

然而跟使用Java的YUI Compressor相比,Minify原生於PHP的運作效率並不差,甚至可以說更好。到目前為止運作都蠻順暢的。而大量請求網頁的時候仍會出錯,那也是PHP自身設定就能夠搞定,不用像是使用YUI Compressor的時候還要去研究Java的環境設定。

最後發個牢騷,我還是覺得Java太容易當機了,不論是這邊用Java跑YUI Compressor,還是Tomcat上運作DSpace,Java太容易吃光記憶體跟佔用大量CPU,這讓我感到非常困擾。(即使設定了Xms跟Xmx還是很容易當機!)即使如此,用Java開發Servlet網站服務的專案還是很多,大家到底是怎麼面對這種問題的啊?我很好奇!

(more...)

在 PHP 使用 YUI Compressor 壓縮 JavaScript 跟 CSS

在 PHP 使用 YUI Compressor 壓縮 JavaScript 跟 CSS

2011-11-14_232832 yui logo

我在寫程式的時候,習慣搭配程式註解一併撰寫。寫JavaScript就搭配JSDoc,寫CSS就搭配CSSDoc,寫著寫著程式碼就會非常非常地龐大,造成使用者每次都要下載過大的檔案內容而效率緩慢。

YUI是一套開放原始碼的JavaScript與CSS框架,以BSD授權條款開放使用。它其中一個相當知名的功能是JavaScript與CSS的壓縮器(compressor)。這篇是要教你如何在PHP的環境中使用YUI Compressor來壓縮JavaScript與CSS。


安裝配置

由於這篇的目的是使用PHP作為開發環境,相信你已經有了一個運作PHP的環境。我自己的測試環境是以XAMPP 1.7.0架設,使用PHP 5.2.8。

首先,你要準備一個資料夾,用來擺放YUI Compressor與相關檔案。

再來將以下檔案下載到該資料夾中:

安裝Java

由於YUI Compressor使用Java,你必須確保你的環境中安裝了JDK才行。請到Java網站下載Java Platform (JDK),他有提供Windows與Linux各種版本。

然而我在CentOS作業系統中安裝JDK 7u1時不知為何地失敗了,所以我又回頭去裝了JDK 6u12。

下載之後,請調整該檔案的權限為755,然後執行該檔案。舉例來說指令如下:

[root@dspace ~]# chmod 755 jdk-6u12-linux-i586-rpm.bin
[root@dspace ~]# ./jdk-6u12-linux-i586-rpm.bin

如要確認Java能夠使用,請輸入以下指令並可能會看到如下訊息:(可能是因為我先裝7u1失敗的關係,以下版本顯示的是7u1而不是6u12)

[root@dspace ~]# java -version
java version "1.7.0_01"
Java(TM) SE Runtime Environment (build 1.7.0_01-b08)
Java HotSpot(TM) Client VM (build 21.1-b02, mixed mode)

這樣就完成配置囉。


壓縮使用

使用YUI Compressor時,要先引用Minify_YUICompressor.php,然後透過它的靜態方法minifyJs()跟minifyCss()來輸入要壓縮的程式碼,並取得壓縮過後的程式碼。

以下是minifyJs跟minifyCss的用法:

/**
* Minify a Javascript string
*
* @param string $js
*
* @param array $options (verbose is ignored)
*
* @see http://www.julienlecomte.net/yuicompressor/README
*
* @return string
*/
public static function minifyJs($js, $options = array())

/**
* Minify a CSS string
*
* @param string $css
*
* @param array $options (verbose is ignored)
*
* @see http://www.julienlecomte.net/yuicompressor/README
*
* @return string
*/
public static function minifyCss($css, $options = array())

使用範例

為了讓大家更容易理解如何使用,請下載以下兩個範例檔案到上述Minify_YUICompressor.php跟yuicompressor-2.4.6.jar的同一個資料夾中。

我們先看一下blogger-template.css的原始碼,這是一個沒有經過壓縮的CSS程式,大概長得像下面這樣子,程式碼大小為19.9KB:

2011-11-14_232059 blogger-template.css

然後我們再透過minifyCss.php看一下壓縮過的樣子,壓縮過後程式碼大小為13.4KB,足足有67.3%的壓縮率:

2011-11-14_232133 minifyCss.php

以下是minifyCss.php的程式碼內容。這裡面包括了在HTTP標頭輸出為CSS格式的指令,以協助瀏覽器正確判斷資料內容。

<?php
header('Content-type: text/css', TRUE);

include("Minify_YUICompressor.php");
$script = file_get_contents("blogger-template.css");
$packed = Minify_YUICompressor::minifyCss($script);
echo $packed;

而JavaScript也是以此類推,以下是minifyJs.php壓縮JavaScript的標頭輸出與壓縮寫法:

<?php
header('Content-type: text/javascript', TRUE);

include("Minify_YUICompressor.php");
$script = file_get_contents("puliBloggerDigest.js");
$packed = Minify_YUICompressor::minifyJs($script);
echo $packed;


致謝與修改過程

YUI Compressor雖然一般是要透過Java以指令列的方式運作,不過多虧了minify專案開發了Minify_YUICompressor.php,讓我們得以輕易地以PHP使用YUI Compressor。

然而由於原本的Minify_YUICompressor.php需要將YUI Compressor跟暫存目錄指定為絕對路徑,我覺得不是很好用,所以將暫存目錄改為系統自訂的暫存目錄、YUI Compreesor改為跟Minify_YUICompressor.php相同的目錄,讓我們只要把兩個檔案擺在一起就能夠使用。這樣在程式碼轉移的時候特別方便,例如將PHP系統從Windows轉移到Linux的時候。

結語:最好搭配快取機制一起使用

YUI Compressor的壓縮功能雖然挺好用的,搭配上Minify程式之後就更容易讓PHP取用,但是運作效率卻有待堪慮。我在Windows環境中請YUI Compressor壓縮1MB左右的JavaScript的時候,通常會延遲個3秒鐘左右才完成。儘管這是因為我檔案太大的關係,但是如果太過頻繁的壓縮,恐怕仍會帶來效率上的問題。

我建議在使用YUI Compressor的時候,同時搭配一些快取機制(cache),將YUI Compressor的壓縮結果另存為一個靜態的JavaScript或CSS程式碼。下次就能讓系統直接讀取壓縮結果,而不需要經過YUI Compressor的壓縮。

(more...)

Blogger範本應用CSS Sprites技術記事

Blogger範本應用CSS Sprites技術記事

padding_header_footer

blogger-icon-set subscribe-icon-set

最近我在學習CSS Sprites技巧,並嘗試將此技巧應用於「布丁布丁吃什麼?」blog中。以下記錄開始修改的機緣、簡單介紹CSS Scrites的原理,然後一一記錄我如何將CSS Scrites應用於Blogger的範本中的過程。


機緣

最近在修改Blogger範本之後,就想說應該拿個什麼測速工具之類的檢測一下Blog有什麼問題。赫然想起之前電腦玩物介紹了Google Page Speed Online,他可以提供網站速度評測指標的分析與指導,似乎頗值得拿來參考。

image

分析之後,Page Speed Online指出「布丁布丁吃什麼?」最優先需要修改的建議是「將圖片合併到CSS合併圖片」,也就是它建議我應用CSS sprites技巧來改善網頁的讀取速度。

image

我用Firebug檢查了一下圖片的請求狀況,發現光是布丁的自我簡介(2011年版)就有56個請求(如上圖)。趁著改良Blog的機會,我也想來練習做做看CSS Sprites,提昇自己的程設能力。


CSS Scrites原理

CSS Scrites是一種提高網頁讀取速度的技巧。其原理是降低圖片請求(request)數量,以節省請求時額外消耗的速度。

概要作法是將網頁中多張圖片結合起來,再透過CSS語法調整,讓每個位置都只顯示該部分的圖片。應用CSS Scrites之後,原本網頁需要讀取多張圖片時需要跟伺服器請求的數量,會因為合併成一張圖片,而大幅降低了請求數量,因此也節省了多次請求而消耗的速度。

其原理很容易懂,但是實作的時候卻不容易。這需要熟悉HTML跟CSS語法才能進行,而且也需要分辨哪些圖片可以應用CSS Scrites,或是哪些不行。

Page Speed Online有給我們一些建議,我嘗試翻譯如下:

  • 合併會一起讀取的圖片:建議合併時常在同一頁面中同時讀取的圖片。例如,每一頁都會用到的同一組圖示,就適合進行合併。相反的,每一次讀取都會改變的動態圖片,例如大頭貼照片、或是在頁面中會時常變更的圖片,就不建議進行合併。
  • 優先合併GIF跟PNG圖片:GIF跟PNG圖片使用無損壓縮法,因此合併時並不會因此降低合併圖片的品質。
  • 優先合併小型圖片:每一個圖片請求都會需要固定的額外請求時間(request overhead),即使是下載小型圖片,瀏覽器也會需要為此耗費額外的請求時間。藉由合併小型圖片,將可以從每一次請求一張圖片到一次請求就讀取整張合併的圖片,因此降低了額外請求的時間。
  • 合併可以快取的圖片:建議合併快取時間(caching lifetime)較長的圖片。如果圖片已經被瀏覽器快取,那瀏覽器就不需要再次下載該張圖片,以提高讀取的效率。
  • 使用CSS Sprite服務:合併圖片時,可以使用SpriteMe之類的服務,讓你輕易應用CSS sprites。
  • 最小化合併圖片中的空白處:為了顯示圖片,瀏覽器必須解壓縮並解碼該圖片。圖片的尺寸通常是跟圖片的解析度成正比。因此,當合併圖片中的空白處過多的時候,即使沒有明顯改變圖片的檔案大小,但是沒有顯示的像素依然會佔據記憶體的用量,造成瀏覽器回應速度變慢。
  • 合併使用同樣色彩的圖片:合併圖片如果超過256色,將會讓PNG從palette type改成使用truecolor type,並造成合併圖片檔案變大。為了產生最佳化的合併圖片,要合併的圖片最好都使用相同的256色。如果你的圖片還有調整的空間,建議考慮想辦法讓你的合併圖片色彩數量降低到256色。

SpriteMe的安裝與分析建議

image

既然Page Speed Online都建議我先從SpriteMe開始了,那就先用SpriteMe看看有什麼好的建議吧。

安裝SpriteMe書籤

SpriteMe是一個書籤小工具(Bookmarklet)請把下面的連結拖曳到書籤列上吧。

SpriteMe

使用SpriteMe

image

打開你要分析的網頁,這邊我一樣以布丁的自我簡介(2011年版)來做做看。SpriteMe將網頁中的圖片分成「Suggested Sprites」(建議合併)與「Non-Sprited Images」(不合併圖片)這兩種。以下是詳細的列表:

image

接著讓我們來看看SpriteMe為什麼建議合併與不建議合併的理由。

合併建議1:合併不重複的圖片

第一項是「vertical, varied width」(垂直的,多變的寬度),直接翻譯還真是看不懂是什麼意思,但仔細一看他列出的圖片,大多都是寬度、高度不等,而且在CSS中都是不重複(no-repeat)的圖片,簡單來說就是建議合併的大雜匯啦。

以下舉幾張例子:

  • image
    頁首標題前的箭頭
  • image
    擺在頁首的底部圖片
  • image 
    日期前的箭頭
合併建議2:X軸重複、寬度相等

這一個建議很特別,他分析出兩張寬度相等(760px)的背景圖,而且他們也都是設定為X軸重複(repeat-x),因此也適合合併成一張圖。

這兩張圖各別是:

  • image
    頁首的背景
  • image
    頁尾的背景

他們都Y軸的漸層效果。仔細一看,似乎這背景圖也不需要這麼寬,就能用X軸重複達到填滿的效果了。

不合併的建議

除了合併的建議之外,SpriteMe也給了不合併的建議。我把圖片的長寬尺寸與理由列舉如下:

  • bg_body.gif 5x600:因為X軸重複,而且太短
  • bg_sidebar_arrow.gif 80x98:Y軸重複而且太窄
  • icon_list_item_left.gif 9x7:因為包含他的區塊(block)比他高且比他寬。(意思是這種情況還是用背景圖來顯示比較好)
  • bg_sidebar.gif 5x464:因為看不到。(我動態地把導覽列隱藏起來的關係)

SpriteMe的使用過程

SpriteMe不僅僅是分析建議很詳細,就連使用起來也很容易。

建立合併

image

上面提到SpriteMe建議我合併X軸重複、寬度相等的圖片,而該區右上角有個「make sprite」按鈕,就能夠自動產生CSS Sprite的效果。

image

按下去之後稍待一會,圖片就合併成一張了。打開項目的詳細事項,裡面記載著SpriteMe調整過的元素內容。點選該元素,他會在元素外圍描繪上紅色的框線。

image

他同時也提供了一張合併後的圖片,如上圖。儘管我很好奇的是,不知道為什麼SpriteMe合併之後的圖片間會有這麼多空白間隔(padding)。可能是預留排版出錯時的緩衝空間吧?

image

SpriteMe直接將合併之後圖片的語法寫在受到調整的元素中。上圖是頁首背景圖片直接套用了SpriteMe的合併圖片,可以看到他以background-image跟background-position設定直接寫在元素的style屬性中了。

輸出CSS

image

雖然右上角有個「export CSS」功能,可以把合併後的圖片與語法輸出成CSS。只是在Chrome裡面發生了JavaScript錯誤而無法執行,後來我改用Firefox 4來操作,就能夠開啟SpriteMe Export CSS網頁。

image

Export CSS網頁中,先告訴我剛剛我合併的圖片網址。

然後下面列出了這個網頁使用的CSS檔案,並嘗試在這些檔案中找尋剛剛修改的元素設定位置。可惜因為Firefox的跨網域限制,SpriteMe沒辦法自動幫我分析這些CSS檔案的內容。

接著他列出CSS的建議修改方式,包括刪掉原本的圖片,並替換上新的圖片。這個建議可以讓我輕易地修改CSS檔案,非常地實用。其內容如下:

DIV id=header class=header section
{
  background-image: url("http://www.blogblog.com/thisaway/bg_header.gif")
  background-image: url("http://www.jaredhirsch.com/coolrunnings/public_images/a98ceddb07/spriteme2.png");
  background-position: 0px -18px;

}
DIV id=footer class=footer section
{
  background-image: url("http://www.blogblog.com/thisaway/bg_footer.gif")
  background-image: url("http://www.jaredhirsch.com/coolrunnings/public_images/a98ceddb07/spriteme2.png");
  background-position: 0px -118px;

}

當然,我會把SpriteMe產生的合併圖片下載之後,上傳到自己的空間,然後再把之間的網址改成我的空間,這樣才不會造成SpriteMe伺服器的負擔。

image

修改完成之後,圖片的請求數從原本的56個降低為55個囉。

分組寬度相當、顏色相近的圖片來建立合併圖片

image

如果直接採用SpriteMe的建議,把寬度、高度不等的圖片直接合併,就會出現像上面的合併圖片。在Page Speed Online建議合併圖片要盡量降低空白處,而上圖很明顯的違反了這個建議。多次嘗試之後,我發現SpriteMe只會將圖片垂直排列來合併。因此,如果只將寬度相當的圖片進行合併,就能夠將合併圖片的空白處降低到最小。

image

上圖是SpriteMe預設的建議,圖片的寬度從760px到10px都有,合併起來將會出現相當多的空白。還好,SpriteMe也提供了讓使用者自訂合併圖片的功能,請按下上面的「new sprite」按鈕。

image

這時候上面就會出現新的合併列表,但是是空的。

image

你可以從下面的圖片,將寬度差不多的圖片拖曳到這個區塊,SpriteMe就會依照你指定的圖片建立新的合併圖片。

image

我將合併圖片分成三組,個別是寬度為10px的圖片、寬度為760px的圖片,以及寬度為47px到54px之間的圖片來進行合併。

合併之後的結果如下:

  • image
  • image
  • image

這樣子空白處就減少很多囉。

另外Page Speed Online也建議將合併圖片的顏色數量降低到256色之內,這也是分組時的一個參考依據。

SpriteMe忽略了<img>圖片

仔細比較一下Page Speed Online給的建議,會發現SpriteMe還忽略了很多圖片。再細部分析一下,這些圖片都是以<img>圖片標籤顯示的內容。

image

上面的「訂閱所有留言」功能就用了大量的<img>標籤,而且都是固定常出現的小型圖片。Page Speed Online建議我合併這些圖片,但是SpriteMe並沒有分析到這邊。

為了要讓SpriteMe偵測到這些圖片,我必須先把<img>中src指定的圖片,改成以background-image背景圖片的方式來顯示。

原本我是想在<img>直接設定背景圖片,但是效果卻不如預期。Firefox中,只有將<img>顯示型態設為block的時候,才能順利顯示背景圖。因此,我決定將<img>改成<div>,並以CSS的background-image來顯示圖片。


<img>改成<div>背景圖

以下我以這個「訂閱所有留言」的功能來說明修改的過程。這是一個寫在小工具區的HTML程式碼,內容如下:

<div class="subscribe-wrapper subscribe-type-COMMENT">
    <div style="display: none;" id="SW_READER_LIST_Subscribe1COMMENT" class="subscribe expanded subscribe-type-COMMENT">
        <div class="top">
            <span onclick="return(_SW_toggleReaderList(event, &quot;Subscribe1COMMENT&quot;));" class="inner">
                <img src="http://img2.blogblog.com/img/widgets/arrow_dropdown.gif" class="subscribe-dropdown-arrow" />
                <img border="0" align="absmiddle" src="http://img1.blogblog.com/img/icon_feed12.png" class="feed-icon" alt="" />
                訂閱所有留言
            </span>
        <div class="feed-reader-links">
            <a target="_blank" href="http://www.google.com/ig/add?source=bstp&amp;feedurl=http%3A%2F%2Fpulipuli.blogspot.com%2Ffeeds%2Fcomments%2Fdefault" class="feed-reader-link">
                <img src="http://img1.blogblog.com/img/widgets/subscribe-google.png" />
            </a>
            <a target="_blank" href="http://www.bloglines.com/sub/http://pulipuli.blogspot.com/feeds/comments/default" class="feed-reader-link">
                <img src="http://img1.blogblog.com/img/widgets/subscribe-bloglines.png" />
            </a>
            <a target="_blank" href="http://www.netvibes.com/subscribe.php?url=http%3A%2F%2Fpulipuli.blogspot.com%2Ffeeds%2Fcomments%2Fdefault" class="feed-reader-link">
                <img src="http://img1.blogblog.com/img/widgets/subscribe-netvibes.png" />
            </a>
            <a target="_blank" href="http://www.newsgator.com/ngs/subscriber/subext.aspx?url=http%3A%2F%2Fpulipuli.blogspot.com%2Ffeeds%2Fcomments%2Fdefault" class="feed-reader-link">
                <img src="http://img1.blogblog.com/img/widgets/subscribe-newsgator.png" />
            </a>
            <a target="_blank" href="http://add.my.yahoo.com/content?url=http%3A%2F%2Fpulipuli.blogspot.com%2Ffeeds%2Fcomments%2Fdefault" class="feed-reader-link">
                <img src="http://img1.blogblog.com/img/widgets/subscribe-yahoo.png" />
            </a>
            <a target="_blank" href="http://pulipuli.blogspot.com/feeds/comments/default" class="feed-reader-link">
                <img align="absmiddle" src="http://img1.blogblog.com/img/icon_feed12.png" class="feed-icon" />
                Atom
            </a>
        </div>
    </div>
    <div class="bottom"></div>
</div>
<div onclick="return(_SW_toggleReaderList(event, &quot;Subscribe1COMMENT&quot;));" id="SW_READER_LIST_CLOSED_Subscribe1COMMENT" class="subscribe">
    <div class="top">
        <span class="inner">
            <img src="http://img2.blogblog.com/img/widgets/arrow_dropdown.gif" class="subscribe-dropdown-arrow" />
            <span onclick="return(_SW_toggleReaderList(event, &quot;Subscribe1COMMENT&quot;));">
                <img border="0" align="absmiddle" src="http://img1.blogblog.com/img/icon_feed12.png" class="feed-icon" alt="" />
                訂閱所有留言
            </span>
        </span>
    </div>
    <div class="bottom"></div>
    </div>
</div>

 

程式碼有點長,不過構造還算簡單。大致上需要改的有兩種類型,以下一一敘述作法。

修改顯示類型為block(區塊)圖片

image

有些<img>被賦予了display:block;的設定,表示他會跟<div>一樣以block(區塊)的樣式顯示。在「訂閱所有留言」中,下拉選單的各種圖示都是以這種形式呈現。

這種形式的<img>圖片可以很容易地修改成<div>背景圖,也不容易影響排版。

讓我們先看看image Add to Google這個圖示的原始碼:

   1: <a target="_blank" 
   2:     href="http://www.google.com/ig/add?source=bstp&amp;feedurl=http%3A%2F%2Fpulipuli.blogspot.com%2Ffeeds%2Fcomments%2Fdefault" 
   3:     class="feed-reader-link">
   4:     <img src="http://img1.blogblog.com/img/widgets/subscribe-google.png" />
   5: </a>

這是一個<a>連結標籤包含著<img>圖片標籤的元素。在其他的CSS當中,此處的<img>被設定為display:block;,因此我們可以考慮直接把這種<img>換成<div>,並加上額外的CSS設定。

修改的過程有幾個步驟:

  1. 在<img>後面建立<div>,並給予適當的class名稱,以便後續CSS設定中可以正確地選擇到該<div>。這邊要注意的是,必須完整撰寫<div></div>標籤,而不能用<div/>這種空標籤喔。
  2. 增加額外的CSS設定,包括:
    • background-image: url(圖片網址):指定<img>讀取的圖片
    • background-repeat: no-repeat:不重複圖片
    • width & height:根據圖片大小設定
  3. 移除原本的<img>

以下是修改之後的元素程式碼與CSS設定:

   1: <a target="_blank" href="http://www.google.com/ig/add?source=bstp&amp;feedurl=http%3A%2F%2Fpulipuli.blogspot.com%2Ffeeds%2Fcomments%2Fdefault" class="feed-reader-link">
   2:     <div class="subscribe-google"></div>
   3: </a>
   4: <style type="text/css">
   5: .feed-reader-link .subscribe-google {
   6:     background-image: url(http://img1.blogblog.com/img/widgets/subscribe-google.png);
   7:     background-repeat: no-repeat;
   8:     width: 104px;
   9:     height: 17px;
  10: }
  11: </style>

儘管大致上改到如此就可以告一段落,但是這段HTML還有繼續改善的空間。

由於<a>標籤中只包含<img>(後來被我換成<div>了)一個元素,因此我們可以考慮將樣式直接套用到<a>中,省略掉多餘的<div>。

更進一步修改之後的程式碼如下。必須注意的是,CSS中選擇器改成指定<a>的class名稱,並加入顯示類型display: block的設定囉:

   1: <a target="_blank" href="http://www.google.com/ig/add?source=bstp&amp;feedurl=http%3A%2F%2Fpulipuli.blogspot.com%2Ffeeds%2Fcomments%2Fdefault" class="feed-reader-link subscribe-google">
   2: </a>
   3: <style type="text/css">
   4: .feed-reader-link.subscribe-google {
   5:     background-image: url(http://img1.blogblog.com/img/widgets/subscribe-google.png);
   6:     background-repeat: no-repeat;
   7:     width: 104px;
   8:     height: 17px;    display: block;
   9: }
  10: </style>

修改之後的程式碼又更簡潔了。實際應用的時候,CSS設定會集中在其他檔案中,而不會像上面一樣跟HTML寫在一起。

修改顯示類型為inline(同軸)的圖片

image

<img>圖片的預設顯示類型是inline,意思是會跟文字一樣一起排列。而<div>卻是block顯示類型,會強制將後面的內容換行。

有些時候,<img>會以原始的inline跟其他文字一起排列,這種情況要改成<div>就比較困難,通常還要搭配float浮動樣式、margin外距調整、以及搭配一些CSS修改經驗才能達成。

這次我要修改的是「訂閱所有留言」左邊的RSS圖片,這張圖片與「訂閱所有留言」文字一起排列,是以預設的inline顯示類型呈現。它的原始碼如下:

   1: <span 
   2:  onclick="return(_SW_toggleReaderList(event, &quot;Subscribe1COMMENT&quot;));">
   3:     <img border="0" 
   4:      align="absmiddle" 
   5:      src="http://img1.blogblog.com/img/icon_feed12.png" 
   6:      class="feed-icon" alt="" />
   7:     訂閱所有留言
   8: </span>

我為它進行的修改步驟為:

  1. 在<img>後面建立<div>,並給予適當的class名稱,以便後續CSS設定中可以正確地選擇到該<div>。
  2. 增加額外的CSS設定,包括:
    1. background-image: url(圖片網址):指定<img>讀取的圖片
    2. background-repeat: no-repeat:不重複圖片
    3. width & height:根據圖片大小設定
    4. float: left:因為該圖片位於文字的左方,所以用此設定
    5. margin 調整外距
  3. 移除原本的<img>

修改後的程式碼與新增的CSS設定如下:

   1: <span 
   2:  onclick="return(_SW_toggleReaderList(event, &quot;Subscribe1COMMENT&quot;));">
   3:     <div class="feed-icon"></div>
   4:     訂閱所有留言
   5: </span>
   6: <style type="text/css">
   7: .inner .feed-icon {
   8:     background-image: url(http://img1.blogblog.com/img/icon_feed12.png);
   9:     background-repeat: no-repeat;
  10:     width: 12px;
  11:     height: 12px;
  12:     float: left;
  13:     margin-top: 2px;
  14:     margin-left: 7px;
  15: }
  16: </style>

image

修改之後,因為加上了margin外距調整,感覺比之前的<img>更順眼了點呢。

顯示圖片讓SpriteMe能夠偵測

即使把<img>改成<div>的背景圖片了,還要記得把他們顯示出來(visible),SpriteMe才能夠偵測並判斷它是否適合成為合併的圖片,否則會被歸類成不適合合併的圖片。

image

在分析之前,我先將「訂閱所有留言」的選單打開,再使用SpriteMe的功能,如上圖。

image

這下子SpriteMe總算偵測到剛剛我修改的<div>背景圖,而且將它列入建議合併的圖片中了。

其他CSS Sprites技巧

SpriteMe是分析背景圖片(background-image)以達到CSS Sprites技巧的效果,不過除了背景圖片之外,還有其他技巧可以使用。

CSS擁有無限的可能性,而且隨著瀏覽器跟標準不斷地改變,未來也可能會有更好用的技巧出現也說不定吧。


真的有必要做CSS Sprites嗎?

儘管CSS Sprites能夠降低圖片請求數量、提昇網頁讀取的速度,但任何技術都不是萬靈丹,在使用CSS Sprites的時候我也發現到一些限制,在此提出來跟大家討論一下:

合併後的圖片難以管理

現在我們將多張圖片合併成一張大型圖片,用CSS Sprites設定背景圖片與背景位置。如果未來需要變更其中一張圖片的高度,這不僅要把之前所有的圖片都找回來,還需要修正下面的圖片的背景位置。

為了避免這種情況發生,在使用CSS Sprites技巧時,盡量挑選不會變更的圖片,或是將可能會一起變更的圖片一起合併,要改的時候也一起改。

最後,要記得保留合併前的舊圖片,以免未來要重新合併時找不到圖片。我在CSS樣式檔中,就會將舊圖片的連結先註解掉,讓未來還有回復成原本圖片的機會:

   1: #header {
   2:   background-color: #634320;
   3:   /*background-image: url(http://www.blogblog.com/thisaway/bg_header.gif);*/
   4:   background-image: url(http://dl.dropbox.com/u/717137/blogger/img/bg_header_footer.png);
   5:   background-position: 0px -18px;
   6:   background-repeat: repeat-x;
   7: }
將<img>轉換成<div>的人工成本與風險相當高

SpriteMe不會分析<img>中的圖片,而需要我們手動將<img>轉換成<div>背景圖,才能順利讓SpriteMe分析。前面我也簡單地敘述了兩種轉換的過程,不過實際使用時一定會遇到許多更棘手的情況。

最大的問題仍是在<img>以inline顯示類型與<div>的block顯示類型基本上就有很大的差別。由於<div>一定要設定為block才能設定寬度與高度,所以不能單純轉換為inline顯示類型。

當然,還有許多CSS設計技巧可以迴避掉類似的問題,但不論是哪種方法,都需要相當有經驗的CSS設計師才能做到。隨隨便便套用CSS設定,都會帶來版面構造破壞的風險。

區塊有重複延展的需求時,盡量不要做CSS Sprites

前面SpriteMe的建議都是針對不重複、或是針對X軸重複的圖片建議合併,但是有時候SpriteMe的建議也不是萬能,它並不能預測到你這個區塊未來是否有需要延展的空間。

有一種CSS設計,是在指定不重複(background-repeat: no-repeat)的背景圖片(background-image),同時搭配背景顏色(background-color)的情況。這種設計並不是讓背景圖片重複來填滿背景,而是用背景顏色來填滿,但只有特定的地方顯示背景圖片而已。

image

「布丁布丁吃什麼?」的範本中時常出現這種設計,例如上圖的頁首區塊。它的CSS設定如下:

   1: #header {
   2:   background-color: #634320;
   3:   background-image: url(http://www.blogblog.com/thisaway/bg_header.gif);
   4:   background-repeat: repeat-x;
   5: }

image

如果當頁首資料量太多的時候(我繞了好多遠路),背景還能順利地延展開來,不會讓版面變得很奇怪。

image

但是如果照前述的方式將頁首區塊的背景合併成CSS Sprites,當資料量一多的時候,就會發現頁首喪失了延展性,背景變得很奇怪了。

即使不是資料量變多,而只是單純地縮小視窗寬度,資料自動往下擠壓而造成額外的高度需求,那也可能發生類似的破版畫面。

儘管有很多技巧可以避免上述的問題,不過這邊我想說的是,當區塊有擴增、延展的需求時,不要輕易地將它的背景圖片做成CSS Sprites,以免徒增版面發生錯誤的風險。


成果與結語

image

經過CSS Sprites調整之後,布丁的自我簡介(2011年版)從56個圖片請求數降低到了47個請求數。老實說,感覺成效並不明顯。

image

另一方面主要的原因在於大多數圖片並不是我能掌控的範圍,像是Google Friend Connect的大頭像、Plurk的顯示圖片。結果Page Speed Online還是建議我去合併這些圖片,我也沒輒了。

附帶一提,我並沒有特別去注意下載速度與圖片壓縮的數量,因為實際上都是小圖片的整併,比較這一點點差距沒有多大意義。

總之,經過這次的把玩,總算對CSS Sprites這個技巧有更深入的了解。以後在設計網頁的時候,不妨預先考量到可以進行CSS Sprites的轉換空間,將<img>以<div>替代,然後最後再利用SpriteMe來動手術,努力提昇提昇網頁讀取效率吧。

(more...)