做WEB介面編程很痛苦。菜鳥做WEB介面編程比黃連還苦。 Dojo 提供了一個Tooltip Widget。當我們的滑鼠移到指定的地方時,一個提示框就彈出來了。 再把Tooltip稍稍擴充一下,就可以在彈出來的Tooltip裡放任意HTML代碼。加上一點AJAX調用,一個內容豐富的提示框就做出來了: 滑鼠可以在tooltip裡自由移動。滑鼠移出tooltip,該tooltip自動消失。夠簡單吧?可惜接著我就被dojo玩兒死了。 展開任何一個的下拉框,把滑鼠移進下拉框。Tooltip立刻消失。難道Ellen Ullman的The Bug這一刻靈魂附體? 順藤摸瓜到dojo.widgets.tooltip的代碼裡。既然跟滑鼠移動有關,就找和事件onmousemove有關的代碼。很容易地找到了dojo.widgets.tooltip._onMouseMove = function(e){…}。代碼很簡單,靠一個協助函數dojo.html.isOnElement(element, e)來判斷滑鼠的位置是否在Tooltip內。加上一行dojo.debug(…),重新整理頁面,發現問題所在:當滑鼠在一般HTML控制項上,比如table, div, select一類,用來判斷座標的mouseEvent.pageX都是滑鼠相對於document的座標。可是當滑鼠移動到展開的option下拉框裡,座標就編程相對於包含該option的div的座標了。怒。不過還好。不就是累加所有mouseEvent.targe和targetParent的(offsetX, offsetY)麼: 重新整理,再試。“Select size”下拉框可以正常工作了。不過作為一個負責的程式員,不能只測試一種情況吧?於是把滑鼠移到了第二個下拉框。我的心立馬哇啦哇啦地涼了。滑鼠移到第二行選項時,整個tooltip消失樂。 仔細觀察了一下,注意到兩件事:1. 雖然滑鼠移到第二行選項,這一行並沒有顯示高亮,高亮仍然停留在第一行。2. 這種情況只存在於第二個下拉框。第一個和第三個都沒有問題。3. 滑鼠的座標奇蹟般地從(600, 400)變成了(20, 10)。嗯,老實的程式員從來不在遇到問題的時候懷疑工具和機器,所以我開始沉痛檢討自己的程式。多加一條dojo.debug後,發現雖然滑鼠在下拉框裡,丫對應的MouseMove.target居然是HTMLSelectElement!也就是說,牛皮轟轟的瀏覽器把下拉框背後被遮蓋的select控制項框當成MouseEvent.target返回,而MouseEvent.pageX/Y仍然是針對option的相對座標。而我上面的代碼只針對option做了修複,自然跳過了當target為HTMLSelectElement的情況。在if語句裡加上 || element instanceof HTMLSelectElement也不行,因為沒有重疊時,滑鼠移到Select上面產生的MouseEvent.pageX不需要累加targetParent的座標。更重要的是,為什麼會發生這種情況?為什麼選項沒有高亮顯示?現在的自然選擇是檢查select的代碼。純HTML代碼,有點CSS樣式,沒有問題阿。波利亞老大在How to Solve It裡反覆強調的話響起來了:
你已經知道什嗎?什麼肯定有效?什麼是已經解決了的相關問題?能排上用場嗎?嗯,迴歸原始。一個陽春select肯定沒有問題,於是我開始一項一項去掉附加的CSS樣式。結果當width被去掉後,問題消失。進一步試用不同的寬度,發現當選項長度超過選項控制項長度時,問題就會出現,而且只在配合dojo使用時出現。不爽啊不爽。 墨菲定律說,壞事紮堆。推論就是,測試時,應該在出現問題的時候測試周邊情況,多半能發現更多錯誤。有了上面的經驗,自然聯想到如果下拉框超出tooltip邊界會怎麼樣?不出所料,滑鼠移到超出邊界的選項時,下拉框消失了。這個錯誤應該是dojo的問題:單單比較滑鼠和控制項的座標是不夠的。當然也可以說是我們的問題:也許tooltip根本就不是設計來做我們這種資訊框的。幸好解決方案不難:我們其實不關心滑鼠的具體座標。我們只關心滑鼠是不是還在tooltip內的任何一個控制項上。既然這樣,我們可以找到滑鼠所在的控制項,然後比較該控制項是不是在tooltip裡。這個簡單。又是累加offsetX和offsetY的問題: 再判斷得到的座標是不是在tooltip內。重新整理。嗯,所有情況都正常了。不對,好像過猶不及。現在把滑鼠移出tooltip,tooltip也不消失了。以頭戧地一分鐘。定神一下,不由再次感歎臥春,臥室達春綠。不同的HTML元素可以重疊。所以哪怕滑鼠在tooltip之外,它所在的HTML元素(比如DIV)的一角完全可能在tooltip內。於是改為判斷四個角都在tooltip內。這步其實不可靠。不過我們在tooltip裡只支援簡單的HTML form元素,所以還是比較可靠的。這下終於成功了。 靠,其實是俺的幻覺。IE不認HTMLOptionElement,拋出異常。加入跨瀏覽器的代碼。這個終於世界清靜了。哪位老大有更好的解決方案嗎? 教訓:§ UI編程麻煩,處理太多特例。§ 抽象層泄漏是個現實問題。用不成熟的架構抽象層泄漏就成了不可避免的現實問題。§ UI編程一點都不好玩兒。一個多小時就折騰出這點玩意兒。獲得的知識出了UI界就沒有任何傳承性。說不定這些東西在饅頭界早成曆史。