2007年05月3日

關於 HTML label 元素無法作用

石頭成老大在我的「終於用 jQuery 做了一個有趣的服務」提到了在 IE 裡 label 元素的 for 屬性是有 bug 的,我研究了一下,把心得分享給大家。

原本我一直以為表單中的 input 元素只要設定 display: none 或是 visibility: hidden 的話,就沒辦法送出;不過 W3C 中在「 17.13.2 Successful controls 」一節中有明確的規範:

Hidden controls and controls that are not rendered because of style sheet settings may still be successful.

也就是說不論是 display:none 或是 visibility: hidden ,只要是利用 CSS 來讓 control 消失的,都還是應該把值送給後端。

我用了以下的程式測試了一下:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>TEST</title>
<style type="text/css">
.hide {
    visibility: hidden;
}
.none {
    display: none;
}
</style>
</head>
<body>
<form action="" method="post">

<label for="type1">Type1</label>
<input type="radio" class="hide" id="type1"
 name="type[]" value="1" checked="checked" />

<label for="type2">Type2</label>
<input type="radio" class="none" id="type2"
 name="type[]" value="2" />

<input type="submit" value="送出" />
</form>
<a href="<?php echo $_SERVER['PHP_SELF']; ?>">重新整理</a>
<?php var_dump($_POST); ?>
<script type="text/javascript">
window.onload = function () {
    var t2 = document.getElementById('type2');
    t2.focus();
}
</script>
</body>
</html>

結果發現 Firefox 、 Opera 不論有沒有預設的 checked ,在點選 label 後都會正確送出對應的 radio 欄位值。

而 IE6 、 IE7 事實上也是支援,只是它們僅能送出有預設 checked 的 radio 欄位;如果完全沒有預設 checked 的 radion 欄位時,因為點選 label 仍無法讓 radio 的值改變,因此也無法送出任何 radio 值 (就如石頭成老大所說明的) 。

所以我原先誤認 IE 不會理會 display: none 或是 visibility: hidden 的元素,其實是 label 無法作用;但更正確的來說,則是因為 IE 無法接受把 focus 放在不可見的元素上

換句話說,我們沒辦法在 IE 上 focus 任何被 display: none 或是 visibility: hidden 的元素;而 label 則是 HTML 用來 focus 對應 input 元素的捷徑,也因此就不會有作用了。

註:所以我在「終於用 jQuery 做了一個有趣的服務」裡的作法就是為了避開這個問題。

最後這是不是 bug 呢?我個人倒覺得像是 IE 開發團隊自己的想法。不過 IE 無視於 W3C 標準也不是第一次了,所以石頭成老大說這是 IE 在 W3C 實作上的 bug 其實也不為過。至於解法,大家可以直接參考石頭成老大的說明,這裡我就不多寫了。


Posted by jaceju at 樂多Roodo!14:23回應(4)引用(1)

2007年04月19日

IE7 對錯誤 Hostname 在處理 Cookie 上的問題

昨天發現了一個 IE7 的問題,我們兩個做了一個討論後在這裡把結果記錄下來。

要重現這個問題很簡單,請照著以下的步驟試試看。

首先在 Windows 的 hosts 檔裡加入粗體部份:

127.0.0.1    localhost 
127.0.0.1    host_name

然後在網站根目錄底下建立一個 session.php ,內容如下:

<?php
session_start();
$test = (isset($_SESSION['test']))
      ? (int) $_SESSION['test']
      : 0;
echo $test;
$test ++;
$_SESSION['test'] = $test;

然後試試用 IE7 瀏覽以下網址:

http://host_name/session.php

試試看去重新整理這個頁面,會發現 Session 沒辦法動作。

不過同樣的網址在 IE6 和 Firefox 上, Session 都會在每次重新整理時正常加一;但如果把 host_name 換成 localhsot ,則每個瀏覽器都會正常動作。

一開始我以為是 IE7 有支援 HttpOnly Cookie 的設定,不過這項設定是在 PHP 5.2 以後才會有,而凱用的是 PHP 5.1.6 ;然而 Firefox 也支援 HttpOnly Cookie 卻能正確運作,所以並不是這個問題。

凱告訴我,他在 hosts 上加入 127.0.0.1 my.xxx.net 的設定,然後再用以上三種瀏覽器去瀏覽 http://my.xxx.net/session.php ,發現 Session 是可以運作的。這時我們就想到會不會是 host_name 有底線的關係,所以凱很快地重新把底線去掉後再用 IE7 測試,結果就正常了。

最後我們兩個人的結論是:

在 IE7 中,有底線的 hostname 會沒辦法正確使用 Cookie 。

其實微軟不建議 Hostname (或 Domain Name ) 使用底線字元,這個可以參閱「為電腦、 網域、 站台及 OU 命名慣例在 Active Directory 」一文。


Posted by jaceju at 樂多Roodo!10:32回應(2)引用(0)

2007年03月16日

透視 WebMVC

這篇寫超久的...大概從去年九月開始寫的,不過因為中間事情太多,因此是以牛步進行;之間有跟幾位 PHP 界的長輩們預告過本文的誕生,還好沒有跳票。

本文主要是把我所認知的 WebMVC 概念,以實作的方式來說明。而範例本來寫得很複雜,也請過丫凱兄幫忙測試。不過後來想想其實不必要在範例裡加入太多功能,因此就簡化了很多部份。

希望大家看完後,能給我一些建議與指導。

文章網址:透視 WebMVC


Posted by jaceju at 樂多Roodo!18:26回應(3)引用(0)

2007年02月4日

5 個我在舊專案的 XHTML 中常發現的問題

最近在一些維護一些舊專案裡的頁面,它們都已經採用了 XHTML 1.0 Transitional 。一個正確的 XHTML 1.0 Transitional 頁面的範例如下:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=big5" />
<title>Page Title</title>
</head>
<body>
...
</body>
</html>

當然大部份頁面是符合的,不過我還是發現了一些問題;在這裡我把它們記下來,供往後開發時參考。

...繼續閱讀

Posted by jaceju at 樂多Roodo!16:28回應(3)引用(0)

2007年01月3日

將資料和程式分離

有位 jocosn 網友問了一個我覺得很棒的問題,我把我的觀點提出來供大家參考。

註:以下我會以引用區塊的方式來標示該網友的問題,然後以回答該問題的語氣說明。且基於本人龜毛的個性,也會把內容稍作排版。

如果我有一個 welcome.php,它的樣版檔是 welcome.tpl.htm,但是兩個檔案的編碼不相同,也就是 php 使用 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> , tpl.htm 使用 <meta http-equiv="Content-Type" content="text/html; charset=big5">,結果螢幕顯示時會以 utf-8 編碼格式顯示 tpl.htm 檔案,必須再手動挑選 big5,請問這種情況有辦法解決嗎?還是程式這種架構有問題?

使用 big5 是因為 tpl.htm 檔案有用到 mailto,像這樣:

<a href='mailto:h...@yahoo.com.tw?subject=其他問題&body=問題描述:%0A%0A%0A%0A聯絡方式%0A地址:%0A電話:%0A聯絡人:%0A'>寫信連絡</a>

如果改用 utf8,點下 mailto 開啟 outlook 6 會出現亂碼。所以採用 big5 才會正常顯示中文字。但是 php 檔案因有些部份未使用樣板處理一些簡單的小訊息 (如顯示"檔案建立成功") ,所以採用 utf8,以免顯示中文像「功」字會出現問題。

像這種情況下,請問該怎麼處理,或是有更好的解決方式嗎?

事實上你的 template 還是可以用 utf-8 的,只不過有些地方要做一些手腳。

在這之前,先給你一個觀念:把資料和程式分離。怎麼說呢?直接給你看例子好了:

首先我把 mailto 會用到的中文字另存成一個 ini 檔:

[mailto]
subject = "其他問題"
body = "問題描述:\n\n\n\n聯絡方式\n地址:\n電話:\n聯絡人:\n"

特別要注意的是,這個 ini 檔是用 big5 來存檔的。

然後我在 index.php 裡把它載入:

<?php
$mailto = parse_ini_file('mailto.ini');
  
$subject = urlencode($mailto['subject']);
$body = str_replace('%250A', '%0A', urlencode(str_replace('\n', '%0A',  $mailto['body'])));
  
include('template.php');

這邊要特別注意的是 php 檔是用 utf-8 來存檔 (事實上你也可以用 ANSI 來存) 。

最後來看看 template.php :

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>測試 mailto</title>
</head>

<body>
<a href="mailto:jxxxxxxxxx@gmail.com?subject=<?php echo $subject; ?>&body=<?php echo $body; ?>">寫信連絡</a>
</body>
</html>

這裡一樣也是用 utf-8 存檔。

執行它看看,跑出來的結果是不是符合你的需要呢?

這裡就導出一個觀念了: PHP 在處理資料時,其程式檔案的編碼是不會影響資料本身的編碼的。

在上面的例子裡, parse_ini_file 取得的資料就是 mailto.ini 本身的編碼 (也就是 big5) ,而 PHP 也不會因為自己的檔案編碼是 utf-8 而改變 parse_ini_file 所取得的資料編碼。因此這時候的「其他問題」和「問題描述...」都能夠用在 template 上...

不過等一下,如果你直接把 $mailto['subject'] 和 $mailto['body'] 套入 template.php 的話,那就慘了。為什麼呢?因為 big5 編碼的內容在 utf-8 編碼的網頁裡,瀏覽器不知道該怎麼解釋它們 (也就是一堆亂碼啦) 。所以這裡我們要利用 urlencode 這個函式,來將 big5 的文字做 URL 編碼。

至於 \n 的部份只是個表示方式,實際上我們還是要手動將它換成 %0A (雖然我換的方式還滿醜的) 。

當然不只是文字檔案,就連資料庫中取出的資料也是相同的道理。這點你可以參考我先前寫的 MySQL 中文編碼徹底研究,裡面有詳細的內容。

再請問,如果用 smarty 時,php 檔案是不是應該儘量只有 php 程式碼?

依照上面的觀念,你認為我這裡會給什麼樣的答案呢?想想看吧 :)

另外 php 檔案存成 utf-8 或 big5 有差嗎?還是說網頁儘量不要存成 big5,以免特殊中文字會出現錯誤?

例如 php 檔案中像這樣 $mysmarty->assign('myVar1','許蓋功') ,存成 big5,我在測試時,就會出現錯誤 Parse error: parse error, unexpected T_STRING

PHP 引擎會對 <?php ?> 裡面的特殊字元很敏感,所以上面的觀念一樣適用在這裡;當然如果是在 <?php ?> 外的話 (通常是 HTML ) 就沒問題了。

希望上面的說明能讓你清楚相關的觀念 :)


Posted by jaceju at 樂多Roodo!16:38

2006年05月8日

[心得] 網頁程式開發建議

這篇是寫給新同事以及我自己備忘用的,也歡迎大家提出指正。

在撰寫網頁程式時,有些基本的東西要非常注意,這樣寫出來的程式才不容易出現問題,也才能夠交給客戶。以下就是我自己的開發心得,供大家參考。

利用明確的指定方式來取得頁面傳來的變數

不管是 GET 或 POST 的參數,我會希望明確地指定它的型態,例如 id 應該是整數, content 是文字等。而且我也常常發現有人把接到這些變數後,沒有事先做處理就直接串到程式中,這是非常危險的一件事情!

最明顯的例子就是 SQL Injection 了,我想這應該不用多說了。總而言之,不要過份相信用戶端傳來的資訊,把它轉換成你能掌握的型態再用吧。

資料庫的連結每個頁面只作一次

資料庫連結是網站頁面常做的事情,但是儘可能不要讓一個頁面產生太多的資料庫連結。我們可以利用 Singleton 模式來取得資料庫連結物件,這樣可以確保程式裡只會用到一個資料庫連結;當然需要連結不同資料庫的話就另當別論。

所以我們要確定資料庫會在頁面初始化時連結,頁面結束時關閉。這樣有單一的入口與出口,程式也就不容易出現奇怪的問題。

相同的邏輯不要寫兩遍以上

如果發現有兩支程式會用到相同的程式邏輯時,不要猶豫,把它抽出來變成類別或函式 (最好是類別,原因在下一則) 。因為如果哪一天需要更新程式的邏輯時,你只需要更改一支程式即可。對健忘的人來說,這點尤其重要。

我就遇過有人把產生選單的邏輯重複寫在十多支程式裡,結果有次客戶要求要修正其中一個地方,可憐的維護人員 (就是我啦) 就得一支一支地去翻出來改。

利用物件來管理錯誤

我這裡指的錯誤是任何預期中的狀況,也就是你不希望使用者操作的方式,例如編號不存在或是檔案大小超過限制等。

當錯誤發生時,不要立刻結束,應該利用錯誤上升機制來讓通知上一層的程式,最後再由頁面控制程式來決定要如何處理錯誤。我常常會遇到有人在函式裡利用 exit 離開程式,但是這時候頁面的資料庫或其他物件等等都還沒釋放掉;雖然程式平台可能會幫你做,但那總是很難預期。

所以我建議不要使用函式,而改為使用物件的方法,然後利用類似 PEAR::isError() 來判斷是否執行成功;如果失敗的話就把錯誤往上丟,直到頁面能夠控制為止。

該釋放的要記得釋放

不管是物件還是資料庫,都應該在頁面結束前將它們銷毀或關閉,而不要過於依賴程式執行平台。網頁程式是很多人會同時存取的,如果沒有正確地將資源即時釋放掉的話,久而久之就會造成系統效能上的不穩定。

而釋放的動作要什麼時候做呢?記住一句話:誰開的就誰負責關。例如上面頁面控制程式開啟資料庫連結,那就在頁面控制程式的最後把資料庫連結關閉。類別建構函式產生的物件,就在類別解構程式裡銷毀。函式開的頭,當然就在函式尾收掉;不過有個例外,那就是這個函式如果本身就是要回傳產生的物件時,那就不能把它給釋放囉,而是要改為呼叫這個函式的程式來釋放。

利用樣版技術

樣版是用來分離程式邏輯與視覺頁面的,也常常有人用 MVC 這個模式來稱呼它。然而兩者分離除了不相互干擾外,其實還有一個好處:那就是程式可以在錯誤發生後,決定要顯示的結果。就像上面提到的錯誤管理,當我們在頁面控制程式取得錯誤訊息時,我們就可以而用置換樣版來避免掉頁面的錯誤,或者是導向別的處理程式。

這點我覺得 PHP 的 Smarty 就考慮得很好,因為它是後期頁面綁定 (Binding) ,而不會像傳統樣版引擎在前期就把頁面拉進程式處理,導致錯誤發生時徒然浪費處理時間 (當然要看怎麼設計的) 。

徹底瞭解開發環境的性質

網頁程式和一般應用程式 (例如視窗應用程式) 在本質上是有差異的,這些差異不僅是在操作上,就連執行的過程都非常的不一樣。雖然現在有 AJAX 或其他技術可以縮小彼此的差距,但是它還是建構在 HTTP 這個無狀態協定之上。身為網站程式開發人員,其實應該要瞭解這些基礎,而不要只熟稔某些已經被包裝過的技術就顯得自得意滿。

最簡單的就是伺服端程式與用戶端程式之間的溝通,例如 PHP 和 JavaScript 。我常常在網路上看見有人問道:要如何讓 PHP 和 JavaScript 之間的變數互通?如果瞭解 HTTP 執行的過程,那麼你就會自己發現這些問題的答案。


Posted by jaceju at 樂多Roodo!16:39
 [1]