事情是这样的,因为最近在学习前端的知识,学完了需要巩固下知识,所以在网上找的题来做。遇到这样的题。(包含遇到问题的详细过程,觉得例子无趣的可直接跳转结论)
其代码也很简单,设计表格和事件绑定,最近学习了jquery,很快将代码写出来。
说明下,工程上不要使用行间样式和内嵌js代码。应该做到HTML(结构)+CSS(样式)+JavaScript(控制)分离。我是为了图方便一页代码描述完问题才这样写的。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<style>
td{
width: 25%;
}
</style>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js"></script>
<body>
<table border="1" cellpadding="0" cellspacing="0" style="width: 100%;">
<tr>
<td>物品</td>
<td>数量</td>
<td>金额</td>
<td>备注</td>
</tr>
<tr>
<td><span id="clothSpan" style="cursor: pointer">+</span> <span>服装</span></td>
<td>100件</td>
<td>12000元</td>
<td></td>
</tr>
<tr style="display: none">
<td></td>
<td colspan="3" style="width: 75%">
<div>
<table border="1" style="position: relative;width: 500px;height: 100px;left: 30%;margin-top: 25px">
<tr>
<td>明细</td>
<td>数量</td>
<td>单价</td>
<td>备注</td>
</tr>
<tr>
<td>外套</td>
<td>10</td>
<td>300</td>
<td></td>
</tr>
<tr>
<td>裤子</td>
<td>90</td>
<td>100</td>
<td></td>
</tr>
</table>
</div>
</td>
</tr>
<tr>
<td><span id="fruitSpan" style="cursor: pointer">+</span> <span>水果</span></td>
<td>100KG</td>
<td>290元</td>
<td></td>
</tr>
<tr style="display: none">
<td></td>
<td colspan="3" style="width: 75%">
<table border="1" style="position: relative;width: 500px;height: 100px;left: 30%;margin-top: 25px">
<tr>
<td>明细</td>
<td>重量</td>
<td>单价</td>
<td>备注</td>
</tr>
<tr>
<td>西瓜</td>
<td>10</td>
<td>20</td>
<td></td>
</tr>
<tr>
<td>柚子</td>
<td>9</td>
<td>10</td>
<td></td>
</tr>
</table>
</td>
</tr>
</table>
</body>
<script>
$("#clothSpan").click(function () {
var tr = this.parentElement.parentElement;
var nextTr = tr.nextElementSibling;
if (nextTr.style.display == "none") {
nextTr.style.display = "table-row";
tr.cells[0].firstChild.textContent = "-";
}else{
nextTr.style.display = "none";
tr.cells[0].firstChild.textContent = "+";
}
});
$("#fruitSpan").click(function () {
var tr = this.parentElement.parentElement;
var nextTr = tr.nextElementSibling;
if (nextTr.style.display == "none") {
nextTr.style.display = "table-row";
tr.cells[0].firstChild.textContent = "-";
}else{
nextTr.style.display = "none";
tr.cells[0].firstChild.textContent = "+";
}
});
</script>
</html>
可以看到,两个按钮的逻辑操作代码是一模一样的,这破坏了我们代码复用 原则,所以为了提高代码利用率我们做如下更改。
<script>
var showElement = function(spanElement){
var tr = spanElement.parentElement.parentElement;
var nextTr = tr.nextElementSibling;
if (nextTr.style.display == "none") {
nextTr.style.display = "table-row";
tr.cells[0].firstChild.textContent = "-";
}else{
nextTr.style.display = "none";
tr.cells[0].firstChild.textContent = "+";
}
};
$("#clothSpan").click(showElement(document.getElementById("clothSpan")));
$("#fruitSpan").click(showElement(document.getElementById("fruitSpan")));
</script>
问题出现
上述代码中,因为我们不知道该代码中this指向谁,所以需要将调用该方法的控件对象经过参数传递过来。
按理来说,逻辑应该没问题的,对于初学jquery的我,还沾沾自喜就认为jquery就这。但运行后结果出现了大问题,隐藏表格直接展开了,并且在点击缩小按钮根本没反应。我还仔细的过了以一遍代码和变量追踪,始终没发现逻辑上有任何错误,在这里烦恼了半个小时。最终在网上查阅相关资料和再看jquery和js的基础知识才找到问题所在。
问题分析
问题就出现在$("#clothSpan").click(showElement(document.getElementById("xxx"))) 这里。
仔细阅读JavaScript基础,发现一旦函数带了()就代表运行,也就是说一旦js遇到函数名+(),它就会去执行这个函数。
所以我们上述的showElement 方法仅在绑定的时候运行了一次,然后我们将showElement 返回值(无返回值undefined)绑定给控件的点击事件上,然后不论你后面怎么点击都没有用,因为点击事件没绑定上正确的函数。
那么,参数在jquery中华到底应该怎么正确地传递呢?
解决方法
<script>
var showElement = function(event){
console.log(event);
console.log(event.data.spanElement);
console.log(event.data.aaa);
console.log(event.data.bbb);
var tr = event.data.spanElement.parentElement.parentElement;
var nextTr = tr.nextElementSibling;
if (nextTr.style.display === "none") {
nextTr.style.display = "table-row";
tr.cells[0].firstChild.textContent = "-";
}else{
nextTr.style.display = "none";
tr.cells[0].firstChild.textContent = "+";
}
};
var arg1 = {
spanElement : document.getElementById("clothSpan"),
aaa : 1,
bbb : "qwe"
}
$("#clothSpan").click(arg1, showElement);
var arg2 = {
spanElement : document.getElementById("fruitSpan"),
}
$("#fruitSpan").click(arg2, showElement);
</script>
我们可以查看jquery源码里的click 函数。click(data,fn) 中的data其实是json对象,取的时候,只能通过当前的事件源来取,data是默认放在event中的,所以这里的data是event.data,引用的时候也使用event.data.xxx。这样jquery中函数传参问题得到了解决。
例如
$("select#test").change({msg: "ok"}, function(event) {
myHandler(event.data.msg);
});
结论
- JavaScript中函数名+()就代表着函数运行,所以绑定函数的时候一定不要加(),否则会将返回值进行绑定。直接将函数名看作一个对象,看作一个指向函数的指针,直接用就是了,加()就是无参运行。
- JQuery中绑定事件带有参数的函数时,可以借助自带的event,待绑定函数声明时参数带有event,其中传参时参数以json对象(键值对)的格式存在。函数内引用的时候通过event.data.xxx引用。形如
$("选择器").click(以json对象形式存在的参数, 待绑定函数名称);
纠正一个错误
上述问题中,发现一个理解上的小错误。在次说明下。
其实上述问题可以不用通过传参解决,直接$("#clothSpan").click(showElement); 就解决了,上述之所以传参是我以为,该方法不知道谁调用它,所以需要将调用它的控件对象传递过去,其实不用!
方法无论如何不会自己运行的,一定有调用(call)它,它才能运行,否则根本不会运行,就像工具一样只有有人用它它才能动起来,否则放在那里永远都不可能动起来,所以函数里this是存在的,追溯到最后都没人调用它就是window调用它。是我搞错了,不用传参,直接传递方法名过去,也能获得到this,函数没有人调用怎么能运行起来吗?jquery前面都已经选择定位到那一个了。
同时,这个错误让我更加坚定了JS中函数名是指向函数的指针,传递函数不带(),带()就是无参运行!
|