最近客戶反應,網頁上的中文排列順序,變得跟以前不太一樣,例如:
這件事的確離奇,不過讀者需要先瞭解的是,Javascript 能處理的中文排序,原本就跟我們的預期不太一樣,原理可參考「javascript 中文排序問題」,因此我也曾在「Blogger 樹狀標籤 V2.0」→ 「五、常見 FAQ」→ Q2 提醒,最好要排序的字串,前面加上英數字串,才能確保依照預期來排序。
然而客戶的網頁,以往中文排序都是在預期的狀態,直到最近才突然不正常,究竟是什麼原因引起,請見本篇的研究心得。
(圖片出處: pixabay.com)
既然原本 JS 執行排序的邏輯沒問題,但現在的邏輯卻不一樣,第一個想到的,可能 JS 解析引擎是否不太一樣。由於 Chrome 常常更新版本,所以先測試 Chrome 狀況。
把同樣的網頁用 Chrome 以外的瀏覽器開啟,果然是正常的排序邏輯,只有 Chrome 的排序邏輯跟其他瀏覽器不同,那麼答案就出來了:
如果是市佔率低的瀏覽器(例如 IE),還可以當作沒發生。偏偏 Chrome 是目前市面上最主要的瀏覽器,因此這是個必須立即解決的問題。
找到這篇文章,算是 JS 中文排序整理得最完整的:
大致整理一下排序邏輯的重點:
藉由前述參考文章「黑暗執行緒」提供的範例程式碼,我們得到的其中一個結論是:
但現在使用 Chrome 執行修改後的範例程式碼,來看看為何最近 Chrome 更新後中文排序異常:
執行結果如下:
這樣就找出原因了,新版 Chrome 之下,localeCompare() 不指定語系的排序效果,完全看不出邏輯,效果完全等同於設定了語系 "zh-TW" 之後的排序,也就是依照簡體拼音進行排序。
我們可以歸納出,新版 Chrome 會自動幫 localeCompare() 設定語系,且幫繁體語系設定了 "zh-TW",偏偏這是錯誤的設定。
因為根據前述「黑暗執行緒」這篇參考文章,留言有讀者提到,"zh-TW" 會被自動判讀為簡體拼音編碼!
而正確的語系設定,應該為 "zh-hant" 才對。
所以最後總結一下,JS 使用 sort 對中文進行排序時,請使用以下的語法:
如此就能得到:
- 原本會按 "上午"、"下午"、"晚上" 排序,現在變成 "上午"、"晚上"、"下午"
- 原本會按 "一"、"二"、"三" 排序,現在變成 "二"、"三"、"一"
這件事的確離奇,不過讀者需要先瞭解的是,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 相關技巧: