麻烦更新内容,保留原来的东西。你让原来的答题者,情何以堪
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
.active{ background: red}
</style>
</head>
<body>
<input type="button" vlaue='anniu'>
<input type="button" vlaue='anniu'>
<input type="button" vlaue='anniu'>
<script>
var oBtn=document.getElementsByTagName('input');
for(var i=0; i<oBtn.length;i++){
oBtn[i].onclick=function(){
//oBtn[i].className='active' //为什么这样添加属性会报错,
this.className='active';
}
}
</script>
</body>
</html>
更新之后代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
#p1 .active{background:yellow;}
#p1 p{width: 200px;height: 200px;background: #ccc;border: 1px solid #999;display: none;}
</style>
<script>
window.onload=function(){
var op=document.getElementById('p1');
var aBtn=op.getElementsByTagName('input');
var ap=op.getElementsByTagName('p');
for(var i=0; i<aBtn.length;i++){
aBtn[i].index=i;
aBtn[i].onclick=function(){
for(var i=0;i<aBtn.length;i++){
//而这里却不会报错。
aBtn[i].className="";
ap[i].style.display='none';
}
//为什么这里用aBtn[i].className='active'添加报错 SCRIPT5007: 无法设置未定义或 null 引用的属性“className”
this.className='active';
ap[this.index].style.display="block"
}
}
}
</script>
</head>
<body>
<p id='p1'>
<input class="active" type="button" value='教育'>
<input type="button" value='金融'>
<input type="button" value='招生'>
<input type="button" value='出国'>
<p style='display:block;'>111</p>
<p>222</p>
<p>333</p>
<p>444</p>
</p>
</body>
</html>
高洛峰2017-04-10 17:13:09
这是一个新手经常会遇到的问题。为什么会这样呢?一步一步来。先看下面的代码
for(var i=0; i<oBtn.length;i++){
oBtn[i].onclick=function(){
console.log(i);
}
}
你会发现所有的输出都是2,也就是oBtn.length
。
当click
事件发生的时候,会执行代码console.log(i)
。
控制台会打印i
控制台会打印i
控制台会打印i
那么i
又是什么?不是你想象的数字。这里i
是一个变量,它最终打印出是这个变量的值。
这个值是oBtn.length
,因为循环语句
for(var i=0; i<oBtn.length;i++){
}
执行完毕后,i
已经变成了oBtn.length
。因此,在内部使用oBtn[i]
是不行的。
以下内容,初学者要耐心
如果,非要使用oBtn[i]
,那么就必须在oBtn[i].onclick
赋值的时候,用变量把i
的每次值保存起来。
for(var i=0; i<oBtn.length;i++){
oBtn[i].onclick=(function(j){
return function(){
oBtn[j].className='active'
}
})(i)
}
这里,我们执行了一个匿名函数
,并且把i
,传给了这个函数。然后,这个匿名函数返回一个函数,作为click
属性的值。当点击事件发生的时候,就会触发这个函数。
当匿名函数返回一个函数的时候,会形成一个闭包。被返回的函数
(内部函数)可以访问匿名函数
(外部函数)的参数,以及两个函数中间的变量。
// 这是把i作为参数传给匿名函数,并执行函数。
(匿名函数)(i)
匿名函数 = function(j){
return function(){
oBtn[j].className='active'
}
}
当i传过来的时候,j = i
;当i
的值发生变化的时候,j的值是不会跟着变的。这与下面代码是一个意思
var a, b = 5;
a = b;
b = 6;
a, b // 5, 6; a不会跟着变了
这样每一个不同的i
的值就被保存下来了。也许你会有疑问,每一次都执行了j = i
,为什么j
的值没有变化呢?因为每次循环的时候,匿名函数
都是新的,包括参数
、变量
等。
显然,上一个j
与下一个j
已经不是同一个了。
ringa_lee2017-04-10 17:13:09
你的这段代码仅在页面加载时执行了一次,而事件绑定的监听函数都是后触发的
换句话说,如果是
oBtn[i].className = 'active';
其实等价于
oBtn[3].className = 'active';
你这里oBtn的下标总共也就0, 1, 2,所以当然会出错
ringa_lee2017-04-10 17:13:09
闭包的问题,循环给每个元素绑定了点击事件,但外部变量i在循环结束已经是3。匿名函数里的oBtn[i] = oBtn[3],已经超出元素。所以你每次都要保存变量的值。
for(var i=0; i<oBtn.length;i++){
oBtn[i].index = i;
oBtn[i].onclick=function(){
oBtn[this.index].className='active';
}
}
哦,你现在又变成4个了。第二个循环没报错是因为你又把i从零赋值开始了,出了循环i的值是4了,你再用oBtn[i].className = 'active';已经越界,获取的元素是NULL,肯定报错了啊。
迷茫2017-04-10 17:13:09
典型的闭包问题。当click发生时i已经变成了3,数组下标越界。
1,使用闭包把i保存在闭包的作用域下
2,使用this
3, 使用event.target