问题所在:
最近在学习使用JQ中ajax方法来渲染页面数据,填充表格,附上ajax获取数据填充表格的代码:
$.ajax({
//请求方式
type:'GET',
//发送请求的地址以及传输的数据
url:"https://www.easy-mock.com/mock/5bd5c6b982302f7129a2809c/example/mock",
//服务器返回的数据类型
async : false,
dataType:'json',
success:function(data) {
var thead = "<thead><tr><th><input type='checkbox' id='select-all'></th>"
var tbody = "<tbody>";
var th = "";
var tr = "";
//thead部分
$.each(data.data.colum,function (key,value) {
th = "<th>"+value+"</th>";
thead += th;
});//完成thead的组装,取的是data->data->colum的value
var rowArr = data.data.row;//行数组
var colArr = data.data.colum;//列数组
for(var i=0;i<rowArr.length;i++){
tr += "<tr><td><input type='checkbox'></td>";
var sumObj = data.data.dataContent[rowArr[i]];
if(sumObj){
for(var k in sumObj){
tr += "<td>"+sumObj[k]+"</td>";
}
}
tr += "<td class='operating'><button class='edit-button'><span>编辑</span></button > <button class='delete-button'><span>删除</span></button></td></tr>";
}
tbody += tr;
$('.table').append(thead+tbody);
},
error:function(jqXHR){
//请求失败函数内容
alert("ERROR");
}
});
从代码中可以看到我通过拼接字符串 来完成table的组装,其中使用ajax从后台获取数据。那么要实现一个自定义的表格,必定离不开组件、id和class的使用,而这些也是需要在拼接字符串中完成。
于是问题来了,我在通过ajax组装完成表格后,我之前全部的点击事件都失效了。附上全选框和排序功能的代码。
/*全选框*/
$(function(){
$("#select-all").click(function() {
if (this.checked){
$("tbody input[type='checkbox']").each(function(i){
$(this).prop("checked", true);
});
} else {
$("tbody input[type='checkbox']").each(function() {
$(this).prop("checked", false);
console.log($(this).attr("checked"));
});
}
})
});
/*点击列表头排序功能*/
$(function() { /*入口函数,等待dom渲染完毕之后才执行*/
var sort_direction=1; //排序标志,1为升序,-1为降序
$('th').each(function(i) {
$(this).click(function() {
if(sort_direction==1) {
sort_direction=-1;
}
else {
sort_direction=1;
}
//获得行数组
var trarr=$('table').find('tbody > tr').get();
//数组排序
trarr.sort(function(a, b) {
var col1=$(a).children('td').eq(i).text().toUpperCase();
var col2=$(b).children('td').eq(i).text().toUpperCase();
return(col1 < col2) ? -sort_direction: (col1 > col2) ? sort_direction: 0;
}
);
$.each(trarr, function(i, row) {
//将排好序的数组重新填回表格
$('tbody').append(row);
}
);
}
);
}
);
});
入口函数
先不说解决办法,我们看看加载事件:
JavaScript的入口函数要等到页面中所有资源(包括图片、文件)加载完成才开始执行。
jQuery的入口函数只会等待文档树加载完成就开始执行,并不会等待图片、文件的加载。
JQ和JS中还有load方法,用于等待页面中资源完全加载,再执行相应代码。JQ入口函数等待DOM加载完成便可以执行代码。
事件委托
事件委托,通俗地来讲,就是把一个元素响应事件(click、keydown......)的函数委托到另一个元素;
一般来讲,会把一个或者一组元素的事件委托到它的父层或者更外层元素上,真正绑定事件的是外层元素,当事件响应到需要绑定的元素上时,会通过事件冒泡机制从而触发它的外层元素的绑定事件上,然后在外层元素上去执行函数。
个人理解,事件委托是把一个事件委托到(绑定到)某一元素上,等到此元素被捕获的时候可以完成对应事件。
没错,事件委托的其中一个优点就是动态绑定事件,可以为“未来元素”绑定好事件。
在很多时候,我们需要通过 AJAX 或者用户操作动态的增加或者去除列表项元素,那么在每一次改变的时候都需要重新给新增的元素绑定事件,给即将删去的元素解绑事件;
如果用了事件委托就没有这种麻烦了,因为事件是绑定在父层的,和目标元素的增减是没有关系的,执行到目标元素是在真正响应执行事件函数的过程中去匹配的;
所以使用事件在动态绑定事件的情况下是可以减少很多重复工作的。
问题初探
事实上事件的机制不难理解,JQ中无非就是几个方法的实现:
- $.on: 基本用法: $('.parent').on('click', 'a', function () { console.log('click event on tag a'); }),它是 .parent 元素之下的 a 元素的事件代理到 $('.parent') 之上,只要在这个元素上有点击事件,就会自动寻找到 .parent 元素下的 a 元素,然后响应事件;
- $.delegate: 基本用法: $('.parent').delegate('a', 'click', function () { console.log('click event on tag a'); }),同上,并且还有相对应的 $.delegate 来删除代理的事件;
- $.live: 基本使用方法: $('a', $('.parent')).live('click', function () { console.log('click event on tag a'); }),同上,然而如果没有传入父层元素 $(.parent),那事件会默认委托到 $(document) 上;(已废除)
我试过用live(存活)方法修改全选框功能的点击触发,确实有效,但是我发现对于我的排序功能,需要获取每一个表格th,然而渲染完成前后排序代码已经开始执行了,当然获取不到,这就不是点击事件那么简单了。
异步和同步
注意我上面加粗的那段文字,表格通过ajax渲染的同时,其他的代码一直在执行,这就是ajax异步的一种体现。
异步:在ajax请求的同时其他代码仍然可以执行。
同步:与排队同理,ajax请求完成之前其他的代码处于排队状态,页面假死,等待ajax完成之后执行其他代码。
解决方法
所以理论上需要将ajax换为同步通信,就可以让其他功能不受影响。
async : false; //同步
只要一行代码,当然,功能需要在同步+dom加载完成的基础上才可以真正实现,也就是click事件还是要放在入口函数之中。
有没有其他办法呢?据我看来还有很多可行的办法,我还没有试过,比如说在组装表格的时候为标签加上onClick,然后把功能写进function之中,点击事件执行function。
参考文献(知乎):https://zhuanlan.zhihu.com/p/26536815