JavaScript - 程式碼壓縮與最佳化

壓縮程式碼

在正式上線前,可以將JavaScript程式碼進行壓縮,減少檔案大小,讓使用者下載更快,也節省頻寬。方法有手動與自動兩種。

手動(效果不好):

  • 刪除「長變數名」、「註解」、「空格」、「換行」…
  • true改用1, false改用0

缺點,易產生錯誤。

自動(建議使用):

使用工具,自動化產生壓縮式的JavaScript檔案:


JavaScript自動化壓縮工具還有很多,各位可以自行上網找一套自己喜歡的來用。未來新的 Visual Studio 11 / .NET Framework 4.5 會將自動化壓縮最佳化 JavaScript 與 CSS 等特性加入。

JavaScript 程式碼最佳化

這個部份提出的都是自動化壓縮工具比較幫不上忙,有些是邏輯技巧,有些是程式技巧,順序上,應該先進行內部程式碼最佳化,然後再使用壓縮工具來加速提升。

宣告變數

JavaScript中未宣告的變數(函式中也一樣)預設為全域變數,全域變數只有在瀏覽器關閉後才釋放。

name = Bruce;

使用內建函數

內建函數都已編譯過,執行速度會比即時編譯的JavaScript快很多。

巢狀if

將出現率最多的情況放在前面,另外,使用switch語句替代巢狀if/else if。

整合同性質工作

指令碼中程式行數越少,執行時間越快,將同性質的指令碼集中一次處理,例如,例如宣告變數:

// 宣告方法一
var name="Bruce";
var age = 18;
var birthday = new Date(2010/10/10);

// 宣告方法二
var name="Bruce", age=18,birthday=new Date(2010/10/10);

節約使用DOM

JavaScript對DOM的處理可能是最耗時費力,盡量減少DOM的操作,例如,為ul新增10個li,我們的處理方式可以有:

var oUL = document.getElementById("ulItem");
for (var i=0; i<10; i++){
  var oLI = document.createElement("li");
  oUL.appendChild(oLI); //10次
  oLI.appendChild(document.createTextNode("Item" + i)); //10次
}

共執行20次DOM操作。其實我們可以有更好的選擇:

var oUL = document.getElementById("ulItem");
var oTemp = document.createDocumentFragment();
for (var i=0; i<10; i++){
  var oLI = document.createElement("li");
  oLI.appendChild(document.createTextNode("Item" + i));
  oTemp.appendChild(oLI);
}
oUL.appendChild(oTemp); //1次

共執行1次DOM操作。20 : 1,相信那段程式碼效率一眼就可以看出來。

[KKBruce補充]可多利用 document.createDocumentFragment 來最佳化相關程式碼。

最佳化的影響

壓縮程式碼與程式碼最佳化通常會大大影響「可讀性」,不佳的可讀性讓後續「維護」困難度升高,所以正常做法是分為上線和開發兩個版本。

  • 上線:使用壓縮及最佳化,加速下載速度,提高執行效能。
  • 開發:保持良好程式架構和註解等,提高可讀性及減低維護難度。

JavaScript - 除錯, Debug

常見錯誤有:
  1. 語法錯誤(syntax error)
  2. 邏輯錯誤(logic error)
  3. 執行錯誤(runtime error)

除蟲清單:
  • 確定「括號( )」成對出現
  • 確定程式區塊的「大括號{ }」成對出現
  • 避免打錯字,使用有關鍵字提示功能IDE開發工具
  • JavaScript有大小寫之分
  • 字串與變數連接錯誤,例如,多或少+連接符號
  • 單、雙引號,維持一致使用方式
  • 單、雙引號,沒有成對出現也是常見錯誤
  • 確定物件在使用前已經建立
  • 別對區域變數與全域變數取相同的名稱
  • 變數不存在
  • 等號(=)與比較符號(==)使用錯誤
  • 記得要結束";"

bug處理:
  • alert()與document.write()方法
  • 使用window.onerror事件
  • try ... catch
  • Firebug for Firefox

目前主流且最新版本的瀏覽器(IE9, Chrome 16)都內建不錯的除錯功能,也是另一種選擇。

JavaScript - 物件導向(Object-oriented Language, OOP)

Object-oriented Language

Object-oriented Language,物件導向程式語言,特性有:

  1. 封裝Encapsulation
  2. 繼承Inheritance
  3. 多形Polymorphism

Object(物件)

讓正真有需要的原始碼接觸資料,減少資料曝露的程度,讓程式碼再利用。物件結合資料與行為,建立一種新的資料型別,其中可以儲存(store)資料,也可以根據資料而行動(act),所以物件是個容器,其中儲存資料,並聯繫資料與依據資料行動的原始碼。物件(Object)在一個儲存容器內聯繫起變數與函式。

資料(變數)+行為(函式)=物件

物件內的資料稱「property」(屬性、特性),物件內的行為稱「method」(方法),

property + method = Object

.(點)

點號運算子('.' dot operator)用於取用物件property與method,

Object + . + property/method

例如,

// mail 物件
// . 運算子
// from, to, send 方法
mail.from = "...";
mail.to = "...";
...
mail.send();

物件可以包含其他物件,例如,

var mail = new Mail(new Date(), "from", ...);
在Mail建構式中傳入一個Date物件。

建構式(constructor) - 屬性(property)

物件具有資料,資料必須在物件建立時「初始化」,每個自訂物件都需要自己的建構式,名稱與「物件相同」。

function Mail(from,to,body,subject){
  this.from = from;
  this.to = to;
  this.body = body;
  this.subject = subject;
}

建構式使用首字母大寫,建立property需使用JavaScript的關鍵字「this」,關鍵字this區分物件property與一般變數。this指定物件property的擁有權,同時設定property的初始值建立屬於「這個(this)」物件的property,例如,

this.from = from;

將收到的參數指定給新property。沒有this,建構式不會知道你正在建立物件property。以建構式建立物件時,我們使用new運算子,它呼叫物件的建構式,開啟物件建立過程。例如,

var mail = new Mail("from","to","body","subject");

  1. var mail:新物件儲存在變數裡
  2. new Mail(...):new運算子建立新Mail物件(呼叫建構式)
  3. "from","to","body",...:傳入參數給建構式,以設定property

建構式(constructor) - 方法(method)

function Mail(from,to,body,subject){
  // Object method
  this.send = function(){
  ...
  }
}

該如何知道指令碼(函式)應放入method?

method就是根據物件的property而採取某些行動,換言之,如果指令碼需要取用物件內部資料,就非常合適放入method。

把函式轉為方法

  1. 宣告method
  2. 把現有程式碼移入method裡
  3. 修改使用物件property

宣告method,使用this建立method,與建立property類似。例如,

this.send = function(){
  // Do something
}

關於選用參數

function Mail(from,to,body,subject,smtp,port){
  ...
  // 假設smtp與port為選用功能
  this.smtp = smtp;
  this.port = port;
}

當某個參數未傳入給function、method、constructor時,在任何試圖使用參數值的原始碼裡,它的值都是null,在缺少參數相對應的property將會被設為null。確認選用參數在function參數清單的最後面。例如:當funcion有兩個參數,可能的選擇有:

  • 兩個都傳入
  • 只傳入第一個
  • 兩個都不傳
  • 無法只傳第二個

複本

Mail物件的send()方法,Mail物件的method是在建構式裡,利用關鍵字this建立,但依此建立的Mail物件都會各有一份物件方法的複本,關鍵字"this"用於設置「instance」擁有的property和method。例如,

var mail1 = new Mail(...);
var mail2 = new Mail(...);
var mail3 = new Mail(...);

每個Mail物件(mail1,mail2,mail3),都會有自己的property與method複本,等於會有三份method,這是浪費又沒效率。property為每個物件儲存獨特資料,method應該為物件所共享。

Class與instance

類別Class,是物件的描述,樣版、藍圖。例如,

function Mail(...)

實例Instance,實際物件,依Class建立。例如,

var mail = new Mail(...);
mail是依Mail來建立的物件(instance)。

每個instance的property多半都不一樣,所以每個instance才需要有自己的property複本,但method沒有必要複製到每個instance中。

Class擁有method

class-owned instance method,當method為Class所擁有時,所有該Class的instance都可以取用此method,而不需要另外複製一份。

在Class使用prototype

在JavaScript中每個物件都有一個prototype物件(以property存在),prototype用以設定Class level(類別層級)的property與method, 而非屬於instance。也就是說,我們需要把method的「擁有權」指派給Class,而不是指派小單一instance,例如,

Mail.prototype.send = function(){}

prototype使用需在建構式之外。send()方法直接附加到Mail class本身,而非隸屬於某個instance,這樣無論Mail class建立多少份instance,都只會有一份send()方法。當send()接受呼叫時,將在class內執行,例如,

mail1.send();
mail2.send();
mail3.send();

當其他Mail物件的instance也呼叫了send()方法,仍會執行Class內的同一方法。

class property
類別屬性,此property只需要在類別儲存一次,但能被所有instance存取,例如,

Mail.prototype.smtp = "smtp.kkbruce.net";
Mail.prototype.port = "25";

此property只有一個值,讓所有instance共享。想成,它是class的全域變數,但只能讓class對應的instance取用。JavaScript標準物件能也使用prototype,每個物件都有prototype,它允許在Class Level增加property與method,例如,擴充Date物件,

Date.prototype.shortFormat = function(){
  return this.getFullYear() +'/'+(this.getMonth()+1)+'/'+ this.getDate();
}

var d = new Date();
d.shortFormat();
這樣即可傳回「年/月/日」格式日期函式。

Class自己的method

類別方法(class method),它屬於Class,只能取用class property,不能取用instance property,建立class method是直接為Class設定method,而不使用prototype,例如,

Mail.showSMTP = function(){
  alert('SMTP:'+Mail.prototype.smtp+' ,\n Port:'+Mail.prototype.port);
}
...
Mail.showSMTP();
  • Mail.showSMTP:建立Mail物件的showSMTP方法
  • Mail.prototype.smtp:為了從class method取用class property,你必須下探至prototype,smtp是個Mail class property,所以Mail class method可以取用
  • Mail.showSMTP();:直接透過Class名稱Mail來呼叫方法

再回建構式

讓建構式專注在property的建立與初始化 OOP範例:AJAX OOP的寫法

function AjaxRequest() {
  if (window.XMLHttpRequest) {
    try {
      this.request = new XMLHttpRequest();
    } catch(e) {
      this.request = null;
    }
  } else if (window.ActiveXObject) {
      try {
        this.request = new ActiveXObject("Microsoft.XMLHTTP");
      } catch(e) {
        this.request = null;
      }
  }

if (this.request == null)
    alert("建立Ajax物件錯誤.\n" + "細節:" + e.message);
}

AjaxRequest.prototype.send = function(type, url, handler, postDataType, postData) {
  //type, url, handler為get必要參數
  //postDataType, postData為post必要參數
  if (this.request != null) {
    this.request.abort();

    url += "?dummy=" + new Date().getTime();

    try {
      this.request.onreadystatechange = handler;
      this.request.open(type, url, true); 
      if (type.toLowerCase() == "get") {
        this.request.send(null);
      } else {
        this.request.setRequestHeader("Content-Type", postDataType);
        this.request.send(postData);
      }
    } catch(e) {
      alert("Ajax程式錯誤.\n" + "細節:" + e);
    }
  }
}

AjaxRequest.prototype.getReadyState = function() {
  return this.request.readyState;
}

AjaxRequest.prototype.getStatus = function() {
  return this.request.status;
}

AjaxRequest.prototype.getResponseText = function() {
  return this.request.responseText;
}

AjaxRequest.prototype.getResponseXML = function() {
  return this.request.responseXML;
}

宣告AjaxRequest物件,

var ajax = new AjaxRequest();

JavaScript - Asynchronous JavaScript And XML, AJAX

同步與非同步

同步HTTP請求:請求 → 顯示 → 互動 → 暫停 → 請求 → 顯示 → 互動 → 暫停 …

需要不斷重新刷新整個頁面,會出現一段時間的空白。

非同步HTTP請求:請求 → 顯示 → 互動 → (請求)互動 → (請求)互動 …

非同步HTTP請求在顯示、執行互動不需要再暫停操作,可以馬上執行所需的互動,只向伺服器要求一小部份資料,然後更新頁面的一小部份, 不需要重新刷新整個頁面,也不會出現一段時間的空白。

非同步好處

  1. 減輕伺服器的負擔,加快瀏覽速度(只給所需)
  2. 帶來更好的用戶體驗(更流暢的畫面)
  3. 標準化技術,主流瀏覽器都支援
  4. 促進頁面呈現與資料分離(提高可維護性)

AJAX組成

AJAX不是單一技術,是集4種技術而成,JavaScript、CSS、DOM、XMLHttpRequest。(或說5種,再加上(X)HTML)

JavaScript AJAX技術組成
名稱說明
JavaScript通用指令碼語言。是撰寫AJAX主要程式語言
CSS為Web頁面元素提供視覺化樣式的定義方法, AJAX透過修改CSS樣式,改變使用者介面
DOMAJAX可以在執行時透過DOM來改變使用者介面,或局部更新某個節點
XMLHttpRequestXMLHttpRequest物件允許Web開發人員向伺服器以非同步方式取資料, 資料格式通常是XML或是文字,目前較流行為JSON

XMLHttpRequest物件

能夠直接在Client使用Javascript來提出HTTP請求。IE內建MSXML剖析器有多個版本,其提供的XMLHttpReqest物件也有多個版本(不建議使用)。

KKBruce提醒,除非您必須處理類似IE等相容性問題,不然是不建議使用以下表格內容。

JavaScript IE內建MSXML剖析器
版本DLL名稱ProgID
2.0(IE5)msxml.dllMicrosoft.XMLHttp
2.6msxml2.dllMSXML2.XMLHttp
3.0(IE6)msxml3.dllMSXML2.XMLHttp.3.0
4.0msxml4.dllMSXML2.XMLHttp.4.0
5.0msxml5.dllMSXML2.XMLHttp.5.0
6.0msxml6.dllMSXML2.XMLHttp.6.0

建立XMLHttpRequest物件

在Mozilla,Chrome和Safaris等主流瀏覽器是內建物件,例如,

// 1. 透過 new XMLHttpRequest() 產生物件
// 2. 注意大小寫
var xmlHttp=new XMLHttpRequest();

在IE6之前是透過ActiveX控制項物件(由MSXML提供),例如,

var xmlHttp=new ActiveXObject("Microsoft.XMLHTTP");

IE7之後,同時支援原生XMLHttpRequest物件(預設值)和ActiveX控制項物件,例如,

var xmlHttp;
function createXMLHttpRequest(){
  if (window.XMLHttpRequest) //支援原生型
    xmlHttp = new XMLHttpRequest();
  else if (window.ActiveXObject) //IE6以下
    xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
}

[KKBruce補充] 建立XMLHttpRequest物件注意事項

XMLHttpRequest物件屬性

JavaScript XMLHttpRequest物件屬性
名稱說明
readyState唯讀屬性,目前狀態
responseBody唯讀屬性,取得伺服器的回應,傳回非符號位元組的陣列
responseSteam唯讀屬性,取得伺服器的回應,傳回IStream資料流物件
responseText唯讀屬性,取得伺服器的回應,傳回字串
responseXML唯讀屬性,取得伺服器的回應,傳回XML DOM物件,可作DOM處理
status唯讀屬性,取得HTTP狀態碼
statusText唯讀屬性,取得HTTP狀態訊息字串
onreadystatechange存取屬性,執行readyState屬性改變的事件處理,設定處理的程序

readyState會回傳一數值,以表示目前處理的進度。

JavaScript readyState回傳狀態值
名稱說明
0uninitalized,未初始化
1loading,載入中
2loaded,載入完成
3interactive,可互動
4completed,完成

status會回傳狀態碼,以表示目前處理的結果。

流程 -
名稱說明
200OK,請求成功
202請求被接收,但處理未完成
400錯誤的請求
404Not Found,找不到網頁
500Internal Server Error,內部錯誤
...…其他網頁錯誤碼

[KKBruce補充] 這裡有一篇整理很棒的 HTTP 狀態碼,【HTTP 狀態碼,在 IIS 7.0 和 IIS 7.5】非常建議加入我的最愛中,在查詢錯誤的 HTTP 狀態碼時非常有用。例如,我們一般只知道 404 狀態碼是找不到,但 404 下還有整整 20 項細部的定義,這對於開發人員是不可多得的資訊,可以更快速清楚問題所在,而不是用經驗法則在處理。

XMLHttpRequest物件方法

JavaScript XMLHttpRequest物件方法
名稱說明
abort()取消目前HTTP請求
getAllResponseHeaders()取得全部HTTP標頭內容
getResponseHeader("指定Header名稱")取得指定HTTP標頭名稱的內容
open("GET|POST","URL",async,userid,pwd)開啟HTTP請求
send("參數")傳送HTTP請求到伺服器
setRequestHeader("自訂Header名稱",value)自訂HTTP標頭資料

XMLHttpRequest物件open方法參數
名稱說明
GET取得伺服器回應
POST傳送資料到伺服器
URL請求的XML文件或伺服端程式(ASP.NET, PHP, JSP...)
async布林值, 預設true,false表示需要等到伺服器回應後才繼續執行Javascript
userid, pwd伺服器需要id/password,才需要使用

send("參數")說明

參數表示要向伺服器發送的資料,參數格式為查詢字串的形式,例如,

var param = "name=Bruce&age=18";
在open中指定為GET,則參數作為查詢字串傳送,如果指定為POST,則作為HTTP的POST方法傳送,對於send()方法而言,此參數為必需, 如不發送任何資料,請傳送null即可。

// GET
xmlHttp.send(null);
// POST
xmlHttp.send(param);

setRequestHeader()說明

如果使用POST方法,在發送之前必須設定HTTP標頭,例如,

xmlHttp.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
// 加上UTF-8是最好
xmlHttp.setRequestHeader("Content-Type","application/x-www-form-urlencoded;charset=UTF-8");

AJAX下的GET與POST

function startRequest(){
    // 建立xmlHttp物件
 createXMLHttpRequest(); 
 // 向Demo.aspx,使用GET開啟請求
 xmlHttp.open("GET","Demo.aspx");
 // 狀態有改變時,就執行
 xmlHttp.onreadystatechange = function(){
     // 當狀態值為4(完成)與狀態碼為200(請求成功)
  if(xmlHttp.readyState == 4 && xmlHttp.status == 200)
      // Do something
   alert("服務器傳回訊息: " + xmlHttp.responseText);
 }
 // 傳送HTTP請求到伺服器
 // GET記得加上null
 xmlHttp.send(null);
}

IE會自動暫存非同步的結果,解決方法是每次非同步請求的URL不同,我們可以加上一個伺服器不需要的參數,讓每次的URL都不同。

// 加上一個時間戳計當亂數使用
var url = "Demo.aspx?timestamp=" & new Date().getTime();
xmlHttp.open("GET",url, true);

GET請求如果有參數,直接放入URL網址中,例如,

var queryString = "name=Bruce&age=18";
var url = "Demo.aspx?" + queryString + "&timestamp=" + new Date().getTime();

POST請求有參數,統一放在send()中來傳送,例如,

var queryString = "name=Bruce&age=18";
var url = "Demo.aspx?timestamp=" & new Date().getTime();
// url放在open()
xmlHttp.open("POST",url,true);
...
xmlHttp.setRequestHeader("Content-Type","application/x-www-form-urlencoded;charset=UTF-8");
// queryString放在send()
xmlHttp.send(queryString);

POST方法中文會出現亂碼

在IE8與Firefox上測試已無此問題,但為了相容性還是建議加上解決方法。

這是因為XMLHttpRequest物件在處理傳回responseText/XML時是使用UTF-8編碼,建議使用解決方法2,對回傳的資料進行decodeURIComponent()解碼即可。

解決方法一

對要傳送的資料進行兩次encodeURI()編碼,以解決中文亂碼問題,例如,

...
xmlHttp.send(encodeURI(encodeURI(queryString)));

對傳回資料responseText/XML再進行一次decodeURI()解碼,例如,

responseDiv.innerHTML = decodeURI(xmlHttp.responseText);

解決方法二

setRequestHeader直接宣告(charset)使用UTF-8,例如,

xmlHttp.setRequestHeader("Content-Type","application/x-www-form-urlencoded;charset=UTF-8");
xmlHttp.send(queryString);

對傳回資料responseText/XML進行decodeURIComponent()解碼,例如,

responseDiv.innerHTML = decodeURIComponent((xmlHttp.responseXML));

解決方法三

ASP,ASP.NET: 輸出之前Response.Charset = "big5 | gb2312",依取得的頁面charset設定。

onreadystatechange方法

AJAX的重點在狀態改變後的事件處理函式(handler),也可以說,這是唯一我們必須自訂(手動)設計程式碼的地方,換個方向說,我們在請求/回應,而伺服器回應資料後,我們要如何處理及顯示到網頁上(DOM)。

同時觸發非同步問題

如果我們必須同時觸發(呼叫)多個AJAX物件,因為我們通常都是把建立的AJAX物件的xmlHttp宣告為全域變數,所以可能會產生覆蓋的情況。

解決同時觸發非同步問題

就是將xmlHttp宣告為區域變數來處理,並在收到伺服器資料且處理完後,手動刪除。

function getData(){
  var xmlHttp="";
  createXMLHttpRequest();
  xmlHttp.onreadystatechange = function(){
    ...
    // 處理完立即刪除與釋放
    delete xmlHttp;
    xmlHttp = null;
  }
}

delete關鍵字

刪除物件的property。The delete operator deletes an object, an object's property, or an element at a specified index in an array.

JavaScript - XML DOM簡介

Asynchronous JavaScript And XML, AJAX

  1. HTML/XHTML與CSS:顯示UI與相關資料
  2. XML:伺服端非同步傳遞的資料
  3. XML DOM:當Browser非同步取得XML資料後,可進一步使用Javascript和XML DOM取出所需資訊
  4. XMLHttpRequest:Javascript透過XMLHttpRequest物件來建立非同步HTTP請求

Extensible Markup Language, XML

利用特殊的標示語言(Markup Language),建立XML文件(XML Document)。

XML Document

XML Document是由元素(Elements)組成。

標籤(Tags)

XML能自己定義標籤,如:<book>、<title>…。

元素(Elements)

元素是一個完整項目:<book code="K2010">Javascipt入門</book> 開始標籤、屬性、文字內容、結尾標籤。

屬性

屬性值需要使用引號(單、雙皆可)括起。

實體參考(Entity Reference)

XML本身有一些保留符號,如文件內需要使用這些符號,請使用實體參考。

JavaScript XML實體參考
實體參考代表符號
&lt;<
&gt;>
&amp;&
&apos;'
&quot;"

註解(Comment)

與HTML相同,例如,

<!-- 註解內容 -->

PCDATA

文字內容稱PCDATA,例如,

<book code="K2010">Javascipt入門</book>

PCDATA → Javascript入門。

CDATA

Character Data,表示XML在剖析文件時不用處理此區塊的內容,例如,

<script type="text/javascript">
<![CDATA[
    function a(){
    ...
    }
]]>
</script>
CDATA的註解
在HTML解析器(Browser)是不承認CDATA開始"<![CDATA["與結束"(]]>"標籤,也不認識HTML實體參考(Entity Reference), 可能導致會把問題顯示在Browser,並可能產生"跨網站腳本"問題,所以在HTML會建議註解CDATA。

範例一:JavaScript註解
<script type="text/javascript">
// <![CDATA[
  document.write("<");
// ]]>
</script>

範例二:CSS註解

<style type="text/css">
/*<![CDATA[*/
  body { background-image: url(marble.png) }     
/*]]>*/
</style>

Processing Instructions, PI

允許XML文件包含傳送給應用程式的指令,例如開頭宣告就是PI,PI如同註解,不屬於XML文件內容。

<?xml version="1.0" encoding="utf-8" ?>

Document Type Declarations, DTD

是XML文件的驗證機制,可以檢查XML標籤與文件架構是否正確。

<!DOCTYPE book [
<!ELEMENT book (code, title, author, price)>
<!ELEMENT code (#PCDATA)>
<!ELEMENT title (#PCDATA)>
<!ELEMENT author (zh-tw_name, zh-en_name)>
<!ELEMENT zh-tw_name (#PCDATA)>
<!ELEMENT zh-en (#PCDATA)>
<!ELEMENT price (#PCDATA)>
]>

一行一行看,不會很難。

  • <!DOCTYPE book [:book之下包含以下元素
  • <!ELEMENT book (code, title, author, price)>:元素book之下包含其他子元素
  • <!ELEMENT code (#PCDATA)>:code元素含文字內容

XHTML

Well-Format(良好格式)的HTML。什麼是Well-Format?

  1. 標籤、屬性都是小寫
  2. 一定要<html>為根元素
  3. 標籤必須有結尾標籤,空元素需以 />結束標籤
  4. 巢狀標籤不能重疉
  5. 屬性必須有值
  6. 屬性值需使用引號
  7. Script需使用CDATA

第三點,標籤必須有結尾標籤,例如,

<-- 有頭有尾 -->
<b>粗體字</b>
<-- 空元素需以 />結束標籤 -->
<input id="name" type="text" />
<br />

第五點,屬性必須有值,例如,

<-- HTML -->
<input type=radio checked>

<-- XHTML -->
<input type="radio" checked="checked" />

Document Object Model, DOM

Document Object Model,XML DOM可以將XML文件視為一棵樹狀結構的節點, XML DOM就是W3C DOM針對XML文件所提供應用程式的標準程式介面, 可以透過API來存取各節點物件,將一份結構化文件(XML Document)轉換成一棵節點(Nodes)樹, 提供節點的屬性、方法來存取節點內容,或新增、修改、刪除節點內容。例如,

<book>
  <code>K2010</code>
  <title>Javascript入門</title>
  <author>
   <zh-tw_name>金剛</zh-tw_name>
   <zh-en_name>Bruce</zh-en_name>
  </author>
</book>

經過DOM剖析器之後,如圖,

DOM樹
圖一:DOM樹


MSXML

在Javascript程式可使用IE內建的MSXML元件來建立XML DOM物件,建立物件使用ProgID字串,但MSXML物件只能在IE使用,要注意相容性問題。

KKBruce提醒:實務上已經很少人使用MSXML來撰寫程式碼,以下只是簡單介紹,大多都已經使用標準AJAX方式(XMLHttpRequest物件)來處理。

JavaScript MSXML DOM版本
版本DLL名稱ProgID
2.0msxml.dllMicrosoft.XMLDOM或MSXML.DOMDocument
2.6msxml2.dllMSXML2.DOMDocument
3.0msxml3.dllMSXML2.DOMDocument.3.0
4.0msxml4.dllMSXML2.DOMDocument.4.0
5.0msxml5.dllMSXML2.DOMDocument.5.0
6.0msxml6.dllMSXML2.DOMDocument.6.0

範例程式一(IE Only):

<table border="1">
<script type="text/javascript" >
//<![CDATA[
  // 建立XML DOM物件, 使用ProgID來建立
  var xmlDom = new ActiveXObject("MSXML2.DOMDocument.6.0");
  //async=false,可以保證XML文件能完全載入
  xmlDom.async = "false";
  // 載入XML文件
  xmlDom.load("Books.xml");
  // 建立文件元素子節點樹
  var objNode = xmlDom.documentElement.childNodes;
  // 顯示所有XML節點
  for ( i=0; i < objNode.length; i++ ) {
      document.write("<tr><td>");
      document.write(objNode.item(i).nodeName + "</td><td>");
      document.write(objNode.item(i).text + "</td></tr>");
  }
//]]>
</script>
</table>

範例程式二(IE Only):

>// 字串變數
var strXML = "<book>";
strXML = strXML + "<code>K2012</code>";
...
//載入字串變數
xmlDom.load(strXML);

先建立XMLDOM物件,然後由Books.xml當成資料來源,由XMLDOM物件讀入轉換成節點樹,再將節點讀取顯示出來。

[補充]使用MSXML2.DOMDocument.6.0與MSXML2.DOMDocument.3.0就好。

JavaScript - W3C DOM簡介

HTML DOM

DOM Core(核心)提供HTML與XML文件瀏覽、處理和維護階層架構的功能。主要有二大功能:

  • 瀏覽(Navigator):走訪各節點
  • 參考(Reference):存取節點

提供一致的走訪方式,只要建立好樹狀結構,所有能走訪到的節點就是標籤物件,文字節點都可以使用相同的nodeValue屬性更改其內容。

DOM HTML

HTML專屬的DOM API,提供現成的物件模型。需使用id/name屬性或forms[]、images[]等物件集合才能取得標籤物件, 而且每種標籤物件都擁有不同的屬性。

DOM Core可使用在HTML與XML,DOM HTML則針對HTML。

每一節點都是一個物件

  • DOM HTML提供各節點物件的屬性和方法
  • DOM Core提供屬性來走訪節點

W3C DOM只能更改文字節點的內容,其它標籤物件的屬性和方法還是來自DOM HTML。

轉換為節點樹

<table>
  <tbody>
    <tr><td>HTML</td><td>CSS</td></tr>
    <tr><td>Javascript</td><td>jQuery</td></tr>
  </tbody>
</table>

瀏覽器會將由伺服器所傳遞過來的HTML轉換為一節點樹,節點樹一定由html元素為根節點,html底下一定有兩個子點節,head元素子節點與body元素子節點。我們可以把這一層一層的關係畫成圖,此圖又稱節點(樹)圖。

節點(樹)圖

以上面table結構為例,

節點(樹)
圖一:節點(樹)

節點(樹狀)結構:


  • 下一層的子節點 → Child Node → tbody是table的Child Node
  • 上一層的父節點 → Parent Node → table是tbody的Parent Node
  • 左右節點(兄弟) → Sibling Node → tr與tr或tr之下td與td,各為Sibling Node
  • 最下層葉節點 → Leaf Node → td為Leaf Node
  • 文字節點(HTML的內容) → Text Node → HTML/CSS/Javascript/jQuery

在轉換一般table時需注意,要先將table轉換成HTML 4.0版的表格,即擁有<tbody>標籤,這樣在訪問時,才不會出錯。

另注意<font>,IE在轉換時,會在<font>後多加一個文字節點#text,目前HTML標準已不建議使用<font>標籤,請改用CSS。

在DOM中有3種節點

  1. 元素節點:element node,各種標籤便是這些元素節點的名稱,元素節點可以包含其他元素,唯一沒有被包含只有根元素<html>
  2. 屬性節點:attribute node,屬性節點總是被包含在元素節點之中,例<a href="http://blog.kkbruce.net">KKBruce Blog</a>,a是元素節點名稱,href是屬性節點名稱
  3. 文字節點:text node,標籤裡具體的文字內容,網頁最終目的是向使用者展示內容

W3C DOM屬性

JavaScript W3C DOM唯讀屬性
名稱說明類型/傳回類型
firstChild傳回第一個子節點childNodes物件集合,包含此節點下所有的子節點Node
lastChild傳回最後一個子節點的childNodes物件集合,包含此節點下所有的子節點Node
parentNode傳回父節點的物件,如已是根節點,傳回nullNode
nextSibling傳回下一個兄弟節點物件,如已是最後一個節點,傳回nullNode
previousSibling傳回上一個兄弟節點物件,如已是第一個節點,傳回nullNode
nodeName傳回節點的HTML標籤(英文大寫)名稱String
nodeType傳回節點種類,1是標籤(element node)、2是屬性(attribute node)、3為文字(text node)Number
specified布林值,傳回在HTML標籤是否有設定指定的屬性值Boolean

W3C DOM讀寫屬性

JavaScript W3C DOM讀寫屬性
名稱說明類型/傳回類型
nodeValue存取文字節點(text node),其他種類的節點傳回nullString

W3C DOM物件集合

W3C DOM物件集合可以取得下一層子節點和節點的屬性。

JavaScript W3C DOM物件集合
名稱說明類型/傳回類型
attributes節點屬性的物件集合,可以直接使用名稱來存取,僅用於元素節點NameNodeMap
childNodes所有子節點的物件集合(Array),方法item(i)可訪問第i+1個節點NodeList

在IE中可以直接使用id/name屬性來取得指定的節點物件,但用此方法實作的程式碼相容性不佳, 建議先使用getElementById("id")來取得指定的節點物件,然後再實作以確保Browser相容性。例如,

// 相容性不好
myid.childNodes[0].firstChild.nodeValue = "ASP.NET MVC";

// 相容性好
var objValue = document.getElementById("myid");
objValue.childNodes[0].firstChild.nodeValue = "ASP.NET MVC";

IE與其他Browser中childNodes走訪不同問題

我們在使用childNodes走訪節點時,IE會依順序走訪各元素節點,但其他瀏覽器不光是元素節點,連它們之間的空格也被當成子節點計算。例如,

<ul id="comicList">
 <li>航海王</li>
 <li id="Naruto">火影忍者</li>
 <li>海棉寶寶</li>
<ul>

可以使用以下JavaScript在IE與其他瀏覽器測試,

var eleUL=document.getElementById("comicList");
var listName="";
if (ele.hasChildNodes()) {
 var eleLI = eleUL.childNodes;
 for (var i=0,len=eleLI.length;i<len;i++){
  listName += eleLI[i].nodeName + "\n";
 }
 alert(listName);
}

在IE的測試結果:

  1. LI
  2. LI
  3. LI

在Firefox測試結果:

  1. #text
  2. LI
  3. #text
  4. LI
  5. #text
  6. LI

解決瀏覽器走訪不同問題

function nextSib(node){
  var tempLast = node.parentNode.lastChild; //取得node的最後一個節點
  if (node ==tempLast) { //是否為最後一個節點
    return null;
  }
  var tempObj = node.nextSibling; //非最後一個,可找下一個Node
  while ( tempObj.nodeType!=1 && tempObj.nextSibling!=null) //nodeType不是元素節點且不是最後一個,即找到元素節點為止
    tempObj = tempObj.nextSibling; //往下找下一個
  return (tempObj.nodeType==1) ? tempObj:null; //如果是元素節點,傳回節點本身,否則傳回null
}

function prevSib(node){
  var tempFirst = node.parentNode.firstChild; //取得node的第一個節點
  if (node ==tempFirst) { //是否為第一個節點
    return null;
  }
  var tempObj = node.previousSibling; //非第一個,可往上找上一個Node
  while ( tempObj.nodeType!=1 && tempObj.previousSibling!=null) //nodeType不是元素節點且不是第一個,即找到元素節點為止
    tempObj = tempObj.previousSibling; //往上找上一個
  return (tempObj.nodeType==1) ? tempObj:null; //如果是元素節點,傳回節點本身,否則傳回null
}

我們可以透過這兩個函式來走訪,而不用擔心相容性問題。

var eleLI = document.getElementById("Naruto");
var nextItem = nextSib(eleLI);
var preItem = prevSib(eleLI);
if (nextItem != null) //傳回不是null,是元素節點
...

W3C DOM方法

W3C DOM提供方法能建立、刪除、複製、交換、取代節點。

流程 -
名稱範例說明
appendChildobj(父).appendChild(obj(子))在obj(父)節點新增子節點obj(子),傳回新增的節點物件
cloneNodeobjNew = objDup.cloneNode(deep)複製objDup節點的新節點物件,deep為false僅複製此節點,true連子節點的整個節點樹都複製
createElementobjNew = document.createElement("tagName")建立一個HTML節點物件
createTextNodeobjNew = document.createTextNode(string)建立文字節點
hasChildNodesobjNode.hasChildNodes()檢查節點是否擁有子節點,有為true
insertBeforeobj(父).insertBefore(obj(子),objBrother)在obj(父)節點前插入一個子節點obj(子),位置在objBrother節點前
removeChildobj.parentNode.removeChild(targetNode)先找到要刪除的節點,透過parentNode的removeChild()方法來刪除目標節點
replaceChildobj.parentNode.replaceChild(newNode,oldNode)先找到要替換的節點,透過parentNode的replaceChild()方法來替換節點
getAttributeobj.getAttribute("attr")讀取obj的attr屬性值,例如,obj.getAttribute("title");
setAttributeobj.setAttribute("attr","value")設定obj的attr屬性的值為value,例obj.setAttribute("src","bruce.png");

insertAfter()函式

DOM只有提供insertBefore()在目標元素之前插入新元素,或是appendChild()在父元素的childNodes尾新增新元素, 如果需要在特用元素後插入新元素,需自行撰寫。

function insertAfter(newElement, targetElement){
  // 取得目標元素的上層節點
  var tParent = targetElement.parentNode;
  // 檢查是否有子元素
  if (tParent.lastChild == targetElement)
    // 附加至子節點後
    tParent.appendChild(newElement);
  else
    // 新增至節點之前
    tParent.insertBefore(newElement, targetElement.nextSibling);
}

新增HTML Tag步驟

  1. 建立節點
  2. 進行處理

建立節點

// 新增HTML節點
var objNew = document.createElement("P"); 
// 新增文字節點
var objText = document.createTextNode("新增說明文字");

進行處理

// 將objNew節點加入objNode節點最後面
objNode.appendChild(objNew);
// 將objText文字節點加入objNew節點之後
objNew.appendChild(objText);

例如,在一個空的<table>可以一層一層加上<tr>、<td>來自由動態產生你所想要的<table>。

改變節點文字的安全步驟

  1. 刪除所有子節點:removeChild()
  2. 根據新內容,建立新內容節點:createElement(),createTextNode()
  3. 將新內容節點,附加在目標節點之下:appendChild()

function replaceNodeText(id, newText){
  var node=document.getElementById(id);
  while (node.firstChild)
    node.removeChild(node.firstChild);
  node.appendChild(document.createTextNode(newText));
}

DOM與CSS樣式

DOM提供了透過節點取用樣式的途徑。

className屬性:提供對節點樣式類別的存取。

alert(ele.className);
ele.className = "highlight";

style屬性:提供對單一樣式的存取。

ele.style.visibility = "hidden";
ele.style.display = "none";

JavaScript Anchor Element

Anchor,連結,也就是HTML裡最重點的一個特性,讓我們可以在不同網頁之間瀏覽。

<a href="protocal://domain/file#bookmark" target="frame_name">context</a> 

href屬性

設定超連結的目的地。

  • protocal:通訊協定,例如,http/https/ftp
  • domain:網域名稱或IP
  • file:檔案名稱
  • #bookmark:指定HTML文件中的降落點

讓我們用範例來說明降落點:

<a id="info">TOP</p>

在網頁設定一個名稱top的降落點,然後,

<a href="news.htm#info">News</a>

我可以指定要到那一個網頁的那一降落點。如果#info是在網頁的中間,你就能發現,我們一點連結過去時,網頁會自動移至#info的位置,這也就是降落點的意思。

連結其他屬性

JavaScript 連結屬性
名稱說明
name/id名稱
type存取連結資源的Content type
accessKey快速鍵
target設定超連結目的地HTML文件的frame名稱
charset連結資源的編碼方式
hreflang連結資源的語系
tabIndexTab鍵切換的順序值

target屬性設定值

JavaScript 連結target屬性設定值
名稱說明
_blank開啟新視窗來顯示HTML文件
_self在原視窗來顯示HTML文件
_top在原視窗顯示HTML文件
_parent如為frameset,且擁有父frame,就在父frame顯示HTML文件

_top如為frameset,取消frameset以全視窗顯示HTML文件。

在Windows Server 2008 R2 x64上安裝WAMP服務

我在一台全新乾淨的Windows Server 2008 R2 x64 with Service Pace 1的伺服器上,要架設WAMP服務,一直碰壁,以下心得整理。

WAMP現在區分為x32與x64,如上所述,如果是新伺服器,必須先安裝對應版本的 Visual C++ 2008 可轉散發套件。

wamp x32 需安裝 [Microsoft Visual C++ 2008 可轉散發套件 (x86)]
wamp x64 需安裝 [Microsoft Visual C++ 2008 可轉散發套件 (x64)]

不安裝之後,當你要開啟 wampmanager 管理程式時,會一直出現錯誤,而無法啟用。

wampmanager.exe錯誤訊息:

記錄檔名稱:         Application
來源:            Application Error
日期:            2012/2/20 下午 03:07:10
事件識別碼:         1000
工作類別:          (100)
等級:            錯誤
關鍵字:           傳統
使用者:           不適用
電腦:            WIN08R2SP1
描述:
失敗的應用程式名稱: wampmanager.exe,版本: 1.6.1.33,時間戳記: 0x2a425e19
失敗的模組名稱: KERNELBASE.dll,版本: 6.1.7601.17651,時間戳記: 0x4e211319
例外狀況碼: 0x0eedfade
錯誤位移: 0x0000b9bc
失敗的處理程序識別碼: 0xbb0
失敗的應用程式開始時間: 0x01ccef9e43559d41
失敗的應用程式路徑: C:\wamp\wampmanager.exe
失敗的模組路徑: C:\Windows\syswow64\KERNELBASE.dll
報告識別碼: 811a009f-5b91-11e1-8bd8-08002701cd4a
事件 Xml:
<Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
  <System>
    <Provider Name="Application Error" />
    <EventID Qualifiers="0">1000</EventID>
    <Level>2</Level>
    <Task>100</Task>
    <Keywords>0x80000000000000</Keywords>
    <TimeCreated SystemTime="2012-02-20T07:07:10.000000000Z" />
    <EventRecordID>672</EventRecordID>
    <Channel>Application</Channel>
    <Computer>WIN08R2SP1</Computer>
    <Security />
  </System>
  <EventData>
    <Data>wampmanager.exe</Data>
    <Data>1.6.1.33</Data>
    <Data>2a425e19</Data>
    <Data>KERNELBASE.dll</Data>
    <Data>6.1.7601.17651</Data>
    <Data>4e211319</Data>
    <Data>0eedfade</Data>
    <Data>0000b9bc</Data>
    <Data>bb0</Data>
    <Data>01ccef9e43559d41</Data>
    <Data>C:\wamp\wampmanager.exe</Data>
    <Data>C:\Windows\syswow64\KERNELBASE.dll</Data>
    <Data>811a009f-5b91-11e1-8bd8-08002701cd4a</Data>
  </EventData>
</Event>

錯誤訊息二:

記錄檔名稱:         Application
來源:            SideBySide
日期:            2012/2/20 下午 03:07:10
事件識別碼:         33
工作類別:          無
等級:            錯誤
關鍵字:           傳統
使用者:           不適用
電腦:            WIN08R2SP1
描述:
"c:\wamp\bin\php\php5.3.9\php-win.exe" 的啟用內容產生失敗。 找不到依存組合 Microsoft.VC90.CRT,processorArchitecture="amd64",publicKeyToken="1fc8b3b9a1e18e3b",type="win32",version="9.0.21022.8"。 請使用 sxstrace.exe 進行詳細的診斷。
事件 Xml:
<Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
  <System>
    <Provider Name="SideBySide" />
    <EventID Qualifiers="49409">33</EventID>
    <Level>2</Level>
    <Task>0</Task>
    <Keywords>0x80000000000000</Keywords>
    <TimeCreated SystemTime="2012-02-20T07:07:10.000000000Z" />
    <EventRecordID>671</EventRecordID>
    <Channel>Application</Channel>
    <Computer>WIN08R2SP1</Computer>
    <Security />
  </System>
  <EventData>
    <Data>Microsoft.VC90.CRT,processorArchitecture="amd64",publicKeyToken="1fc8b3b9a1e18e3b",type="win32",version="9.0.21022.8"</Data>

    <Data>c:\wamp\bin\php\php5.3.9\php-win.exe</Data>

WampServer管理程式啟動後,你還會發現就算修改 httpd.conf 裡的 Port 設定,還是無法啟用 Apache!因為在Windows Server 2008 R2 x64上我有掛IIS 7 (使用 80 Port),奇怪的事,就算把IIS 7停用,把80 Port給Apache還是無法啟用。

我知道從Vista / Windows Server 2008之後版本開始,對於 C: 有一些權限上的管理與以前不同,我沒有細查文件,我的決解辦法是移除原安裝,重新安裝至非 C: 的路徑,例如,d:\wamp 立即見效。

如果要把PHP掛到IIS上執行,有二種選擇
  1. FastCGI
  2. ISAPI 
我選擇使用後者,在IIS裡設定相當簡單(注意,你在 IIS 的【角色服務』中,必有啟用【ISAPI 擴充功能】與【ISAPI 篩選器】),在【所在網站】或【虛擬目錄】裡有二個地方要設定,一在【處理常式對應】,二在【預設文件】。

處理常式對應 → 新增指令碼對應 →

  1. 要求路徑:*.php
  2. 執行檔:D:\wamp\bin\php\php5.3.9\php5isapi.dll (路徑自行參考,找 *isapi.dll 就對了)
  3. 名稱:PHP (隨便,方便辨識即可)

預設文件 → 新增 → 【 index.php 】 (一般PHP預設首頁文件檔案)

注意x32與x64混合環境

在上面最後一小段,你將PHP使用ISAPI方式掛到IIS上執行,不知道有沒有像我,碰到一堆的Error!後來驗證後證明是我自己搞不清楚狀況。@_@

我們這台x64伺服器上有一個網站是執行x32(ASP)應用程式,當我要把WAMP x64版本php5isapi.dll掛到IIS執行(ASP網站),不管怎麼試就是無法啟動index.php,會一直出現Error,試了好久,結果是:

  • 【WAMP x64 + Windows Server x64 + IIS (應用程式集區 - 啟用32位元應用程式)】(Bad)
  • 【WAMP x32 + Windows Server x64 + IIS (應用程式集區 - 啟用32位元應用程式)】(Bad)
  • 【WAMP x32 + Windows Server x64 + IIS】(Bad)
  • 【WAMP x64 + Windows Server x64 + IIS】(Good)

也就是說,在Windows Server 2008 R2 x64環境下,想要用ISAPI方法使用PHP只有一種方式,完全x64。【WAMP x64 + Windows Server x64 + IIS】就只有這樣的組合才有辦法讓ISAPI可以正常運作PHP。目前在【Windows Server x64 + IIS (應用程式集區 - 啟用32位元應用程式)】是無法透過ISAPI方式讓PHP正常運作。

JavaScript frame - iframe Element

Frame簡介

框架頁(Frameset)目前使用的越來越少,多會建議使用CSS來實作畫面切割排列。不過<iframe>在影音網站還是很常見。
<frameset cols="value1,value2" rows="value3,value4">
    <frame name="1" src="1.html" scrolling="yes|no" />
    <frame name="2" src="2.html" scrolling="yes|no" />
    <frame name="3" src="3.html" scrolling="yes|no" />
    <frame name="4" src="4.html" scrolling="yes|no" />
</frameset> 

Frame屬性

  1. cols屬性:定義左右分割frame尺寸
  2. rows屬性:定義上下分割frame尺寸

以前面範例為例,先切左右,畫面從中間左右分割為兩塊(cols兩個value),再切上下,再從中間橫切上下兩塊(rows兩個value),所以畫面會被切成四塊,分別載入不同HTML文件。

frameset運作

name="1"的frame,載入1.html;name="2"的frame,載入2.html,以此類推,由左而右,由上而下。

frame屬性

JavaScript frame屬性
名稱說明
src要顯示的HTML文件
nameframe名稱
scrolling是否有捲動軸,yes有

frames集合架構

Frames集合架構
圖一:Frames集合架構

frames屬性

JavaScript frames集合屬性
名稱說明
self傳回目前的window物件,相當於window屬性
parent傳回目前frame的上一層物件(父物件)
top如果是巢狀frame,top就是最上層的window物件,也因為巢狀frame,top不一定等於prent屬性

// 由後往前看,找frames[0]的上一層物件
parent.frames[0]; 
// 使用name來取得window物件,然後就可以進一步設定window物件屬性
parent.1.localtion.href = "1.html";

巢狀frame

  1. 第一層frameset的frame可以擁有第二層的frameset
  2. window和self屬性是指第二層frameset中目前<frame>
  3. parent屬性是指第二層<frameset>,下一層是self屬性的frame
  4. top屬性是最上層第一層<frameset>,下一層是parent屬性的第二層frameset

Frameset屬性

  • cols屬性:定義左右分割frame尺寸
  • rows屬性:定義上下分割frame尺寸

Frame屬性

JavaScript Frame屬性
名稱說明
name/idframe名稱
frameBorder是否顯示frame邊框
src載入的HTML文件
scrolling是否擁有捲動軸
noResize是否可以調整frame的尺寸
marginHeight存取frame邊界的高
marginWidth存取frame邊界的寬

iframe介紹

<iframe name="" src="" height="" width="" align=""></iframe>

JavaScript iframe屬性
名稱說明
name/id名稱
src顯示圖檔、HTML文件、URL網址
height
width
alignIframe與網頁其他文字對齊方式

// 將Video.htm載入到id="if1"的iframe
document.getElementById("if1").src="Video.htm"; 

JavaScript Image Element

所有圖片<img>依照出現順序都屬於images物件集合 。例如,

// 設定網頁裡第一張圖片的alt屬性
document.images[0].alt="wolf"; 

image元素屬性

JavaScript image元素屬性
名稱說明
name存取圖片名稱
src存取圖片檔案的URL
width存取圖片寬度
height存取圖片的高度
hspace/vspace存取圖片的位置
isMap設定圖片為伺服端的影像地圖
useMap設定圖片為客戶端的影像地圖
alt存取無法顯示圖片時的替代文字
complete檢查圖片是否已經完全載入,true為已載入

image元素事件

JavaScript image元素事件
名稱說明
onload當圖片完全載入時,觸發此事件
onabort當使用者中止載入時,觸發此事件
onerror當找不到圖片檔案或圖檔損壞時,觸發此事件

放大圖片時

如果<img>是含在<div>之內,當我們放大圖片時,能在<div>內使用CSS的兩個參數,讓<div>幫我們出現捲動軸。

<div id="imgFrame" style="overflow-x:scroll;overflow-y:scroll;height:350px;width:600px;">
<img src="kkbruce.png" alt="KKBruce" />
</div>

JavaScript Table Element

table物件

table物件就是<table>,不過並沒有提供集合物件存取指定的<table>,使用使用id或name屬性來存取,例如,

document.mytable.border = 3; 

summary屬性


<table summary="This is KKBruce created Table"> 

此table的說明,它對於Browser而言是不會顯示,但對於網路蜘蛛則十分重要,是加分的屬性。

scope屬性

<th scope="row | col"> 

th標題是要放在「行(row)」或「列(col)」。scope預設是row,指定th標題出現在表格上方第一行或左邊第一列。

Table元素屬性


JavaScript Table元素屬性
名稱說明
caption存取表格的標題文字(如果存在)
border存取表格框線尺寸
width存取表格的寬度
cellPadding存取儲存格文字內容和框線的距離
cellSpacing存取儲存格框線間的距離
frame存取表格外框線
rules存取表格內框線
rows傳回表格每一最TableRow物件的物件集合(Array)
tBodies傳回<tbody>物件集合
tFoot傳回<tfoot>物件集合
tHead傳回<thead>物件集合

Table元素方法

在table中,每一儲存格都有一組index編號,index編號是由TableRow物件的rowIndex屬性和TableCell物件的cellIndex屬性所組成,(rowIndex, cellIndex) 所以一個二維表格裡每一儲存格Index編號是:
(0,0)(0,1)
(1,0)(1,1)
(2,0)(2,1)


JavaScript Table元素方法
名稱說明
insertRow(index)插入一列<tr>,插入位置是在參數的列號之前
deleteRow(index)刪除index的表格列
createCaption()建立標題文字<caption>
deleteCaption()刪除標題文字
createTHead()建立標題區塊<thead>
deleteTHead()刪除標題區塊
createTFoot()建立註腳區塊<tfoot>
deleteTFoot()刪除標題區塊

document.mytable.insertRow(0).insertCell(0);
document.mytable.insertRow(0).insertCell(1); 

刪除table某一列

function deleteColumn(table, number) {
  //每行刪除對應儲存格
  for (var i=0, len=table.rows.length; i < len; i++){
    table.rows[i].deleteCell(number);
  }
} 

Table元素屬性與方法

另一種分類法:

針對table元素:
JavaScript table元素屬性與方法
名稱說明
caption指向<caption>元素(如果存在)
tBodies指向<tbody>元素的集合
tFoot指向<tfoot>元素(如果存在)
tHead指向<thead>元素(如果存在)
rows表格中所有「行」的集合
deleteRow(position)刪除指定位置上的行
insertRow(position)在rows集合中的指定位置插入一個新行
creatCaption()建立<caption>元素並放入表格中
deleteCaption()刪除<caption>元素

針對tbody元素:
avaScript tbody元素屬性與方法
名稱說明
rows<tbody>中所有行的集合
deleteRows(position)刪除指定位置上的行
insertRows(position)在rows集合中的指定位置插入一個新行

針對tr元素:
JavaScript tr元素屬性與方法
名稱說明
cells<tr>中所有儲存格的集合
deleteCell(position)刪除指定位置上的儲存格
insertCell(position)在cells集合的指定位置上插入一個新的儲存格
<tr>
  <td>儲存格1</td>
  <td>儲存格2</td>
</tr> 

<tr>...</tr>對應TableRow物件。例如,TableRow → rows[i]。<td>...</td>對應TableCell物件,例如,TableCell → cells[i]。

// rows[i],第幾列
// cells[i],第幾格 
mytable.rows[i].cells[i];

TableRow(<tr>)

JavaScript TableRow屬性
名稱說明
cells傳回表格列中儲存格TableCell物件集合(Array)
align列中儲存格的水平對齊方式
vAlign列中儲存格的垂直對齊方式
rowIndex傳回TableRow物件的列號(Array)
sectionRowIndex傳回<thead>、<tfoot>、<tbody>區塊中TableRow物件列號

JavaScript TableRow方法
名稱說明
insertCell(index)插入儲存格在傳入index編號前
deleteCell(index)刪除傳入index編號儲存格

TableCell(<td>)

JavaScript TableCell屬性
名稱說明
align儲存格的水平對齊方式
vAlign儲存格的垂直對齊方式
colSpan存取儲存格的左右合併,colspan屬性
rowSpan存取儲存格的上下合併,rowspan屬性
cellIndex傳回儲存格編號

JavaScript - 表單元素 - Regular Expression

Regular Expression


Regular Expression(正規表達式) - 基本字元
名稱說明
\n換行符號
\rEnter
\tTab
\xHex16進位ASCII
\xOct8進位ASCII
\符號轉義,讓符號只是符號,無RE作用

因為符號在RE裡有其他作用,\.代表.、\?代表?,符號有:.、?、/、\、[、]{、}、(、)、+、*、|。

Regular Expression(正規表達式) - 字元集"["與"]"符號"
名稱說明
[abc]代表英文a、b、c
[a-z]代表26個英文小寫
[A-Z]代表26個英文大寫
[0-9]代表數字0到9
[a-zA-Z]代表所有大小寫英文
[^abc]除了a、b、c以外任何字元
-英文或數字的一個範圍
[^]在[]裡的^為排除的意思

[ ]代表一個區段、段落。

Regular Expression(正規表達式) - 常用範圍
名稱說明
\w[0-9a-zA-Z_],任何英數字和底線
\W[^0-9a-zA-Z_],也就是^\w的意思
\d[0-9],任何數字
\D[^0-9],也就是^\w的意思
\s比對空格,空白字元(space)、tab、換行字元、return/enter
..(點)比較任何字元,換行字元(newline)除外

以上都是常用的Escape字串。

Regular Expression語法


var reName = /pattern/[g|i|gi]; 

使用變數來儲存pattern,遇/.../字串自動建立RegExp物件。把pattern被在"//"符號之間,就是你所要過濾的規則,例如/[0-9]/。

[g|i|gi]選擇性參數,尋找方式

  • g:比對字串中所有符合pattern字串的子字串, 如果沒有設定,將只找第一個符合pattern的字串
  • i:不區分pattern字串的英文大小寫

尋找方式範例

// jscript(j小寫)和Jscript(J大寫)都符合
var reJS1 = /[jJ]script/;
// 2*2會出現四種符合的組合
// javascript、javaScript、Javascript、JavaScript
var reJS2 = /[jJ]ava[sS]cript/; 

^開頭$結尾


// 找出開頭是This的字串
// 注意,此^不是放在[^],位置不同作用不同
var reStar = /^This/;
// 找出結尾是bye.的字串 
var reEnd = /bye.$/

Regular Expression(正規表達式) - ^開頭$結尾
名稱說明
^比對字串開頭
$比較字串結尾

^開頭$結尾範例


// 需要指定「出現次數」,例如身分證
// A123456789,第一碼為英文,第二碼為男女,3-9碼為數字
var reID = /^[a-zA-Z]{1}[1-2]{1}[0-9]{8}/; 

量詞


Regular Expression(正規表達式) - 量詞
名稱說明(次數)
?0或1次
*0或多次
+1或多次
{n}出現n次
{n,}至少n次
{n,m}n到m次
( )集合字元或/和中介字元,成為子樣式

最後一個(),我們舉個例子,「/(Hot)? ?Donuts/」過濾條件,可比對出Donuts或Hot Donuts。而「/(red|blue) pill/] 過濾條件,可比對出red pill或blue pill。

定義好樣式(pattern)後,可使用test()方法來測試。

// 定義過濾條件
var regex = /^\d{5}/$;
// 使用test()測試是否符適合條件
if (!regex.test(inputField.Value))
  ... 

RegExp物件


var objRE = new RegExp("pattern字串", "g|i|gi"); 

第一個pattern字串(不需放在//符號之間),第二個為尋找方式的參數。使用RegExp物件的test()方法進行比對,符合回傳true。

objRE.test(strValue); 

如果在JavaScript字串變數需要尋找是否擁有符合Regular Expression的pattern子字串,此時請使用String物件的match()方法,例如,

String.match(objRE); 

字串(String)的match()方法範例,

var str = "This is book.";
var objRE = new RegExp("is", "g");
var result = str.match(objRE); 

我們將比對結果儲存起來,然後,

// 沒有會傳回null,有會傳回Array
// is, is --> 陣列
document.write(result);
// 傳回符合數量
// 2
document.write(result.length); 

JavaScript - 表單元素 - textarea Element

textarea

testarea物件就是<textarea>,其中accessKey、defaultValue、disabled、form、name、readOnly、tabIndex、type等屬性和input物件相同。

form表單之textarea元素屬性
名稱說明
cols每一行可以有多少字元
rows多少行(列)

限制使用者輸入字元數

由於<textarea>沒有<input>的maxlength,所以我們自訂一個屬性來使用,例如,

<textarea name="comments" id="comments" cols="40" rows="4" maxlength="100" onkeypress="return lessThan(this);"></textarea> 

function lessThan(objTA){
  //比較輸入字元數與自訂屬性maxlength的比較結果
  return objTA.value.length < objTA.getAttribute("maxlength");
} 

JavaScript - 表單元素 - select and option Element

select元素與option元素

select物件就是<select>,其中disabled、form、name、tabIndex、type、value等屬性和input物件相同。

form表單之select元素屬性
名稱說明
length取得共有多少<option>
multiple設定或取得選擇是否複選,true為多選
options取得option物件集合,這是一個Array
selectedIndex傳回選擇的選項index值,這是options的Index值
size設定欄位為下接式或清單,size大於1是清單方塊
text選項名稱
typeselect類型,單選傳回select-one,複選select-multiple

  • options 陣列的index由0開始。例如,SB.options[2]表示<select>中的第3項。
  • selectedIndex,例如,SB.options[selectedIndex].value;

form表單之option元素屬性
名稱說明
text存取選擇文字
defaultSelectedoption預設選項,即selected屬性
indexoption陣列index位置,以0開始
label設定或取得選項說明文字
selected是否被選取,true為選取

取得<select>選取值


function getSelectValue(selectName){
  var objForm = document.forms["form1"];
  //取得對應的select
  var SB = document.elements[selectName]; 
  if (SB.type = "select-one") { 
    //單選
    var Choice = SB.selectedIndex;
    alert("Your Choice" + SB.options[Choice].text);
  }
  else {
    //複選
    var Choice = new Array;
    for (var i=0,len=SB.options.length; i<len;i++){
      if (SB.options[i].selected)
        Choice.push(SB.options[i].text);
    }
    alert("Your Choice" + Choice.join());
  }
} 

新增、替代、刪除<option>

使用Option()建構式直接新增,例如,

var addOption = new Option(text, value, defaultSelected, selected); 

defaultSelected與selected預設為0(false),可不設定。通常會將<select>清單的length直接設定為新選項,即最後一項。例如,

SB.options[SB.options.length] = addOption; 

將以上程式為一支副程式,

function addOption(selectName, optionIndex) {
  var objForm = document.forms["form1"];
  var SB = objForm.elements[selectName];
  var addOP = new Option("金剛", "King Kong");
  //處理IE相容性
  //先新增,再移動到指定Index
  SB.options[SB.options.length] = addOP;
  SB.insertBefore(addOP, SB.options[optionIndex]);
} 

替代,取代某一置位(index)的option,例如,

SB.options[selectIndex] = addOption; 

刪除,刪除某一置位(index)的option,例如,

// 設定某一index為null即刪除
SB.options[selectIndex] = null; 

將以上程式整理成一支副程式,

function removeOption(selectName, optionIndex){
  var objForm = document.forms["form1"];
  var SB = objForm.elements[selectName];
  SB.options[optionIndex] = null;
} 

JavaScript - 表單元素 - checkbox and radio Element

checkbox與radio


JavaScript form表單checkbox與radio元素屬性
名稱說明
checked設定或取得是否選取或核取,選取或核取為true
defaultCheckedtrue預設選取或核取,checkbox或radio裡的checked屬性

checkbox與radio被選取的值

if (oRadio[i].checked)
  alert(oRadio[i].value); 

設定選取,例如,

document.forms["form1"].oRadio[i].checked = true; 

全選、全不選、反選

function changeBoxes(action) {
  var objForm = document.forms["form1"];
  var CB = objForm.hobby;
  //取得checkbox屬性name=hobby的集合
  for (var i=0,item; item = CB[i];i++){
    if (action < 0) 
      item.checked = !item.checked;
      // action傳入-1為反選
      // 利用!來相反所有選擇
    else
      item.checked = action;
     // action傳入1(true)為全選
     // action傳入0(false)為全不選 
  }
} 

JavaScript - 表單元素 - Input Element

form表單input元素


JavaScript form表單input元素屬性
名稱說明
form取得input屬於那一個form
namename屬性
type類型
value設定或取得input的值,除下拉式功能表以外
accessKey此input的快捷鍵
accept顯示伺服器支援的ContentType清單
defaultValue設定或取得input的預設值
disabled設定input是否有作用,true為沒作用
tabIndex設定Tab鍵切換順序

JavaScript form表單input元素文字
名稱說明
maxLength設定或取得input最大字元數
readOnly設定或取得input是否是一個唯讀input,true為是
size設定或取得input的尺寸

disabled與readOnly


  • disable會使欄位無效,內容值無法傳送或讀取
  • readOnly會使欄位唯讀,但內容可傳送或讀取

JavaScript form表單input元素方法
名稱說明
blur()設定input失去處理權
focus()設定input擁有處理權
select()選取input,使用反白顯示文字內容,只可以使用在文字欄位
click()觸發onclick事件,button、checkbox、radio、reset、submit

如果使用input方法選擇文字欄位,需同時使用select()與focus(),欄位內容才會反白與取得處理權,例如,

obj.select();
obj.focus(); 

在button中使用submit()


<input type="button" value="Submit" onclick="document.forms["form1"].submit();" /> 

如果使用submit()方法來提交表單,不會觸發<form>的onsubmit()事件,這點與<input type="submit />或<input type="image" />提交按鈕不同。

防止使用者重覆按下提交按鈕


<input type="button" value="Submit" onclick="this.disabled=true;this.form.submit();" /> 

滑鼠經過時自動選擇文字


  • 在滑鼠經過時onmouseover="this.focus()";
  • 在取得處理權後,選取文字,onfocus="this.select()";

JavaScript - 表單元素 - Form Element

取得<form>裡元素

<form name="form1">
  <input type="text" id="username" /><br />
  <input type="password" id="password" /><br />
  <input type="button" id="send" />
</form> 

每一對<form>...</form>都解析為一個form物件,可以透過document.forms集合來引用這些物件,例如一個name屬性為"form1":document.forms["form1"]來取得,或使用表單在HTML中的索引來引用:document.forms[1],引用第二個表單物件。可以利用元素在集合中的位置或元素的name屬性來取得元素的引用。

以欄位名稱(name屬性)存取表單欄位

圖一:

// 取得表單名稱為form1的表單集合
var oform = document.forms["form1"]; 

// 取elements集合中name屬性為username的值
var getName = oform.elements.username.value; 

// 以元素名稱(username)來取值
var getName = oform.elements["username"].value;
// 表單和欄位都沒有指定name或id屬性,
// 可使用forms與elements的index位置存取欄位
var getName = oform.elements[0].value;
圖二 …

document.forms[0].elements[0].value; 

  • forms:所有表單物件是都<form>的物件集合
  • elements:表單內所有欄位物件的集合

forms與elements為一個Array Object,依tag在HTML文件出現的順序來編號,index由0開始。此方法,要注意tag順序的變動會影響JavaScript程式。

form

JavaScript form屬性
名稱說明
action存取form處理程式路徑
elementsArray,表單中所有表單欄位的集合
enctype表單向伺服器發送資料時,資料應該使用的編碼方法,預設為「application/x-www-form-urlencoded」,如果要上傳檔案,則「multipart/form-data」
length表單欄位的數量
method存取form資料傳送方法,有post與get
name表單的名稱,即<form>中的name屬性
targetform表單作用的框架

JavaScript form方法
名稱說明
reset()重設form欄位回預設值
submit()送出form欄位到伺服器

Taiwan Taiwan, I Love You

hear me

我不知道各位有沒有發現,我的Blog簡介、個人簡介…裡一定有台灣,I come from Taiwan,來自台灣,以下是個今天且關於台灣的故事。

在2012年1月8日,我從 about.me 這個服務收到一封信,這是一封小怪英文信,一個非常簡單的自我介紹,說他寫了一套軟體,他查詢到我為一些軟體進行過英譯中,想要請我翻譯此軟體為正體中文或簡體中文。(小怪英文信:這位自由軟體開發者( I'm a Freeware developer)是位波蘭人,寫出來的英文有那麼點小怪。)

這讓我考慮很久,原因在於,我翻譯那些軟體是覺得好用,且內容又不會太多太難。copy譯為複製,paste譯為貼上,我相信只要有一點英文底子的人,這誰都會,差異在願不願意出那份工,出那份力。在我有多的時間與體力之下,我就邊玩邊譯,當成複習英文單字。另一個重點,那時的我還沒生小孩,晚上有的是時間與體力。

我考慮了整整一週,突然想到,我自己抱怨過一件事,為什麼現在的外國軟體廠商,只要有出中文版,都一定先出簡體中文版,不然就「只有」簡體中文版。如果是用uni-code來complier,那還好,最少軟體開起來還比較不會有亂碼的問題。我曾經用過一套外國軟體,非常好用,但就是介面只有簡體中文可以選,因為介面上的英文不難,我還自己很不要臉的寫信去說,我可以幫忙翻譯成正體中文,結論是,人家理都不理,連封回信也沒有。那為什麼現在有機會可以幫忙,而你確不幫呢?

很快的我收到了語言檔,我想他是把波蘭文直譯為英文,本想語言檔翻譯好就寄回去了事,結果發現不行,因為有些內容根本與我們在使用的技術英文差異很大,只有打開軟體網頁與軟體本身邊玩邊翻譯。裡面好玩的事,他的英文軟體網頁是由Google線上翻譯的功能,把波蘭文翻譯為英文,老實說,有看沒有懂。

最後花了一個多月時間,終於把正體中文語言檔給寄出去給了他。先說明,我不是英文翻譯高手,也不是專業作家,譯的如何,我是感覺普普,因為很多專業術語,翻譯為中文,自己看了都怪,不譯為中文,又不是每個人都有技術背景,最後我選擇了約90%以上翻譯為中文,例如,Session我採用「工作階段」的中文,等作者把正式版發表,請大家多多指教。

台灣的故事呢?

今天,這位作者寄了beta版本給我測試,突然發現一件事,他軟體裡的語言選擇裡是會帶國旗,也就是英文帶英格蘭國旗,法文帶法國國旗。但讓我傷心的事,是他自己上網找資料(Google),自行把Traditional Chinese譯為中國傳統,而且帶的是五星旗。

這星期的豪小子,加上今天看了最上面的電影「聽說,hear me」,加上我辛辛苦苦翻譯的中文被加上一個錯誤的國旗,讓我有想感慨。

台灣為什麼以前能創造「台灣奇蹟」?為什麼wiki上面的台灣奇蹟是1950 ~ 1990年,那1990年之後,台灣"你"怎麼了?現在苦嗎?聽我爺爺說 823炮戰的故事,什麼感覺?我們根本無法感受到他在炮戰下的任何感受,他說,他怕到會哭,尿褲子算什麼。我們年輕人笑成一團。

現在苦嗎?

我們現在在國際上有個世界第一、第二對手,我們更應該好好加油。媒體只想沾光抄新聞,豪小子很棒,但也不用親戚朋友十八代都請出來上電視,focus在豪小子身上,夠了,豪小子會不會開口說一句:「我是台灣人。」我都很懷疑。不如吵美牛,還算點國際相關。(以上個人意見,請勿批評指教)



這才是我想要的台灣,台灣加油,大家加油。

JavaScript Event(事件) - 事件屬性和方法

事件屬性和方法

主要注意在IE與DOM名稱的差異上。


JavaScript 事件屬性和方法
IE名稱DOM名稱類型讀/寫說明
altKeyaltKeyBooleanR/W按下Alt鍵則為true
butoonbutoonIntegerR/W滑鼠事件,值對應按下的滑鼠鍵(對照下一張表格)
cancelBubblecancelBubbleBooleanIE:R/W、DOM:RIE設置為true可取消氣泡事件
N/AstopPropagation()FunctionN/ADOM使用此方法來取消氣泡事件
clientXclientXIntegerIE:R/W、DOM:R滑鼠指標在「用戶端區域」的X座標
clientYclientYIntegerIE:R/W、DOM:R滑鼠指標在「用戶端區域」的Y座標
ctrlKeyctrlKeyBooleanIE:R/W、DOM:R按下Ctrl鍵則為true
fromElementrelatedTargetElementIE:R/W、DOM:R滑鼠指標所離開的元素
toElementcurrentTargetElementIE:R/W、DOM:R滑鼠指標正在進入的元素
N/AcharCodeIntegerR按下按鍵的Unicode值
keyCodekeyCodeIntegerR/WIE中keypress事件按下按鍵的Unicode值,keydown/keyup事件為按鍵的數位代號。DOM中keypress時為0,其餘為按下按鍵的數位代號。
N/AdetailIntegerR滑鼠按鍵被按一下的次數
returnValueN/ABooleanR/W設置為false時可取消IE事件的預設行為
N/ApreventDefault()FunctionN/ADOM使用該方法來阻止事件的預設行為
screenXscreenXIntegerIE:R/W、DOM:R滑鼠指標相對於「整個電腦螢幕」的X座標值
screenYscreenYIntegerIE:R/W、DOM:R滑鼠指標相對於「整個電腦螢幕」的Y座標值
shiftKeyshiftKeyBooleanIE:R/W、DOM:R按下Shift鍵則為true
srcElementtargetElementIE:R/W、DOM:R引起事件的元素/物件
typetypeStringIE:R/W、DOM:R事件的名稱

我最常看到也最麻煩是程式碼中只寫 returnValue,也就是說單單只針對IE進行處理,只要使用其他Browser即可閃過些段程式碼。正規有進行相容性處理是returnValue與preventDefault都要進行處理。

範例:使用同一個函數處理多個事件

window.onload=function(){
  var objImg = document.getElementByTagName("img")[0];
  objImg=handle;
}

funcion handle(evt){
  // 相容性處理
  if (window.event) evt = window.event; 
  // 透過type屬性判斷事件名稱,這裡沒有on字首
  if (evt.type == "click")
    ......
  else if (evt.type == "mouseover")
    ......
}	

範例:找出觸發事件的物件

function handle(evt){
  // 相容性處理
  if(window.event) evt = window.event;		
  var getTarget;
  if(evt.srcElement)  // 處理相容性,獲取IE事件目標	
    getTarget = evt.srcElement;
  else
    getTarget = evt.target; // 獲取DOM事件目標
  ......
}	

button的值


button 數值表
數值IEDOM
0未按下按鍵左鍵
1左鍵中鍵(滑輪)
2右鍵右鍵
3同時按下左右鍵不支援,undefined
4中鍵(滑輪)不支援,undefined
5同時按下左中鍵不支援,undefined
6同時按下右中鍵不支援,undefined
7同時按下左中右鍵不支援,undefined

鍵盤事件

鍵盤事件重要的是「使用者按了什麼鍵?」IE沒有charCode屬性,而keyCode只有在keydown與keyup事件觸發時才會與標準DOM的keyCode相同,在keypress事件中等於charCode。

if (window.event) {
  evt = window.event;
  evt.charCode=(evt.type == "keypress") ? evt.keyCode : 0;
  ...
}	

通常不採用keyCode是因為它表示鍵盤按鍵,而不是輸出字元,例如輸出"a"和"A"時,keyCode的值是相等的,charCode則以字元為區分,另外,在keypress事件中,DOM的keyCode值始終為0。

JavaScript Event(事件) - Bubble Event(氣泡事件)

Bubble Event(氣泡事件)

圖一:DOM樹
圖一:DOM樹

上面的架構圖,每一個tag都擁有onclick事件,如果在最下層的按鈕button觸發了onclick事件,事件不只在此tag觸發,還會向上傳遞,接著form觸發onclick事件,再來是body也一樣會觸發onclick事件。

JavaScript Event(事件) - Event Object(事件物件)

Event Object(事件物件)

在瀏覽器中的事件都是以「物件」的型式存在,但IE與標準DOM瀏覽器之間在取事件物件上有差別。


IE 事件物件

事件物件是window物件的一個屬性event,例如,

getP.onclick = function(){
  //取得事件物件
  var evt = window.event;
}

DOM瀏覽器事件物件

事件物件須使用唯一參數傳給事件管理函數,例如,

getP.onclick = function(evt){...} 

相容性新增事件監聽

在處理IE與DOM事件相容性時,通常會這樣做,例如,

function (evt) {
 // 透過測試window.event來判斷是否是IE來決定事件物件的建立
 if (window.event) 
   evt = window.event; 
   ...
 else
   // DOM事件
} 

取消事件

事件 → 自訂事件處理 → Browser預設事件處理,取消事件處理函數,只需設成null,例如,

// 取消document的click事件
document.onclick = null;
// 取消mybutton物件的click事件
document.form1.mybutton.onclick = null; 

在自訂事件處理函數取消事件,只需傳回false即可。

return false;

JavaScript Event(事件) - 事件監聽(EventListener)

Event Listener(事件監聽)

頁面中的事件都需要一個函數來回應,這類函數通常稱為「事件處理函數,事件處理函式(event handler)」,另一方面,這些函數都在即時監聽著是否有事件發生,所以也稱「事件監聽函數(event listener)。

將事件當成HTML標籤屬性,例如,

<input type="button" value="訊息" onclick="showInfo()" /> 

將事件當成Javascript的屬性,例如,

// 除了要傳遞參數,否則處理函數不用()符號
document.form1.mybutton.onclick = "showInfo"; 

事件是由window或document等上層物件所觸發,只需設定物件的事件屬性,例如,

document.onclick = "showInfo"; 

考慮結構與行為分離,例如,匿名函數,

// 在視窗完成載入後,
window.onload=function(){
  // 取得元素id屬性為myP元素的控制權
  var getP=document.getElementById("myP");
  // 設定myP元素的onclick事件
  getP.onclick=function(){
    alert("click");
  }
} 

IE事件監聽

在IE中,每個元素都有兩個方法來處理事件的監聽,例如,

// 新增監聽函數
[Object].attachEvent("event handler",fnHandler);
// 刪除監聽函數
[Object].detachEvent("event handler",fnHandler);
event handler為事件的名稱,如onclick,onload…;fnHandler為監聽函數名稱,這個函數不需加上括弧的執行結果。例如,

getP.attachEvent("onclick",fnClick1);
getP.attachEvent("onclick",fnClick2);

在IE7/8中,兩個函數的呼叫順序是fnClick2 → fnClick1,但兩者都會被執行。例如,我們在fnClick2最後加上detachEvent來解除fnClick1,但fnClick1還是會被執行;但如果Click第二次,因為fnClick1已經被detachEvent解除,所以只會執行fnClick2。

IE不會執行標準DOM的addEventListener/removeEventListener方法

標準DOM事件監聽

每個元素都有兩個方法來處理事件的監聽,建議使用標準DOM方法來撰寫監聽事件,例如,

// 新增監聽函數
[Object].addEventListener("event name",fnHandler, bCapture);
// 刪除監聽函數
[Object].removeEventListener("event name",fnHandler, bCapture); 

event name是click、mousemove…沒有on(注意,和IE不一樣),bCapture:false為冒泡。例如,

getP.addEventListener("click",fnClick1,false);
getP.addEventListener("click",fnClick2,false); 

DOM的執行順序為fnClick1 → fnClick2,而且是fnClick1執行完成後才會從行fnClick2。所以如果我們在fnClick1中最後加上removeEventListener來解除fnClick2,那fnClick2就不會執行。

JavaScript Event(事件) - Mouse, Keyboard, HTML Event

Mouse Event(滑鼠事件)

透過event.type來取得的事件名稱沒有on開頭

JavaScript Mouse Event(滑鼠事件)
名稱說明
onmousedown按下mouse按鍵,不論左右鍵
onmousemove移動mouse
onmouseoutmouse指標離開HTML標籤
onmouseovermouse指標進入HTML標籤
onmouseup放開mouse按鍵
onclick按一下mouse左鍵
ondblclick按二下mouse左鍵

按一下Mouse共會引發三個事件,mousedown → mouseup → click。

隱藏Mouse右鍵選單

Mouse右鍵會觸發一個contextmenu事件,即選單事件,我們只要在contextmenu事件裡取消此事件產生動作,即可隱藏右鍵選單。

function blockMenu(Evt){
  // window.event 是IE才有的物件
  if(window.event){
    Evt = window.event;
    Evt.returnValue = false;//取消IE預設事件
  }else
    Evt.preventDefault();//取消DOM預設事件
}
document.oncontextmenu = blockMenu; 

Keyboard Event(鍵盤事件)

透過event.type來取得的事件名稱沒有on開頭。

JavaScript Keyboard Event(鍵盤事件)
名稱說明
onkeydown按下鍵盤按鍵
onkeypress在keydown與keyup之間的事件,或是按著不放時所觸發
onkeyup放開鍵盤按鍵

HTML Event(HTML事件)

透過event.type來取得的事件名稱沒有on開頭。

JavaScript HTML Event(HTML事件)
名稱說明
onload頁面完全載入後在window物件上觸發,圖片載入完成後在其上觸發
onunload頁面完全卸載後在window物件上觸發,圖片卸載完成後在其上觸發
onabort放棄圖片載入
onblur離開擁有處理權的物件
onchange在取得處理權後,更改過文字方塊的內容,離開文字方塊時觸發
onerror腳本出錯時在window物件上觸發,圖片無法載入時在其上觸發
onfocus指定物件擁有處理權
onreset重設表單
onselect選擇網頁內容
onsubmit送出表單

就我們在撰寫網頁相關程式碼中,又以onblur、onchange、onfocus、onreset、onselect、onsubmit這幾個表單事件(form event)最為重要,除了UI的程式碼外,表單是我們很重要與使用者互動的來源,一個良好的表單互動就能簡單讓您的網站有很大的加分的效果,所以這幾個事件的處理份量會比較重一些。

JavaScript-DHTML物件模型-Document物件(四)-CSS

CSS - 存取標籤物件的style屬性


ele.style.fontSize=12px;

CSS樣式屬性與style屬性差異


CSS樣式屬性,例如,

  • font-size
  • font-family
  • background-color
  • background-image


style屬性中,設定CSS樣式,例如,

  • fontSize
  • fontFamily
  • backgroundColor
  • backgroundImage

style需要把中間的「-」符號刪除,然後將後面的英文字頭改為大寫。

JavaScript-DHTML物件模型-Document物件(三)

取得文件元素

此小節裡的三個方法可以說是JavaScript最重要的三個方法,原因是,當我們想要對頁面上任何元素進行操作前,必須先取得此元素的控制權,有了控制權我們才有辦法對元素本身進行操控。

JavaScript 取得文件元素
名稱說明
getElementById()依HTML tag的id屬性取得指定的元素
getElementByName()依HTML tag的name屬性取得指定的元素
getElementByTagName()傳回一個包含某個相同標籤名稱的元素NodeList(Array)

<h2 id="header">取得文件元素</h2>

// 取得元素id屬性為header的元素
ele = document.getElementById("header");

// 取得元素name屬性為input的視素
ele = document.getElementsByName("input");

// 取得元素標籤名稱為li的元素,回傳Array()
ele = document.getElementByTagName("li");
alert( ele.length +', '+ ele[0].tagName +', '+ele[3].childNodes[0].nodeValue);

ele代表元素。

tagName:tag名稱。length:同名tag有多少個。

存取HTML標籤(tags)的內容和屬性 - innerHTML

由getElementById / getElementByName 取得文件元素後,就可以使用innerHTML來存取標籤物件內的子標籤和內容。一般都是用來插入HTML標籤。例如在空的<div>、<span>、<p>插入所需標籤,來顯示動態效果。innerText / outerHTML / outerText 除IE外,其他Browser相容性不佳,不建議使用。

ele.innerHTML = '<span>動態由JavaScript所插入的span元素</span>';

標籤屬性


// 取得<p>屬性align的屬性值
eleAttr = document.getElementById("P").align;
/* IE可用以下方法,但不建議使用。
 * eleAttr = document.all.tags("P").item(0).align;
 */

JavaScript HTML標籤屬性存取
名稱說明範例
getAttribute(attr)取得傳入attr屬性值ele.getAttribute("align");
setAttribute(attr,value)將value值設定給attr屬性ele.setAttribute("align","center");
removeAttribute(attr)刪除傳入的attr屬性ele.removeAttribute("align");

標籤尺寸與位置


JavaScript HTML標籤尺寸與位置
名稱說明
offsetLeft標籤物件距離左方邊界的距離
offsetTop標籤物件距離上方邊界的距離
offsetHeight標籤物件的高
offsetWidth標籤物件的寬
offsetParent取得標籤物件的上一層物件

scrollIntoView方法

scrollIntoView("true|false"):如果Broswer看不到標籤物件,自動捲動視窗顯示標籤物件,例如,

ele.scrollIntoView(true);

JavaScript-DHTML物件模型-Document物件(二)-Collection集合物件

Collection集合物件


JavaScript Collection集合物件
名稱說明
document.all取得HTML文件中所有Tag Object
document.all(index).tagName取得指定index的Tag Name
document.all(index).SourceIndex取得指定index在HTML中出現的順序(IE only)
document.all.tags("P").length使用tags()方法篩選出指定標籤的物件集合,其中HTML標籤名稱需使用大寫字母

document.all在IE使用沒有問題,但在其他Browser相容性不佳,建議使用getElementById("id")或getElementByName("Name")來取得所需的資訊 (使用getElementById/ByName替代document.all)。

JavaScript-DHTML物件模型-Document物件(一)

Document物件屬性


JavaScript Document物件屬性
名稱說明
domain傳回伺服器的網域名稱
referrer傳回URI字串,指出連結此網頁的位置
title傳回和設定title元素,即<title>
URL傳回網頁的URL字串

開啟與寫入文件


JavaScript 開啟與寫入文件
名稱說明
write();向文件寫入HTML、文字、JavaScript…
writeln();同write()方法,但最後會包含換行符號
open("MIME Type","replace");清除目前的文件內容,然後使用write()或writeln()重新輸出文件的內容
MIMEType:text/html
replace可以取代歷存記錄
最後使用document.close()方法,顯示write()或writeln()輸出的文件內容
close();

Cookies


document.cookie = "name=value;expires=date;path=pname;domain=dname;secure";

JavaScript Cookies
名稱說明
name(必要)cookie名稱
expirescookie有效期限,為GMT格式。例如:exDate.toGMTString();
domain伺服器網域名稱
path在domain下的路徑名稱
secure如設定,表示cookie需在保密下才能傳送

domain屬性是為了分別cookie屬於那個網站所建立;path屬性可進一步區別同一網站不同網頁所建立的cookie。

getCookies = document.cookie;

取得cookie屬性的值;kname1=kvalue1;kname2=kvalue2...,為名稱/值的對應。我們必須自行寫Code存取Cookie的資料。

JavaScript-DHTML物件模型-Location物件

Location物件

這裡我們要先了解網址的結構是如何組成的,舉例一:

http://www.microsoft.com:80/default.aspx#user

網址的結構(一)
名稱說明
hrefhttp://www.microsoft.com:80/default.aspx#user
hostnamewww.microsoft.com
port80
hash#user

舉例二:

http://www.microsoft.com:80/default.aspx?user=kkbruce

網址的結構(二)
名稱說明
protocolhttp:
hostwww.microsoft.com:80
pathnamedefault.aspx
search?user=kkbruce

Location物件屬性


JavaScript Location物件屬性
名稱說明
hostname伺服器"網域名稱"或"IP位址"
hrefURL網址
hostURL的hostname:port
hashRUL的#符號後的部份
port通訊埠號,WWW預設80埠號,FTP預設21埠號
pathname網頁檔案的名稱和路徑
searchURL的?符號後的部份
Protocal使用的通訊協定,常用有http://與https://

轉向


// window.location.href='http://...';
location.href="http://...";

可使用「上一頁」來回到上一頁。

location.replace(url);

轉向url的網址,同href屬性,但不能使用「上一頁」來回到上一頁。

重新載入


location.reload(true);

重新載入現在開啟的HTML文件,如同Browser的"重新整理"鈕。

JavaScript-DHTML物件模型-History物件

History物件


JavaScript History物件
名稱說明
length傳回history物件的歷史記錄數
back();回到上一頁
forward();移到下一頁
go(num);移到上或下幾頁,num大於0為下幾頁,小於0為上幾頁(常用"-1"代表上一頁)

JavaScript-DHTML物件模型-Navigator物件

取得Browser和系統資源資訊


JavaScript Navigator物件
名稱說明
appCodeName傳回Browser的程式碼名稱
appMinorVersion傳回Browser的次版本(IE)
appName傳回Browser的名稱
appVersion傳回Browser版本和作業系統名稱
browserLanguage傳回目前Browser使用的語系(IE)
cookieEnable傳回Browser是否支援cookie
cpuClass傳回CPU的類型(IE)
onLine傳回目前系統是否在線上
platform傳回作業系統的平台
systemLanguage傳回系統預設的語系(IE)
userAgent傳回HTTP通訊協定資料user-agent
userLanguage傳回目前使用者所使用的語系(IE)

JavaScript-DHTML物件模型-Window物件-Screen物件

Screen物件


JavaScript Screen物件
名稱說明
heightMonitor解析度的高度
widthMonitor解析度的寬度
availHeightMonitor視野的高度
availWidthMonitor視野的寬度
colorDepthMonitor的色彩數

取得座標


JavaScript Screen物件之取得座標
名稱說明
screenLeft(IE)/screenX傳回左上方邊界的座標
screenTop(IE)/screenY傳回上方邊界的座標

調整與移動視窗


JavaScript Screen物件之調整與移動視窗
名稱說明
moveTo(x,y);將Browser移到座標x和y位置
moveBy(offsetX,offsetY);將Browser從目前位置,移動x,y的位移量,位移量大於0表示向右和向下移動,小於0表示向左和向上移動
resizeTo(width,height);將Browser視窗調整為width和height的尺寸
resizeBy(offsetX,offsetY);將Browser視窗放大或縮小,x,y大於0為放大,小於0縮小

內容捲動


JavaScript 視窗內容捲動
名稱說明
scroll(x,y);捲動視窗內容到指定的位置
scrollTo(x,y);捲動視窗內容到指定的位置
scrollBy(offsetX,offsetY);從目前視窗內容的位置,捲動參數的位移量

JavaScript-DHTML物件模型-Window物件- open,close,對話方塊

open視窗


window.open(url,name,features,replace);

features


Open函式 features屬性
名稱說明
left/screenX (*)設定Browser左上角X座標,單位px
top/screenY (*)設定Browser左上角Y座標,單位px
height/innerHeight (*)設定Browser視窗的高,單位px,最小100
width/innerWidth (*)設定Browser視窗的寬,單位px,最小100
directories顯示Browser連結列(無屬性值)
location是否顯示Browser網址欄
menubar是否顯示Browser功能表
resizble是否允許調整視窗的尺寸
scrollbars是否顯示垂直與水平的動軸
status是否顯示Browser下方的狀態列
titlebar是否顯示Browser上方的標題列
toolbar是否顯示Browser的工具列

(*) left,top,height,width為IE使用,screenX/Y,innerHeight/Width為MF使用,建議相對應的屬性(例:left/screenX)都設定。

replace

只適用IE。

windows.open()範例


window.open("http://blog.kkbruce.net","_blank","height=300,width=400,innerHeight=300,innerWidth=400"); 

在逗號或等號前後不能有空格。

close視窗


window.close();

對話方塊

alert(message); 

警告訊息。

confirm(message); 

確認方塊,true按確定鈕,false為按取消鈕。

prompt(message,value); 

輸入文字對話方塊,message為訊息內容,value為預設值。

列印

window.print(); 

JavaScript-DHTML物件模型-Window物件-計時器

只會執行一次

  • setTimeout(express,time)
    當time所設定的時間到達時,執行express運算式,time為千分之一秒
  • clearTimeout()
    停止setTimeout方法啟動的計時器

週期執行

  • setInterval(express,time)
    以time時間週期性地執行express運算式
  • clearInterval()
    停止setInterval方法啟動的計時器

JavaScript-DHTML物件模型-Window物件-Browser狀態列

Browser狀態列

  • defaultStatus
    設定和取得Browser程式狀態列的預設訊息文字
  • status
    設定和取得Browser程式狀態列的訊息文字

JavaScript 流程控制 - function函式

function函式組成

function name(){
  statement
} 

(arguments),括號內可有一或多個參數。當資料做為參數而傳入函式,它的行動就像函式內已初始化的區域參數;雖然函式參數的行為很像函式內的區域變數,在函式內改變參數,卻不會影響函式外的任何事物。

function函式回傳資料

使用return關鍵字來回傳資料。

function name(){
  statement
  return value;
}

函式只要碰到return後立刻結束。技巧:在流程控制上,我們也可以使用沒有回傳值的return來結束函式。return不只回傳資料,也負責結束函式。

function name(){
  statement
  return;
} 

所以函式的回傳值會取代了函式的呼叫。例如,

// 假設有一計算價錢的函式cale(),參數number傳入數量,會得到金額
totalPrice = cale(number);
// cale()函式計算後會return price;
// 最後會以回傳值取代函式的呼叫,可以看成:totalPrice = price; 來得到最終的結果 

function is data

函式實字(function literal)

當函式本體單獨出現,沒有名稱。把函式本體看成值,把函式名稱看為變數名稱。

var showMsg = function(Msg){
  alert(Msg);
} 

showMsg:函式名稱(函式參考),即變數名稱。

function(){...}:把function(函式實字)當成變數值。

函式實字讓函式能像變數般操縱,例如,

var myMessage = showMsg;

指派showMsg()函式給變數myMessage。以上程式碼表示函式也能利用變數語法而建立,甚至組成元件也相同。指派函式名稱給另一個變數,就是為了讓變數可以取用函式本體。

alert(myMessage("Hello World!")); 

function reference, 函式參考

呼叫showMsg()與myMessage的結果相同,因為兩個函式最後都參考(reference)了相同的原始碼,因此函式名稱也稱函式參考(function reference)。

函式其實只是「值」參考到函式本體的變數。

函式參考與函式呼叫

函式參考只會單獨出現,例如,

var myMessage = showMsg; 

對myMessage指派函式參考。函式呼叫則必定後隨括號( ),很多時候還附有參數,例如,

myMessage("Hello World!"); 

呼叫myMessage()與showMsg()相同。

參考重要性

函式參考不像一般變數,變數的資料儲存成記憶體的某個區段,函式則儲存對原始碼的參考,函式變數的值不是原始碼本身,而是指向儲存原始碼的記憶體位置的指標,函式使用參考,而非實際的值,比起儲存多份函式碼的複本,參考有效率多了。

JavaScript 流程控制 - break與continue關鍵字

break與continue關鍵字

  • break; 終止此迴圈的執行,離開此迴圈
  • continue; 終此這一次的迴圈的執行,執行下一輪的迴圈;或停止continue之後所有迴圈程式,回到迴圈開頭(for/while/do)

JavaScript 流程控制 - 用with建立物件程式碼區塊

with 與 for ~ in差異

for ~ in主要顯示物件的所有屬性,with敘述能夠針對物件(Object)建立一個程式區塊。

with (ObjectName){
   ...
} 

例如:

// 建立一個名片物件
var Card = new Object();
with (Card){
  // 新增property
  name = "Bruce";
  age = 33;
  birthday = new Date("2012/2/15");
  document.write("姓名:"+name+",年齡:"+age+",生日:"+birthday); 
}

JavaScript 流程控制 - 迴圈控制

for迴圈控制

適合重複動作已知次數。

組成:
  1. 初始化(initialization)
  2. 條件句(test condition)
  3. 動作(action)
  4. 更新(update)

// 執行順序為[1][2][3][4]-[2][3][4]-[2][3][4]-...
for ([1]initialization; [2]condition; [4]post loop expression)
  [3]statement

/*
for (1;2;4)
  3
  
或

for (初始化;條件句;更新)
  動作
*/ 

最佳化for迴圈控制

for (var i = 0; i < a.length; i++) {
 a[i]=i;
} 

這樣不是很有效率,因為每迴圈一次就會查詢一次 length 屬性。(但所有JavaScript的教科書都這樣教!)比較好的寫法:

for (var i = 0, len = a.length; i < len; i++) {
 a[i]=i;
} 

初始化時宣告兩個變數,把length屬性值存放到變數中。更棒的寫法:

for (var i = 0, item; item = a[i]; i++) {
 處理item
}

for 迴圈中間指定變數值的部分會被測試是否為「真的」(truthy):如果成功了,迴圈便會繼續。由於 i 每次都會加一,陣列內的每個項目會被照順序指定到變數 item。當偵測到「假的」(falsy) 項目時(如 undefined)迴圈便會停止。注意:這個小技巧只該用在你確定不會含有「假的」值的陣列(比如說一陣列的物件或 DOM 節點)。假如你在可能含有 0 的數字資料或可能含有空字串的字串資料上做迴圈,最好還是用 i, j 的方式。

while / do ~ while迴圈控制

while迴圈本身沒有更新條件程式,必須自建條件更新條件(update)。
while (condition)
  // 需含更新條件
  statement
組成:
  1. 檢測條件(test condition)
  2. 行動(action)

do
  statement
while (condition) 

先執行一次完整程式碼(statement),再進行檢測條件(condition)。

for ~ in迴圈控制

通常用來列舉物件的屬性。
for (property in expression)
  statement 

for / while 迴圈控制討論

while迴圈能做到任何for迴圈的任務,反之亦然。

for (init; test; update){
  action;
} 

init;
while (test) {
  action;
  update;
} 

JavaScript 流程控制 - 條件控制

if條件控制

if範例

if (condition)
  statement 

if (true/false test)
  Do something; 

if/else範例

if (condition)
  // true
  statement
else 
  // false
  statement

if (condition)
  statement
else if (condition)
 statement
else
  statement 

可產生無限個 if / else if。

?:範例

「條件運算子」可用來指定變數的值,如同 if / else,在變數值需要二選一時非常實用

$Var = (condition)? "Value1" : "Value2";

()內為條件,true指定?後的內容給變數,false指定:後的內容給變數。

Hours = (inputHour>=12)? "PM" : "AM";

//原if/else程式碼
/*
if (inputHour >=12)
  Hours = "PM";
else
  Hours = "AM";
*/

switch條件控制

switch (expression) {
  case value:
    statement
    break;
  case value:
    statement
    break;
  ...
  default:
    statement
}

(condition)最終的結果只能為true或false。(expression)相當於資料,case等於「==」邏輯運算子,value等於要比較的值,用說的:(expression)資料 == value嗎?是就執行break前的程式區塊(一段一段case比較),如果都不是請執行default程式碼。

JavaScript Object - 共用屬性與方法

共用屬性與方法

JavaScript 物件共用屬性
名稱說明
constructorjavascript物件的constructor屬性可以取得建立物件副本使用的建構函數名稱(除Global和Math都支援)

範例:檢查物件的建構函數是否為String()


var test = new String();
if (test.constructor == String){
 // Do something ...
} 

JavaScript 物件共用方法
名稱說明
toString()傳回物件的內容,傳回值為字串。例如,object.toString();

依物件型別不同,會回傳不同字串。

JavaScript toString()回傳值
名稱說明
Array將陣列元素轉換成「,」符號分隔的字串
Booleantrue傳回字串"true",false傳回字串"false"
Date傳回日期和時間的字串
Error傳回錯誤息訊的字串
Function傳回字串格式"function name() {...}",其中name為呼叫toString方法的函數名稱
Number傳回數字字串
String傳回String物件的內容

範例:數值轉字串,同時進制轉換


var a=15; // Number
a.toString(8); //8代表8進制
a.toString(16); //16代表16進制 

JavaScript 物件共用方法
名稱說明
valueOf()傳回物件值(Math,Error不支援),例如:object.valueOf();


JavaScript valueOf()回傳值
名稱說明
Array將陣列元素轉換成以「,」符號分隔的字串,如同Array.toString()和Array.join()方法
Boolean傳回布林值
Date傳回前晚到現在的秒數,以千分之一秒為單位
Function傳回函數的本身
Number傳回數字
Object傳回物件本身
String傳回字串