Thursday, June 11, 2009 - 文章

jQuery Textarea - 該用val()還是text()

網頁在FF下不正常,搞了半天才發現問題出在我使用val()指定Textarea的內容,畫面顯示看來一切OK,但經過clone()後,內容值卻消失了。

我整理出以下的範例做測試: (可用Mini jQuery Lab直接執行)

$("<div id='x1'><textarea id='t1'></textarea>" + 
"<textarea id='t2'></textarea></div>").appendTo("body");
$("#t1").text("AAA");
$("#t2").val("BBB");
alert("t1=" + $("#t1").text() + "/" + $("#t1").val());
alert("t2=" + $("#t2").text() + "/" + $("#t2").val());
alert($("#x1").html());

在IE下,不管用val()或text()設定,後續的讀取都正常;但在Firefox下,使用val()指定的值,畫面上會出現,但是用text()或是透過html()檢視時卻是字串。因此在Firefox中,如果你希望設定給textarea值出現在html()中或可以被clone(),請用text(...)設定。

且慢!! 事情如果這麼單純,那麼連小學生也會跨瀏覽器了。使用text()設定時得注意換行問題,若你在IE中下text("A\nA"),在顯示時只會呈現空一格而不會換行。依我測試的結果,在IE下text("A\r\A")得到顯示結果比較接近預期,但是text("A\rA")在Firefox中顯示時會換列,用text()取出時卻是連在一起的... 這... 這... 這...

最後,我採取了懦夫策略,在呼叫.clone()前做了這件事迴避問題:

$theDiv.find("textarea").each(function() { $(this).text($(this).val()); });

很醜,但看來是有效的! 如果有人有其他好點子,再分享一下吧!

【心得】沒有劈成一字馬的本事,不要跟別人說你會"跨"瀏覽器!

CODE-分贓程式的寫法

把一筆錢依特定的比例分給幾個人是我工作上常要處理的需求。由於金額必須四捨五入到元或分,因此常需面對除不盡的錢要設法攤掉的問題。例如100元平分給三個人,每人33元後,最後的1元要發給三人之一的幸運兒,變成一人34, 兩人33的分配結果。

以前年紀小不懂事,很直覺的想法是先用100*1/3四捨五入得到33把錢分一分,之後再跑一個迴圈(沒辦法,總不能打電話請這三個人過來猜拳吧?)把分剩的錢(總金額大、人數多時餘下數十上百元也是有可能滴)每次一元地發下去,直到發光為止。

說實在說,當初並不覺得這個寫法有什麼不對,直到有前輩指點了另一種更精巧的演算法,一口氣就能把錢攤到一毛不剩,省去分完一輪後處理餘數的麻煩。相形之下,原本的寫法挺笨拙的...

using System;
using System.IO;
using System.Threading;
 
public class CSharpLab
{
    public static void Test()
    {
        //100萬元要依比例分給3個人(四拾五入計算到元)
        int totalAmt = 1000000;
        int[] amt = new int[3];
        //三個人勢均力敵,100萬除以3會有餘1元的問題
        //且看以下的邏輯演法如何避免事後分攤的困擾
        decimal[] fact = new decimal[] { 1.2M, 1.2M, 1.2M };
        //先加總分配權重
        decimal factSum = 0;
        for (int i = 0; i < fact.Length; i++)
            factSum += fact[i];
        //使用以下邏輯分配, 會自然吸收掉四捨五入差額
        for (int i = 0; i < fact.Length; i++)
        {
            amt[i] = Convert.ToInt32(
                    Math.Round(totalAmt * fact[i] / factSum, MidpointRounding.AwayFromZero)
                );
            Console.WriteLine("{0} * {1} / {2} = {3}", totalAmt, fact[i], factSum, amt[i]);
            totalAmt -= amt[i];
            factSum -= fact[i];
        }
    }
}

執行結果是: (以上程式可以用Mini C# Lab直接測試)

1000000 * 1.2 / 3.6 = 333333
666667 * 1.2 / 2.4 = 333334
333333 * 1.2 / 1.2 = 333333

很棒吧! 它可以很自然順暢地在分攤過程中吸收掉四捨五入可能產生的差額,一步到位!

最近手上的案子在寫AJAX網頁,開始把這樣的概念搬到Javascript上實作。(以下程式可以使用Mini jQuery Lab測試)

var factor = [ 1.2, 1.2, 1.2 ], factorSum = 0;
var totalAmount = 1000000, amount = [];
for (var i = 0; i < factor.length; i++) factorSum += factor[i];
var r = "";
for (var i = 0; i < factor.length; i++)
{
    amount[i] = parseInt(Math.round(totalAmount * factor[i] / factorSum))
    r += totalAmount + " * " + factor[i] + " / " + factorSum + " = " + amount[i] + "\n";
    totalAmount -= amount[i];
    factorSum -= factor[i];
}
alert(r);

執行結果是

1000000 * 1.2 / 3.5999999999999996 = 333333
666667 * 1.2 / 2.3999999999999994 = 333334
333333 * 1.2 / 1.1999999999999995 = 333333

數字是對的,但明眼人已可看到其中暗藏殺機... 3.5999999999999996? 明明應該是3.6,因為Javascript裡只有浮點數,沒有像.NET decimal一樣分亳不差的精準數字型別,所以數字都是用逼近的。

俗話說得好: "算錢用浮點,遲早被人扁"! 雖然浮點運算的微小誤差多半要遇到天文數字計算或複雜的累乘累除時才會爆炸,但實事求事總是比較好,跟錢有關的東西,一丁點不對都會吵翻天。"插擠摳"(差一元)是每一個帳務會計程式開發人員的惡夢,為了避免未來某一天為了找一塊錢找到吐血,這裡還是花點心思防範未然。

var factor = [1.2, 1.2, 1.2], factorSum = 0;
var totalAmount = 1000000, amount = [];
function r2(n) { return parseFloat(n.toFixed(2)); }
for (var i = 0; i < factor.length; i++) 
    factorSum = r2(factorSum + factor[i]);
var r = "";
for (var i = 0; i < factor.length; i++)
{
    amount[i] = parseInt(Math.round(totalAmount * factor[i] / factorSum))
    r += totalAmount + " * " + factor[i] + " / " + factorSum + " = " + amount[i] + "\n";
    totalAmount -= amount[i];
    factorSum = r2(factorSum - factor[i]);
}
alert(r);

我假設factor的精準度到兩位,利用to.Fixed(2)的方法四捨五入加總及刪減過的結果。經過這番修正,結果好看多了!

1000000 * 1.2 / 3.6 = 333333
666667 * 1.2 / 2.4 = 333334
333333 * 1.2 / 1.2 = 333333

【心得】Javascript在數字處理上挺弱的,沒有decimal,型別不嚴謹,計算速度無法跟.NET等編譯式語言匹敵,連四捨五入到小數第幾位都得繞一圈。不過看在它可以輕易跨平台跨瀏覽器的分上,只好摸摸鼻子認了。Silverlight 3.0正式版快要誕生了,過陣子再來Survey它與AJAX網頁應用上的互補性。

搜尋

Go

<June 2009>
SunMonTueWedThuFriSat
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011
 
RSS
【工商服務】
最新回應

Tags 分類檢視
關於作者

一個醉心技術又酷愛分享的Coding魔人,十年的IT職場生涯,寫過系統、管過專案, 也帶過團隊,最後還是無怨無悔地選擇了技術鑽研這條路,近年來則以做一個"有為的中年人"自許。

文章典藏
其他功能

這個部落格


BlogLook Score and Rank

Syndication