附錄


表述語言

不同於 programming language 要將程式碼編譯為機器碼,script language 本身既是程式碼,也是可執行碼,所以嚴格來說應該是:「用表述語言,寫腳本程式。」

因此我不把 script 統稱為腳本,而是依使用情況,分別稱為表述語言和腳本程式。

雖然這樣一來大家會聽不懂,但唯獨這件事我不想妥協!中文「腳本語言」這一詞會誤導使用 script language 的人,導致用這語言寫了一輩子的程式,卻還不知道 script 一詞的真正涵義~

英文母語的人倒是沒這問題,他們早就知道 script 的多種意思,哪像台灣人看到「腳本」一詞總會霧煞煞:「是哪裡像腳本了?」統稱腳本,就等於「用腳本寫腳本」的意思一樣,不霧煞煞才怪!


腳本程式

script 是「隨手把事情抄記下來」的意思,因此腳本語言 (Scripting language) 的意思,是隨手就能寫一段程式來跑的語言。雖說隨手抄記,但通常有一定形式要照著寫,不是隨心所欲亂寫一通,例如航海日誌、賽事紀錄、舞台排演、考試卷…各有自己的表現格式!但也就是有格式了,方能隨手劃個記號上去就好,而寫得更少。例如在第一隊第二號投手的三振欄,單單寫個 4 即可,你不用寫「某隊的某某某上場中繼投出 4 次三振」。總之 script 跟 note 是很不一樣滴,它比較像填寫,而不是書寫~

腳本語言通常內建在某個軟體或特定領域,用來直接表述軟體的功能,而不是開發軟體或設計功能。軟體和功能已經開發好了,我只是用腳本語言操作它。JavaScript 就是這麼一款用來操作網頁瀏覽器內建功能的語言,你無法用它開發新的視窗應用軟體,而是在網頁這個特定領域裡發揮創意,用網頁的形式去做跟視窗應用軟體沒兩樣的事。

相對比傳統程式語言,安裝困難、語法嚴格、編譯麻煩,寫起程式就像大工程一樣,JavaScript 這樣的腳本語言,確實寫起來執行時,很輕巧、又快活。

中文維基百科稱之為「手稿語言」,比「腳本語言」更容易理解。我自己則稱「表述語言」,因為它表達與陳述的對象是軟體既有功能。寫出來的程式則稱「腳本程式」,因為這段程式只是寫給特定領域辦個活動用的,就像寫給舞台上演出用的劇本一樣~


getElementsByTagName() 與 querySelectorAll()

querySelectorAll 傳回 NodeList,它是另外複製一份出來的獨立子樹,當 HTML 母樹結構有變化時,無法反應到子樹,只有子樹發生變化時,會更新到 HTML 樹。優點是支援 forEach(),缺點是增刪查改 HTML 結構樹的速度較慢,因為必須先修改子樹,再更新到母樹。

getElementsByTagName 傳回 HTMLCollection,它有點像 HTML 樹狀結構中一小部分內容的參照(reference),當 HTML 樹狀結構變化時,反應的就是最新的狀態。優點是增刪查改 HTML 結構的速度較快,因為直接改,沒有額外成本。缺點是不支援 forEach(),但可用 for of 代替。


5 2
執行速度不是決定使用哪一個的唯一考量!如果你想用 CSS Selector 語法直接指定某特定元素來查詢與修改,那就用 querySelectorAll(),而不是凡事優先使用 getElementsByTagName()。尤其 CSS Selector 功能強大,很多任務是 getElementsByTagName() 做不到的,querySelectorAll() 能省去很多寫程式的工作量。若沒明顯感到程式慢,優先使用 querySelectorAll() 和 querySelector() 才是聰明人的做法~

如果要對某個樹狀結構複雜又龐大的元素,從中增加與刪除子元素,甚至還跑迴圈來執行,那就改用 getElementsByTagName() 來提升執行速度。舉個誇張的例子,就是 body 元素,你不該用 querySelector('body') 複製一整棵網頁主體內容的子樹來增刪查改,而是直接用 document.body。


善用「事件可以覆寫」的特性


事件裡面的程式隨時隨地都能被改寫

事件是會被覆寫的:


第一次在網頁按下鍵盤時,會顯示 AAA,以後則顯示 BBB。
因此寫 JavaScript 程式,你精心佈局的事件驅動模式,很容易就可以被重寫掉。例如某人從外部載入你的 *.js 程式庫,卻不知道你裡面用了 window.onload 為網頁處理一些特殊工作,而他自己也在網頁內部用了 window.onload,這很可能會導致你所提供的程式庫無法正常執行,因為被改寫了。


善用事件隨時可以覆寫的特性

但另一方面來說,這也是讓程式結構變得簡潔的語法特性。

比如說,你希望設計一個用鍵盤方向鍵來操控的遊戲,如果事件無法覆寫,那一個網頁只能限制用一次 window.onkeydown,你只好把所有動作通通塞在 window.onkeydown 裡面,程式會非常地亂…


更別說每個 if 裡面還需要補充其它特殊處理狀況的程式時,會有多雜亂了~

這時還不如利用事件可以覆寫的特性,改寫為:


根據變換的遊戲操作介面狀況,切換不同的事件處理函式,例如進入地圖模式時,就呼叫「地圖()」改寫 window.onload 的內容,進入選單畫面時,就呼叫「選單()」改寫 window.onload 的內容…程式顯得更有組織多了!而且每次改寫,只執行分屬不同遊戲操作介面的程式,不用全部混雜在一起,擔心誤判的話執行錯誤的遊戲指令,程式執行起來更穩定。而在地圖模式想按 ESC 鍵切換到選單模式,追加程式時相當直覺:


addEventListener() 簡單示範


範例


補充

addEventListener() 可用的事件,通常只要將原本事件的 on 去掉即可。例如原本移開滑鼠的事件可能這樣寫:


addEventListener() 改成這樣…


差異

onXXX 系列的事件會被覆寫,addEventListener() 不會。


按下按鈕後,會接連顯示 AAA 和 BBB 兩則文字內容。


注意

如果要跑另外寫好的具名函式,請傳入「函式的參照」,而不是「呼叫函式」的觀念。所以要這樣寫才對…


這樣是錯的:


如果你是剛從其它程式語言跨入 JavaScript 的人,所以不懂為何會有這樣莫名其妙的情景,不妨想像 f1 其實是…


亦即 f1 是 function 物件,接著再將這樣物件的識別名稱傳入到這樣 API 格式裡:

addEventListener(String,Function)

addEventListener() 第二個參數接受的是 Function 物件,所以不要當作第二個參數是用來「呼叫函式」。


技巧

document.onready

document.addEventListener('DOMContentLoaded',函式);

這相當於 jQuery 廣受好評的 .ready() 事件,只要網頁的 DOM 結構配置完成就會觸發,不用等其它圖片之類的資源載入完才觸發。

once:true

addEventListener('click',函式,{once:true});

只觸發一次。


onbeforeunload 事件

若程式還沒執行完畢,網頁就被關閉時,希望能提示「是否要離開網站?可能無法儲存您所做的變更。」可以加入這段程式:


然後在工作結束後加入這行程式:


為防止彈出訊息框干擾使用者,網頁瀏覽器會阻擋這項功能!所以這項功能要寫在使用者手動執行時觸發,不要寫在頁面載入時自動觸發。


Drag and Drop

在兩個 DIV 元素之間拖曳 P 元素:


XMLHttpRequest 與 Fetch

這兩個可以做同樣的事:由客戶端發出請求,取得回應資料。

XMLHttpRequest 其實是 IE5 的 ActiveXObject("Microsoft.XMLHTTP"),後來 Mozilla 也仿造了一個 nsIXMLHttpRequest 來用,於是 W3C 為它進行了標準化,日後成為 Ajax 技術的基礎。

後來改制定 Promise 風格的 Fetch 簡化操作,還解決 XMLHttpRequest 職責混亂的缺失,只要看過新 API 的範例都會拋棄舊的才對。

現在的網頁瀏覽器,會封鎖本地檔案的來源請求,所以本章節範例只能在 HTTP 正常運作,沒辦法在 file:// 下載入資料。


XMLHttpRequest 基本範例

data.txt

client.html


Fetch 基本範例

data.txt

client.html


進階範例

若 XMLHttpRequest 請求的檔案是 HTML 標記文件,可以用 responseXML 直接轉為 DOM 格式的資料,例如:

data.html

client.html

Fetch 只能取得 text 格式的資料,必須另外用 DOMParser 轉為 DOM:


re:Array.prototype.reduce()

6 怎麼來的?這個範例中,我們在 reduce() 設定的初始值是 5,然後跑 callback 函式,因此第一次執行時,這段程式:

currentValue=previousValue+array[index];
document.write(currentValue);
return currentValue;

就等於:

5+1;
document.write(5+1);
return 5+1;

8 怎麼來的?由於 reduce() 會把陣列的元素值,從頭到尾逐一代入給 callback 執行,所以繼續做出如下的程式動作:

6+2;
document.write(6+2);
return 6+2;

因此得到 8。

依此類推,因為還有一個元素沒跑,所以繼續做出如下程式動作,而得到 11:

8+3;
document.write(8+3);
return 8+3;


為什麼是 article 包含 section?

因為你會用 article 表示一篇文章、一串討論、一張網頁,而不會用 section。因此 article 是更像 nav 和 aside 這樣的專欄區,而 section 則用來在這些區域中分割多個子區塊。

當然,如果是在 body 這個區塊中,用 section 分割好幾個子區塊,再各自放入 article,其實也是可以的!因此誰包含誰並不是絕對,端看你網頁結構的語意。

但通常 body 就是劃分成 header、nav、article、footer、aside 等區塊,所以 HTML5 才會定義這些標籤!不用這些,偏偏用 section 來分割 body,感覺就跟 HTML 4.01 一律用 div 來分割沒兩樣,不是很適合~

除非這網頁不是一篇內容為主的文章,而是功能為主的 Webapp,那就很有可能 section 包含 article 會顯得更適切,這時就用 section 吧!


[*] 但是讓 section 跟 header、nav、article、footer、aside 並列,做為 Webapp 的專欄區,就 HTML5 語意來講依然不適合。section 就是用來分割數個對等的子區塊,而不是獨立成一個跟別人並存的主區塊。


何時使用 <hgroup>?

如果只是用了好幾個 H1 到 H6 等標籤,其實並不用包括在 HGROUP 裡面:


但如果多個 H1 到 H6 等標籤外,還有其它種類的標籤,就適合放在 HGROUP 裡面來區隔:


<html lang='zh'>


過去有字元編碼切換的問題

過去 HTML 文件,是各國用自己的字元編碼保存,例如 Big5、GB2312、Shift_JIS。

於是瀏覽器在顯示不同國家的網頁時,必須切換到正確的字元編碼,否則用 Big5 字元編碼去處理 Shift_JIS 字元編碼的網頁,只會看到一堆亂碼。

為了解決這問題,可以在網頁指定 lang 屬性,設定使用哪個國家的語系。


UTF-8 也有自己的狀況

但你要知道,同一個漢字和標點符號,台灣、中國、日本的寫法不見得一樣,因此同一個字、同一個全形符號,不同國家語系的人,在網頁上看到的不見得一樣:

設為 lang='zh-TW':才、直、具,步、邊、內。(台灣習慣的中文寫法和標點符號)
設為 lang='ja'  :才、直、具,步、邊、內。(日本漢字寫法經常和中文不一樣)
設為 lang='zh-CN':才、直、具,步、邊、內。(中國的全形符號和台灣不一樣)

如果你希望文字和符號固定使用哪一國的寫法,而不是不同國家顯示不同寫法的文字和符號,就可以設定 lang。


複製文字到剪貼簿

使用 document.execCommand('copy') 複製選取的文字到剪貼簿。

可搭配 元件.select() 選取填表元件的文字,以及把想複製的文字寫在填表元件選取。


快取與離線應用

將檔案放在「快取」以後,沒有網路連線也能使用網頁。


cache.manifest

指定 index.html 和 style.css 為快取:


index.html


style.css


測試時,先以網頁瀏覽器開啟 webhosting 的 index.html,然後中斷連線,再重新整理網頁,並不會發生「找不到伺服器」的訊息,而是像保持連線那樣正常顯示網頁。

必須注意的是,快取通常在 cache.manifest 的檔案修改日期變動過,才會更新 index.html 和 style.css,修改 index.html 或 style.css 是不會更新快取的。所以,雖然 cache.manifest 內容沒變,也應該重新儲存一下更新修改日期,確保對方能更新檔案。


<meta>


作用

附加資訊在網頁裡面,而不視為內容的一部分。


用法


常用名稱

author作者。
copyright版權宣告。
description簡介、摘要。
distribution發布區域、限定地區。
generator產生網頁的工具。1
keywords提供搜尋的關鍵字。
robots給搜尋引擎的權限,可設定為:all(允許搜尋全部)、follow(允許搜尋超鏈結)、index(允許搜尋內容)、nofollow(禁止搜尋超鏈結)、none(禁止搜尋全部)、noindex(禁止搜尋內容)。
viewport頁面尺寸,可用鍵值對的語法設定 width(通常設為 device-width)、initial-scale(初始縮放倍率)、maximum-scale(最大縮放倍率)、user-scalabl(使用者能否自訂縮放倍率,以 0 和 1 表示)。

meta 的 name 值有標準、非標準之分,像上面的 robots 就不是標準,copyright 則是已汰除。

但夾帶的附加訊息有必要規範與標準嗎?這部分我的態度是沒必要,有人需要夾帶這條訊息,那就夾帶給他,並不影響不需要的人,所以也不會有相容性的問題。

夾帶訊息的相容性和影響層面,是由需要的人定義與承擔,需求者自己就是規範與標準。

所以,想要滿足某方需求,就放手去用,不用管標準與規範。夾帶訊息本來就是滿足不同需求者用的,用於這種無法統一標準化規範的場合。


HTTP-EQUIV 屬性

META 除了藉由 NAME 屬性提供附加訊息,還有另外一個功能,就是用 HTTP-EQUIV 屬性提供參數給瀏覽器,以執行軟體本身的功能。


在 JavaScript 取得 META 訊息


[1] 早期 FrontPage 和 Dreamweaver 產生的 HTML 格式並不相容!FrontPage 產生的 HTML 是 IE 能正常顯示就好的不標準格式,Dreamweaver 是遵循 W3C 規範,在 Netscape Navigator 和 IE 都能正常顯示。因此在 2000 年代初期,有時候很需要藉由 generator 的資訊,判斷這會是怎樣規範的 HTML。


使用多個 window.setInterval() 充當平行程序

設 f1 為每秒鐘累加全域變數 a 的執行序,f2 為輸出全域變數 a 的執行序,f3 為結束 f1 的執行序:


網頁從 1 到 10 顯示數字,然後停止。
因此當你需要平行處理多個執行緒,卻礙於 Web Workers 無法存取非安全性執行緒如 DOM 元件的限制而裹足不前時,可以考慮建立多個 window.setInterval() 來充當。但要注意精準度與效率的問題,畢竟 window.setInterval() 只是個折衷的做法,並非真正的「背景執行」,無法取代 Web Workers 的設計。


關閉填表元件的作用

想讓填表元件 (Form) 看得見但不能用,例如:



做法不是設定 CSS 樣式,而是以 JavaScript 設定元件屬性 disabled 為 true。例如:


觸發 SELECT 元件的事件

元件.focus();
元件.selectedIndex=選擇項目;
元件.onchange();


FileInput + FileReader


file.txt


sample.html


操作方式

用支援 HTML5 的瀏覽器開啟 sample.html,然後用網頁的按鈕點選 file.txt 檔案,就會讀取檔案內容「hello」到網頁的 p 標籤裡面。


Write file


搭配上一節載入檔案的程式,就能實現本機端純文字文件讀寫的功能!

雖然用下載的方式保存檔案,以及開啟舊檔的方式讀取檔案,稱不上完美的解決方案,但對不想依賴 HTTP Server 的單機網頁應用程式來說,還是值得採用的手法,相當於瀏覽器擴充程式常用的匯出、匯入,來備份設定檔。


用 File System API 讀寫純文字檔案

因為 FileInput 和 FileReader 的使用體驗實在很差,所以在一面倒的呼聲下,正式納入 File System API 標準,可進行更多檔案相關的程式設計任務,連資料夾都能處理了!

缺點是它很新,2020 年推出 Chromium 86 正式支援,但那時只有基本功能,完整功能至少要 2022 年推出的 Chromium 102,甚至 Android 的 Chrome 直到 2025 年推出的 132 版才開放 File System API 寫入檔案的權限,其實很多裝置無法正常使用。

本文只介紹讀寫純文字檔案的部分~


data- 標籤屬性和 dataset 物件


58
PG


網站變黑白

對 HTML 標籤使用 filter:grayscale(1) 樣式,例如:

html { filter:grayscale(1) }

<html style='filter:grayscale(1)'>

之所以不用在 BODY 標籤,是因為 backgroundImage 不會變黑白,得另外準備黑白的背景圖。


剪影

color:transparent; text-shadow:0 0 0 black


置中

做法千奇百怪,我慣用的有四種:


Emoji 顯示為黑白圖示而不是彩色圖案的問題

這問題發生在 font-family 設定為文字適用的字型,設定為 Emoji 專用的字型,例如 Segoe UI Emoji,即可解決問題。