10/26/2018

解決 Chrome 下 Javascript 中文排序問題

解決 Chrome 下 Javascript 中文排序問題

A+
最近客戶反應,網頁上的中文排列順序,變得跟以前不太一樣,例如:

  • 原本會按 "上午"、"下午"、"晚上" 排序,現在變成 "上午"、"晚上"、"下午"
  • 原本會按 "一"、"二"、"三" 排序,現在變成 "二"、"三"、"一"

這件事的確離奇,不過讀者需要先瞭解的是,Javascript 能處理的中文排序,原本就跟我們的預期不太一樣,原理可參考「javascript 中文排序問題」,因此我也曾在「Blogger 樹狀標籤 V2.0」→ 「五、常見 FAQ」→ Q2 提醒,最好要排序的字串,前面加上英數字串,才能確保依照預期來排序。

然而客戶的網頁,以往中文排序都是在預期的狀態,直到最近才突然不正常,究竟是什麼原因引起,請見本篇的研究心得。

(圖片出處: pixabay.com)


一、只有 Chrome 異常


既然原本 JS 執行排序的邏輯沒問題,但現在的邏輯卻不一樣,第一個想到的,可能 JS 解析引擎是否不太一樣。由於 Chrome 常常更新版本,所以先測試 Chrome 狀況。

把同樣的網頁用 Chrome 以外的瀏覽器開啟,果然是正常的排序邏輯,只有 Chrome 的排序邏輯跟其他瀏覽器不同,那麼答案就出來了:

  • Chrome 新版本的 JS,改變了中文排序邏輯

如果是市佔率低的瀏覽器(例如 IE),還可以當作沒發生。偏偏 Chrome 是目前市面上最主要的瀏覽器,因此這是個必須立即解決的問題。



二、中文排序最佳解決方案


找到這篇文章,算是 JS 中文排序整理得最完整的:


大致整理一下排序邏輯的重點:

  • Big 5 編碼:
    • 這是大家習慣的排序邏輯,大致上依照筆畫、四聲、部首這樣的順序來排序
    • 但 JS 做不到這樣的排序,因為沒有內建此模組,除非自行另外建立 Big5 排序模組。
    • 但這很麻煩,而且每次執行 JS 都得另外載入這個龐大的模組。
  • 使用 sort() 排序:
    • 依照 Unicode 編碼的順序排序,算是效果很差的中文排序方式
  • sort() 結合 localeCompare() 不指定語系排序:
    • 每個瀏覽器內建設定不同,無法得到跨瀏覽器效果
    • Chrome 以外的瀏覽器,排序效果接近 Big 5,已經是可接受的狀態
    • Chrome 瀏覽器的效果等同 sort()
  • sort() 結合 localeCompare() 指定語系 zh-hant
    • 可得到跨瀏覽器一致的效果
    • 排序效果接近 Big 5(有些為差異,邏輯未知)
  • sort() 結合 localeCompare() 指定語系 zh-TW:
    • 效果等同指定語系 "zh",會依照簡體拼音排序



三、新版 Chrome 為何異常


藉由前述參考文章「黑暗執行緒」提供的範例程式碼,我們得到的其中一個結論是:

  • Chrome 使用 sort() 的效果,等於 localeCompare() 不指定語系排序效果

但現在使用 Chrome 執行修改後的範例程式碼,來看看為何最近 Chrome 更新後中文排序異常:

<script>
var str = "上下晚一二三四五";
var array = [];
for (var i = 0; i < str.length; i++)
array.push(str.substr(i, 1));
array.sort();
document.write("內建 sort()<br />")
document.write(JSON.stringify(array, null, " "));
document.write("<hr />")
array.sort(function(a, b) {
return a.localeCompare(b);
});
document.write("localeCompare 排序<br />")
document.write(JSON.stringify(array, null, " "));
document.write("<hr />")
array.sort(function(a, b) {
return a.localeCompare(b, "zh-TW");
});
document.write("localeCompare zh-TW 排序<br />")
document.write(JSON.stringify(array, null, " "));
</script>


執行結果如下:

內建 sort()
[ "一", "三", "上", "下", "二", "五", "四", "晚" ]

localeCompare 排序
[ "二", "三", "上", "四", "晚", "五", "下", "一" ]

localeCompare zh-TW 排序
[ "二", "三", "上", "四", "晚", "五", "下", "一" ]


這樣就找出原因了,新版 Chrome 之下,localeCompare() 不指定語系的排序效果,完全看不出邏輯,效果完全等同於設定了語系 "zh-TW" 之後的排序,也就是依照簡體拼音進行排序

我們可以歸納出,新版 Chrome 會自動幫 localeCompare() 設定語系,且幫繁體語系設定了 "zh-TW",偏偏這是錯誤的設定。

因為根據前述「黑暗執行緒」這篇參考文章,留言有讀者提到,"zh-TW" 會被自動判讀為簡體拼音編碼!

而正確的語系設定,應該為 "zh-hant" 才對。



四、跨瀏覽器一致的語法


所以最後總結一下,JS 使用 sort 對中文進行排序時,請使用以下的語法:

<script>
var array = ["上午", "下午", "晚上", "一", "二", "三"];
array.sort(function(a, b) {
return a.localeCompare(b, "zh-hant");
});
document.write("中文正確排序<br />");
document.write(array);
</script>

如此就能得到:
  • 跨瀏覽器一致的排序效果
  • 接近 Big5 編碼的排序效果


更多 Javascript 相關技巧:
如這篇文章對你有幫助,歡迎「分享」或給我個讚:

1 則留言:

張貼留言注意事項:

◎ 勾選「通知我」可收到後續回覆的mail!
◎ 請在相關文章留言,與文章無關的主題請至「Blogger 中文論壇」。
◎ 提問若無法提供足夠的資訊供判斷,可能會被無視。建議先參考這篇「Blogger 提問技巧及注意事項」。
◎ CSS 相關問題非免費諮詢,建議使用「Chrome 開發人員工具」尋找答案。
◎ 手機版相關問題請參考「Blogger 行動版範本的特質」→「三、行動版範本不一定能執行網頁版工具」。
◎ 非官方範本問題、或貴站為商業網站,請參考「Blogger 免費諮詢 + 付費諮詢
◎ 若留言要輸入語法,"<"、">"這兩個符號請用其他符號代替,否則語法會消失!
◎ 為了過濾垃圾留言,所有留言不會即時發佈,請稍待片刻。
◎ 本站「已關閉自刪留言功能」。

TOP