项目中自己挖的一个坑。调api获取导航菜单项,for循环获取每一项的相关值拼接html:
html +=
'<li>\
<a onclick="iframe_new(\''+arr[i].filename+'\',\''+arr[i].name+'\')">'+'<span>'+arr[i].name+'</span></a>\
</li>'
显然,当filename或name包含单引号时(filename还不太可能出现,name就很可能了,比如英文的 ‘s)点击这个a标签就会出错,因为实际解析成html之后iframe_new
这个函数的调用语句成了:
<a onclick="iframe_new('user_members_list.php','User's Members')">
<span>User's Members</span>
</a>
在chrome元素面板里看是这样的:
User's
中的单引号和U前面的单引号配对,导致iframe_new
这个函数的括号没有闭合,浏览器报语法错误:
Uncaught SyntaxError: missing ) after argument list
如果包含的是双引号:
<a onclick="iframe_new('user_members_list.php','User"s Members')">
<span>User's Members</span>
</a>
在chrome元素面板里就崩坏了:
浏览器报错:Uncaught SyntaxError: Invalid or unexpected token
我还经常在元素标签内加各种dataset属性,用于传递参数给绑定的事件handler,比如:
html += '<tr data-cid="'+arr[i].contactid+'" data-cname="'+arr[i].contactname+'" class="gotocontact">'
页面初始化时会有相应的事件委托,点击这个tr元素会根据data-cid打开新页面,data-cname作为页面标签的名称。类似地,当contactname包含引号时也会出现问题,区别在于前者是直接导致js语句截断,js执行报错,页面元素渲染没问题;后者是导致页面元素渲染出错,以及传给js的参数不完整,但执行不会报错:新页面能打开,但标签名称不完整。
其实这种时候,正确的做法是使用document.createElement
,然后给新元素的属性赋值:
var li = document.createElement('li');
var a = document.createElement('a');
var span = document.createElement('span');
a.onclick = function() {
iframe_new(arr[i].filename, arr[i].name)
}
span.innerHTML = arr[i].name;
li.append(a);
a.append(span);
document.querySelector('#list').append(li);
但是坑太多了,懒得一个个改。。。于是想办法修补。
最后的做法:
- 处理onclick内的引号时,双引号需要html转义,即替换成
"es;
。单引号需要js转义,即前面加反斜杠。
function escapeQuotes(raw) {
return raw
.replace(/"/g, ""es;")
.replace(/'/g, "\\'");
}
- 处理data-* 等不涉及js逻辑的属性值内的引号时,只需要把双引号html转义,因为包围属性值的引号是双引号,单引号不会配对。
html += '<tr data-cid="'+arr[i].contactid+'" data-cname="'+(arr[i].contactname).replace(/"/g, ""es;")+'" class="gotocontact">'