第一种for循环判断全选和不选,但是
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> <style> table { width: 800px; margin: 0 auto; } table, th, td { border: 2px solid black; border-collapse: collapse; } thead th { text-align: center; } thead th:nth-child(1) input { cursor: pointer; } tbody td { text-align: center; } tbody td:nth-child(1) input { cursor: pointer; } .active { background: #ccc; } </style> </head> <body> <table id="table"> <thead> <tr> <th><input type="checkbox" name="" class="thIpt"></th> <th>#</th> <th>姓名</th> <th>昵称</th> <th>年龄</th> <th>性别</th> <th>爱好</th> <th>操作</th> </tr> </thead> <tbody> <!-- <tr class="cont"> <td><input type="checkbox" name="" id="" class="sel"></td> <td>1</td> <td>刘备</td> <td>小刘</td> <td>58</td> <td>男</td> <td>撩妹,装逼,编草鞋</td> <td><button>X</button></td> </tr> --> </tbody> </table> <script> let data = [ { "name": "刘备", "nickname": "小刘", "age": 58, "gender": "男", "hobby": ["撩妹", "装逼", "编草鞋"] }, { "name": "关羽", "nickname": "关二", "age": 47, "gender": "男", "hobby": ["耍大刀", "变脸", "喝酒"] }, { "name": "张飞", "nickname": "张三", "age": 45, "gender": "男", "hobby": ["打架", "喝酒", "耍流氓"] }, { "name": "赵云", "nickname": "赵四", "age": 22, "gender": "男", "hobby": ["打架", "喝酒", "耍帅"] }, { "name": "貂蝉", "nickname": "美女", "age": 20, "gender": "女", "hobby": ["撩汉", "化妆"] }, { "name": "小乔", "nickname": "乔二", "age": 18, "gender": "女", "hobby": ["弹琴", "唱歌", "撩周瑜"] } ] // 获取渲染数据用到的元素 let table = document.querySelector('#table') let tbody = document.querySelector('tbody') // 1、渲染数据 render() //渲染函数 forEach函数 function render() { data.forEach(function (item, i) { let { name, nickname, age, gender, hobby } = item tbody.innerHTML += ` <tr class="cont" idx="${i}"> <td class="tdIpt"><input type="checkbox" class="sel"></td> <td class="tdIpt">${i + 1}</td> <td class="tdIpt">${name}</td> <td class="tdIpt">${nickname}</td> <td class="tdIpt">${age}</td> <td class="tdIpt">${gender}</td> <td class="tdIpt">${hobby}</td> <td class="tdIpt"><button class="del">X</button></td> </tr>` }) } // //渲染函数 map函数 // function render() { // data.map(function (item, i) { // let { name, nickname, age, gender, hobby } = item // tbody.innerHTML += ` // <tr class="cont" idx="${i}"> // <td class="tdIpt"><input type="checkbox" class="sel"></td> // <td class="tdIpt">${i + 1}</td> // <td class="tdIpt">${name}</td> // <td class="tdIpt">${nickname}</td> // <td class="tdIpt">${age}</td> // <td class="tdIpt">${gender}</td> // <td class="tdIpt">${hobby}</td> // <td class="tdIpt"><button>X</button></td> // </tr>` // }).join("") // // 注意:用map渲染一定要用join转换,去掉里面的逗号 // } // 实现 全选/不选 功能 // 获取元素 let cont = document.querySelectorAll('.cont') let sel = document.querySelectorAll('.sel') let thIpt = document.querySelector('.thIpt') // 1、用for循环 实现全选和不选 thIpt.onclick = function () { // console.log(this.checked) //thIpt的状态,如果选中为true,否则为false for (let i = 0; i < sel.length; i++) { sel[i].checked = this.checked // 这里的this指向thIpt // console.log(this) //this指向thIpt // 判断是否选中,如果选中,让下面的父元素的父元素 tr 背景色改变 if (this.checked) { sel[i].parentNode.parentNode.className = "active" } else { sel[i].parentNode.parentNode.className = "" } } // // 注意:如果用下面的方法 + this 就不能实现全选和不选 // sel.forEach(function (item, i) { // item.checked = this.checked // console.log(this) // 这里的this指向window // }) } // 2、下面复选框需要全部选中, 上面全选才能选中做法: // 给下面所有复选框绑定点击事件,每次点击,都要循环查看下面所有的复选框是否有没选中的, // 如果有一个没选中的, 上面全选就不选中。 for (let i = 0; i < sel.length; i++) { sel[i].onclick = function () { // flag 控制全选按钮是否选中 let flag = true; // 让sel的父元素的父元素tr 的背景色改变 // console.log(sel[i]) sel[i].parentNode.parentNode.className = "active" // 每次点击下面的复选框都要循环检查下面所有的小按钮是否全被选中 for (let j = 0; j < sel.length; j++) { // console.log(!sel[i].checked) // 判断sel[i].checked是否为真,选中为true,没选中为false // 当有一个为false时,也就是没选中,!sel[i].checked为true 执行下面语句 // 把 flag 状态改为 false if (!sel[j].checked) { flag = false; // 让sel的父元素的父元素tr 的背景色改变 sel[j].parentNode.parentNode.className = "" // break; // 退出for循环 这样可以提高执行效率 因为只要有一个没有选中,剩下的就无需循环判断了 } } // 根据上面for循环的结果来判断全选按钮 是否选中 thIpt.checked = flag; } } // // 1、用forEach遍历 实现全选和不选: // thIpt.onclick = function () { // // 全选按钮 // // 判断thInt.checked是否选中,如果选中 为true // if (thIpt.checked) { // sel.forEach((item, i) => { // item.checked = true // // 让sel的父元素的父元素tr 的背景色改变 // item.parentNode.parentNode.className = "active" // }) // // 全不选 // } else { // sel.forEach((item, i) => { // item.checked = false // // 让sel的父元素的父元素tr 的背景色改变 // item.parentNode.parentNode.className = "" // }) // } // } // // 2、下面复选框需要全部选中, 上面全选才能选中做法: 用forEach循环实现 // sel.forEach(function (item, i) { // item.onclick = function () { // var flag = true // // 让sel的父元素的父元素tr 的背景色改变 // item.parentNode.parentNode.className = "active" // // 再循环遍历所有的sel 判断是否选中下面的复选框 // sel.forEach(function (item, i) { // if (!item.checked) { // flag = false // console.log(item) // // 让sel的父元素的父元素tr 的背景色改变 // item.parentNode.parentNode.className = "" // // 因为只要有一个没有选中,剩下的就无需循环判断了,但是forEach会遍历所有 效率不如for循环 // } // }) // // 根据上面forEach循环的结果来判断全选按钮 是否选中 // thIpt.checked = flag; // } // }) // if (!true) { // console.log(1) // } else { // console.log(2) //打印2 // } </script> </body> </html>需求: 1、当全选时,下面全部选中,并让下面的tr背景色改变 2、当下面某一个没有选中时,最上面的全选没有选中,当下面都选中时,最上面的全选自动选中 3、tr的选中的状态同时决定背景色,选中就有背景色,没选中就没有
但是上面的表格,在静态时功能能实现,如果考虑到动态删除表格的数据(比如删除某一行就可能有问题)
功能实现: 1、全选/不选 功能 2、点击任意单元格,选中当前行 ,并勾选复选框 3、选中的时候高亮 4、删除某行后,让里面第二列数据自动更新 ,并且以上功能都能实现
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> <style> table { width: 800px; margin: 0 auto; } table, th, td { border: 2px solid black; border-collapse: collapse; } thead th { text-align: center; } thead th:nth-child(1) input { cursor: pointer; } tbody td { text-align: center; } tbody td:nth-child(1) input { cursor: pointer; } .active { background: #ccc; } </style> </head> <body> <table id="table"> <thead> <tr> <th><input type="checkbox" name="" class="thIpt"></th> <th>#</th> <th>姓名</th> <th>昵称</th> <th>年龄</th> <th>性别</th> <th>爱好</th> <th>操作</th> </tr> </thead> <tbody> <!-- <tr class="cont"> <td><input type="checkbox" name="" id="" class="sel"></td> <td>1</td> <td>刘备</td> <td>小刘</td> <td>58</td> <td>男</td> <td>撩妹,装逼,编草鞋</td> <td><button>X</button></td> </tr> --> </tbody> </table> <script> let data = [ { "name": "刘备", "nickname": "小刘", "age": 58, "gender": "男", "hobby": ["撩妹", "装逼", "编草鞋"] }, { "name": "关羽", "nickname": "关二", "age": 47, "gender": "男", "hobby": ["耍大刀", "变脸", "喝酒"] }, { "name": "张飞", "nickname": "张三", "age": 45, "gender": "男", "hobby": ["打架", "喝酒", "耍流氓"] }, { "name": "赵云", "nickname": "赵四", "age": 22, "gender": "男", "hobby": ["打架", "喝酒", "耍帅"] }, { "name": "貂蝉", "nickname": "美女", "age": 20, "gender": "女", "hobby": ["撩汉", "化妆"] }, { "name": "小乔", "nickname": "乔二", "age": 18, "gender": "女", "hobby": ["弹琴", "唱歌", "撩周瑜"] } ] // 获取渲染数据用到的元素 let table = document.querySelector('#table') let tbody = document.querySelector('tbody') // 1、渲染数据 render() //渲染函数 forEach函数 function render() { data.forEach(function (item, i) { let { name, nickname, age, gender, hobby } = item tbody.innerHTML += ` <tr class="cont" idx="${i}"> <td class="tdIpt"><input type="checkbox" class="sel"></td> <td class="tdIpt">${i + 1}</td> <td class="tdIpt">${name}</td> <td class="tdIpt">${nickname}</td> <td class="tdIpt">${age}</td> <td class="tdIpt">${gender}</td> <td class="tdIpt">${hobby}</td> <td class="tdIpt"><button class="del">X</button></td> </tr>` }) } // //渲染函数 map函数 // function render() { // data.map(function (item, i) { // let { name, nickname, age, gender, hobby } = item // tbody.innerHTML += ` // <tr class="cont" idx="${i}"> // <td class="tdIpt"><input type="checkbox" class="sel"></td> // <td class="tdIpt">${i + 1}</td> // <td class="tdIpt">${name}</td> // <td class="tdIpt">${nickname}</td> // <td class="tdIpt">${age}</td> // <td class="tdIpt">${gender}</td> // <td class="tdIpt">${hobby}</td> // <td class="tdIpt"><button>X</button></td> // </tr>` // }).join("") // // 注意:用map渲染一定要用join转换,去掉里面的逗号 // } // 功能实现 // 1、全选/不选 功能 // 2、点击任意单元格,选中当前行 ,并勾选复选框 // 3、选中的时候高亮 // 4、删除某行后,让里面第二列数据自动更新 ,并且以上功能都能实现 // 获取元素 let cont = document.querySelectorAll('.cont') let sel = document.querySelectorAll('.sel') let thIpt = document.querySelector('.thIpt') //这里用事件委托 table.addEventListener('click', function (e) { // console.log(e.target.className) switch (e.target.className) { // 全选和不选 case "thIpt": // 获取点击th的父元素的父元素的父元素的父元素table let thtable = e.target.parentNode.parentNode.parentNode.parentNode // 获取table下的第二个孩子的所有孩子tr let table_tr = thtable.children[1].children // console.log(table_tr) //tr // console.log(thIpt.checked) if (thIpt.checked) { // tbale_sel.forEach((item, i) => { // item.checked = true // item.parentNode.parentNode.className = "active" // }) // 遍历所有的tr for (var key of table_tr) { // console.log(key) //tr // console.log(key.firstElementChild.children[0]) // 让tr的第一个元素td的孩子 就是 input key.firstElementChild.children[0].checked = true // 让所有的tr都有样式 key.className = "active" } // 全不选 } else { // tbale_sel.forEach((item, i) => { // item.checked = false // item.parentNode.parentNode.className = "" // }) for (var key of table_tr) { key.firstElementChild.children[0].checked = false key.className = "" } } break // 2、点击任意单元格,选中当前行 ,并勾选复选框 // 3、选中的时候高亮 case "tdIpt": let preTr = e.target.parentNode //当前点击td的行 // console.log(preTr) let pretbody = preTr.parentNode //当前点击td的行tr的父元素 tbody var preIdx = 0 // 自定义设置索引号 // tbody.children.length 是tbody下面的所有子长度元素 for (var i = 0; i < pretbody.children.length; i++) { // 排他思想 先清除所有样式 for (var j = 0; j < pretbody.children.length; j++) { pretbody.children[j].className = "" // console.log(pretbody.children[j].children[0].children) // console.log(pretbody.children[j].children[0].children[0]) // 让所有动态之后的tbody的所有孩子tr的第一个孩子td的孩子 就是input复选框 的checked属性为false pretbody.children[j].children[0].children[0].checked = false } // 如果第i个 等于点击的preTr 就把那个 i 索引赋值给 preIdx if (pretbody.children[i] == preTr) { preIdx = i } // console.log(preIdx) // 设置被选中的行的背景色 pretbody.children[preIdx].className = "active" // 让所有动态之后的tbody的孩子(被点击的)tr的孩子td的孩子 就是input复选框 的checked属性为false pretbody.children[preIdx].children[0].children[0].checked = true } break // 4、删除某行后,让里面第二列数据自动更新 case "del": // console.log(e.target.parentNode) //td let delTd = e.target.parentNode //当前点击的列 td let delTr = delTd.parentNode //当前点击的列 tr let deltbody = delTd.parentNode.parentNode //当前点击行的父元素 // console.log(deltbody) //tbody // console.log(deltbody.children.length) //6 var delIdx = 0; //自定义索引号 for (var i = 0; i < deltbody.children.length; i++) { // 判断第 i 个是否等于 当前点击的 delTr if (deltbody.children[i] == delTr) { delIdx = i; //拿到当前点击的索引 } } tbody.innerHTML = "" // 把data中响应的数据删除 data.splice(delIdx, 1) // console.log(data.splice(delIdx, 1)) //返回的是删除的数据 render() thIpt.checked = false break // 选中sel,也让背景色变化 case "sel": let selTr = e.target.parentNode.parentNode let selTbody = e.target.parentNode.parentNode.parentNode let selAllTr = selTbody.children // console.log(selTbody.children) //tr // let preSel = selAllTr.firstElementChild // console.log(preSel) // 判断当前的sel是否被选中 if (e.target.checked) { selTr.className = "active" } else { selTr.className = "" } // 下面复选框需要全部选中, 上面全选才能选中做法: 用forEach循环实现 for (var key of selAllTr) { // 遍历当前的所有的tr let preIpt = key.firstElementChild.children[0] // console.log(key.firstElementChild.children[0]) // console.log(key) key.onclick = function () { var flag = true // 再重新遍历所有的tr 判断 for (var key2 of selAllTr) { let preIpt2 = key2.firstElementChild.children[0] // console.log(prekey2) // console.log(prekey2.checked) if (!preIpt2.checked) { flag = false } } thIpt.checked = flag; } } break } }) </script> </body> </html>上述代码中,有个bug,当第一次打开运行代码时,先全选上时,再去点击下面的复选框的时候,发现全选并没有去掉,而当在点击另一个复选框时,全选才被去掉,最后那部分代码没有处理好,用图片说明
后面我删除数据后,再去操作以上步骤,还是在第一次的时候会出现问题,再试就没有问题了
最终还是那个问题,最上面单独的全选和不选不论是不是第一次都是没有问题的
重点: 当获取的结果为 HTMLCollection(6) [tr, tr, tr, tr, tr, tr]类似的伪数组时,不能用forEach去遍历,会想上面一样报错,可以把伪数组转为数组就可以用了。
Array.prototype.slice.call(需要转化的对象) 能通过Array.prototype.slice转换为真正的数组的带有length属性的对象。 这种对象有很多,比较特别的是arguments对象,还有像调用getElementsByTagName,document.childNodes之类的,它们都返回NodeList对象都属于伪数组。 我们可以通过Array.prototype.slice.call(fakeArray)将伪数组转变为真正的Array对象。