此問題相關信息(我不放在最前面,似乎有些朋友會找不到的樣子.)
IE10+, Safari5.17+, Firefox4.0+,Opera12+, Chrome7+ 已經按新標准實現. 所以就沒有這個問題了.
參考標准 :
http://www.w3.org/html/ig/zh/wiki/HTML5/tokenization 新標准明確提到,如果實體后面遇到的不是;且下一個是= 那么就不處理的.就是為了解決這個坑爹的問題的.
我們來看demo :
<a href="http://www.baidu.com?a=1®=2®_a=3" >悲劇</a>
部分瀏覽器(對應上面已經按新標准實現的版本之下的,各個瀏覽器.)
點上面的鏈接, 會自動把 ® 轉意成® (部分瀏覽器會自動對轉意后的字符進行編碼) .
這個bug.的本質,就是當HTML中出現相關HTML實體(HTML character entity)時.就自動轉意處理了. 所以理論上, 用腳本,動態創建的資源則沒有這個問題,比如 new Image().src = 'http://www.baidu.com?a=1®=2'; 甚至動態創建的iframe.亦如此.
IE9- 有兩個問題比其他瀏覽器更嚴重:
1. 用腳本跳轉當前頁比如
location.href = xxx,或 location.replace(xxx) .又或者是調用
window.open(xxx);如果查詢字符串中包含這些html實體, 仍然會觸發這個問題...
2. ,參見標准, 你會知道實體+"其他字符" , "其他字符中",哪些與實體連接在一起,是沒有這個問題的. 比如 ®a , ®1 其中a, 1 與 ® 連接就不會有這種問題,從標准角度,甚至是 ®_a 也不應有問題. 但是IE9-又一次打敗了我們. 至於其他特殊字符如 # ~ 等.在各個瀏覽器中表現各異. 考慮我們在設計字段名時,不大可能出現那些字符.我們也不再糾結其他瀏覽器在此處實現的差異.
所以,理論上,這個問題應該是后端的同學,在輸出html時.更加要注意的問題. 而前端同學,要注意的則是跳轉或彈窗時的url中是否有相關的字段包含一個無分號即為html實體的情況.
至於IE為啥這么特殊...我也沒想明白...
那么,無論后端同學也好,前端同學也罷,我們可能更改已經定好的字段成本比較高. 所以其實最妥善的辦法,應該是這樣子: (感謝 @辰光未然 的提醒.)
var fixURL = function (url) {
return url.replace(/&/g,'&');
};
//使用fixURL 去替換url中的&.然后再輸出給html, 或者跳轉鏈接,又或者彈窗... 當然,前端的同學在js代碼中之所以要這樣做.主要是受IE的拖累...
那么大概,很多HTML 實體都會出問題:
這個表里, 沒有分號結尾的,都是隱患... 也就是下面這106個: (感謝 @kenny 提供的最新的list 地址. 我花了點時間寫了個腳本.把需要處理的,都抓了出來.)
我們可以用下面這個腳本來幫忙做檢測 :
var checkURL = function () {
var list = [ //106
'Á',
'á',
'Â',
'â',
'´',
'Æ',
'æ',
'À',
'à',
'&',
'&',
'Å',
'å',
'Ã',
'ã',
'Ä',
'ä',
'¦',
'Ç',
'ç',
'¸',
'¢',
'©',
'©',
'¤',
'°',
'÷',
'É',
'é',
'Ê',
'ê',
'È',
'è',
'Ð',
'ð',
'Ë',
'ë',
'½',
'¼',
'¾',
'>',
'>',
'Í',
'í',
'Î',
'î',
'¡',
'Ì',
'ì',
'¿',
'Ï',
'ï',
'«',
'<',
'<',
'¯',
'µ',
'·',
' ',
'¬',
'Ñ',
'ñ',
'Ó',
'ó',
'Ô',
'ô',
'Ò',
'ò',
'ª',
'º',
'Ø',
'ø',
'Õ',
'õ',
'Ö',
'ö',
'¶',
'±',
'£',
'"',
'"',
'»',
'®',
'®',
'§',
'­',
'¹',
'²',
'³',
'ß',
'Þ',
'þ',
'×',
'Ú',
'ú',
'Û',
'û',
'Ù',
'ù',
'¨',
'Ü',
'ü',
'Ý',
'ý',
'¥',
'ÿ'
];
return function (url) {
var l = list;
var i = l.length;
var matchIndex;
var current;
var nextchar;
var errors = [];
for (; i--;){
matchIndex = url.indexOf(l[i]);
current = l[i];
if(matchIndex > -1){
if((current === '&' || current === '&') && url.charAt(matchIndex + 4) === ';'){
//如果是 & 或 & 我們就認為是故意要輸出 & ,比如是一個調用fixURL方法修正過的URL.里面的& 會被我們替換為 amp;
//所以,我們要跳過它,去檢查后面.
continue;
}
nextchar = url.charAt(matchIndex + current.length);
if(!/[a-zA-Z0-9]/.test(nextchar)){
//此處我們只要發現任意一個 ,如 ®后面緊隨字符不在 a-z,A-Z,0-9范圍內.就算有問題.
//這樣處理實際和標准的細節以及瀏覽器實現有細微差異. 但是本着任何瀏覽器來跑case,都能發現潛在威脅的原則.和實現復雜度的考慮.
// 我們姑且粗暴的這樣處理了. 似乎還不錯.
errors.push(current + nextchar);
}
}
}
if(errors.length){
throw Error('contains : \n' + errors.join('\n'));
}
};
}();
test case 1:
var url = '//www.baidu.com?a=1&=2<=3®=4';
document.onclick = function () { //IE9-好了.證明我們的修正是ok的了.
window.open(fixURL(url))
};
test case 2:
var url = '//www.baidu.com?a=1&=2<=3®=4';
try{
checkURL(url);
}catch(e){
alert(e.message)
}
