第二十一课:js属性操作的兼容性问题

时间:2024-09-16 13:34:50

上一课主要讲了属性的概念,用法,固有属性和自定义属性的区别,class属性操作的方法等,这一课主要讲一些有关属性操作的兼容性问题。

IE6-IE8在一些表示URL的属性会返回补全的改过编码的路径,比如:href,action,background,cite,data,src,url等。我们只需要用getAttribute(href,2),就能很好的兼容各种浏览器了。标准浏览器不支持第二个参数,因此忽略第二个参数。而IE支持第二个参数,2代表只取出原字符串的值。

IE6-7下,对于form元素,调用getAttribute取属性值,会取到它的子元素input(ID值或name值等于属性值的input)。举个例子:

<form action="#">

  <input id="action" >

  <input name="length">

</form>

var el = document.getElementsByTagName("form")[0];

el.getAttribute("action");     //会取到<input id="action" >,IE6-7下

el.getAttribute("length");    //<input name="length">,IE6-7下

对于布尔属性,比如checked,IE6-IE8都无法取得用户预设值(<input type="radio" checked="chaojidan">),无论是通过elem.getAttribute("checked"),加上第二个参数2,还是elem.checked,都得不到"chaojidan"。

注释节点的nodeType等于8。

tabindex的取值问题

tabindex 属性规定元素的 tab 键控制次序(当 tab 键用于导航时)。<element tabindex="number">,number 规定元素的 tab 键控制次序(1 是第一个)。tabindex默认情况下只对表单元素与链接有效,对于这些元素,如果你没有显式设置tabindex,标准浏览器下,它会返回0,而对于div这样的默认不支持tabindex属性的,会返回-1.但IE6-8下都返回0.

在chrome中,如果你把固有属性从原型中删掉(delete操作符),会导致下次赋值时出错。

IE7不能修改表单元素的type属性。

这里讲一下判定一个元素的属性是否是布尔属性的方法:

function isBoolAttr(node,prop){

  var isBool = typeof node[prop] ==="boolean" && typeof defaultProp(node,prop)  ==="boolean";

  //如果元素的属性值是boolean类型,就证明它有机会是布尔属性。(还要排除用户强制执行node[prop]=true(false)的情况,这时不是元素默认的情况,而是用户强制赋值)

  return isBool;     

}  

function defaultProp(node,prop){

  return document.createElement(node.tagName)[prop];     //创建一个新的node元素,然后判断这个新节点的prop属性是否是boolean类型。

}

IE6-IE8下有一个href属性的bug,书上的例子没看懂,直接去http://gabriel.nagmay.com/2008/11/javascript-href-bug-in-ie/看了一下,我的理解是:

<a href="http://www.google.com">google.com</a> // fine - changing href will not change text
<a href="http://www.google.com">http://www.google.com</a> // bad - changing href will change the inner html text

address = "@pcc.edu addresses"

$(this).attr("href","http://www.pcc.edu/resources/web/forms/email/?to="+address);

以上代码的意思就是,当a链接的href属性值和a链接的text值相同时,当你改变href的属性值时,会把text值一起替换掉。

解决办法:

(1)text跟href的值不一样,就行了。(这里需要参考下面的讲解,其实如果text里面没有标签元素,同时以www.开头,或者包含@字符,都会引起上面的问题,这里所说的解决办法,只是针对以上例子的特殊情况,比如:google.com不等于http://www.google.com)

(2)在替换href时,我们用相对地址就行了。$(this).attr("href","/resources/web/forms/email/?to="+address);(这里只能解决text跟href相同的情况,不能解决a链接的text里面没有标签元素,同时以www.开头,或者包含@字符的情况)

(3)var linkHtml = $(this).html();先把text的值取出来,$(this).attr("href","http://www.pcc.edu/resources/web/forms/email/?to="+address).html(linkHtml);替换了href后,再恢复成原来的text的值。(这里也有问题,如果<a> 元素里面可能不是一段文本,而是一个 <img>,并且这个 <img> 上绑定了事件,那么,再将 innerHTML 写回去时,就会生成新的节点,原来绑定的事件也就失效了。)

司徒正美的书上有另外一个href属性的bug,在IE6-8下,链接地址:http://oldj.net/article/ie-bug-at-href-innerhtml/,我的理解是:

<a id="a-test" href="http://www.taobao.com">链接@BABY</a>

  setTimeout(function () {
    var a = document.getElementById("a-test");
    a.href = "http://rate.taobao.com/";

  }, 3000);

页面打开时,链接的内容为“链接@BABY”,一切正常,但当 js 函数执行,修改了 <a> 元素的 href 属性时,它的 innerHTML 值也变成了同样的值(http://rate.taobao.com/)。除了直接给 a.href 赋值外,使用 a.setAttribute 方法也是一样的效果。

解决办法:

var a = document.getElementById("a-test"), s;
s = a.innerHTML;   // 先记下 innerHTML
a.href = "http://rate.taobao.com/";
a.innerHTML = s;   // 再将 innerHTML 的值写回去

上面那种先记下 innerHTML 的值,修改 href 之后再将它写回去的做法是有隐患的。比如,<a> 元素里面可能不是一段文本,而是一个 <img>,并且这个 <img> 上绑定了事件,那么,再将 innerHTML 写回去时,就会生成新的节点,原来绑定的事件也就失效了。

一个更好的方法,只需在要写入的 href 的值前面加一个半角空格即可,而现代浏览器会安全地忽略掉 href 值前后的多余的空白字符。即:

a.href = " " + "http://rate.taobao.com/";

或者:

a.href = " http://rate.taobao.com/";    // 注意前面的空格

在 href 前面加一个空格的方式副作用太多,最后决定使用先加一个 <b></b> 节点然后再删除的方法。

var b,
inner_html = anchor.innerHTML;
if (inner_html && inner_html.indexOf("<") == -1) {  //如果a链接有值,并且它的innerHTML没有标签(子元素节点),就创建一个子节点
  b = doc.createElement("b");
  b.style.display = "none";
  anchor.appendChild(b);
}
anchor.href = href;

if (b) {
  anchor.removeChild(b);
}

上面代码的意思是:如果a标签的值里面包含其他元素节点,那么改变它的href属性值时,不会改变a标签的值。

进一步测试发现,如果a标签没有子元素, <a> 链接的内容(值)以“www.”开头,也会有这个问题。

总结以上所有讨论,得到chaojidan的结论:

在a标签没有子元素的情况下,凡是形如电子邮件(包含“@”字符)或网址(以“www.”开头)的字符串,在 IE6/7/8 中都有这种行为。同时,如果a链接的href属性值和a链接的text值相同,也会出现这样的行为。

以上属于超级变态,了解就行。

value的操作

select元素,它的value值就是其被选中的option孩子的value值。不过,select有两种形态。一种type为select-one,一种是select-multiple。option元素,它的value值可以使value属性的值,也可以是其中间文本的值。当用户没有显式设置value属性时,就可以使用其文本的值。取文本可以使用text,或者innerHTML这两种方法,但是option元素的text属性会做trim操作,支持所有浏览器。但只有旧版本的IE,在innerHTML会做trim操作,标准浏览器不会。如何判定option设置了value属性,IE6-7可以使用specified,标准浏览器使用hasAttribute来判定,具体操作请看上一课.

button,它的取值在IE6-7下是取它的text,IE8以及以上IE浏览器才会取value属性的值。标准浏览器,button标签只有当其为提交按钮,(type = submit)时,点击按钮,才会提交其value属性的值。

在safari5下,如果设置了select为disable,那么它的所有孩子都是disable的。

checkbox的value属性值默认为on,只有chrome是空字符串。

在旧版本IE下,form表reset后,select中的option不会改变为默认的selected值。举个例子:默认情况下,select的第一个option有selected属性,就代表第一个option显示在select中。当你做了操作后,把第二个option变成了selected,这时select中显示的是第二个option。这时你点击reset按钮,恢复默认值。在IE旧版本下,select中的option不会把selected值赋给第一个option,恢复默认。而不做任何改变,还是在第二个option中。但是其他浏览器就会恢复默认值,显示在第一个option中。

加油!