业务需求: 为列表模型增加一个全选/全不选的功能,但功能奇葩的地方在于,列表的input实现并不是通过 type=checkbox 中checked的值 来做勾选判断的 而是添加了一个label用通过样式变化 来控制显示不同的伪元素::before(选中框) ::after(勾选图标√)来实现的...这导致我没办法通过jQuery来获取选中状态,所以只能另想他法
1.构思方案
先从最简单的场景开始,在list初始化后,直接点击全选,效果应该是选中所有list,由于前面提到的神奇问题,所以单纯的通过遍历list获取 prop("checked") 是没办法解决了,所以我决定通过触发点击事件的方法来使css变化,达到展示选中样式的效果.
首先,我自定义了一个属性ischecked,用于判断全选按钮的状态,以及默认值.
<input type='checkbox' class='allIpt' ischecked='false'/>
然后为其绑定一个点击事件,这样我们得到了一个可以控制状态的按钮(别问为什么要加个属性,问就是在一坨上,宁添不改)
$(".allIpt").on("click", function (ev) {
if ($(".allIpt").attr("ischecked") == 'false') {
$(".allIpt").attr("ischecked", 'true');
}
else if ($(".allIpt").attr("ischecked") == 'true') {
$(".allIpt").attr("ischecked", 'false');
}
console.log($(".allIpt").attr("ischecked"))
});
接下来的问题是,如何获取到整个list的数据,以及如何修改它们的状态,因为项目是单页面项目 大部分的数据都通过 挂载在window的属性进行传递 且 使用knockout实现数据绑定,所以我自然想到用请求出来的数据作为全选后的值,反选则置空
let SelectedData = null;
//全选
SelectedData = window.FormListID.allList();
//全不选
SelectedData = []
2.完善逻辑
尝试了一下确实可以获取到数据,且反复切换也没有问题,于是决定开始完善功能,目前虽然全选按钮可以获取到数据了,但list的样式还没有切换,这里开始处理样式问题.
要解决的问题有
- 每项被选中后的背景色
- label伪类切换实现的勾选样式
- 在有个别项被选中/未选中时,点击全选/全不选时 判断是否需要跳过此项
处理后关键代码
$(".allIpt").on("click", function (ev) {
ev.stopPropagation();
if ($(".allIpt").attr("ischecked") == 'false') {
SelectedData = window.FormListID.datasource();
$("#datalist tbody tr").each(function () {
if (!$(this).hasClass("selected")) {
$(this).find("input").click();//通过触发点击事件来切换css样式
$(this).addClass("selected");
}
});
$(".allIpt").attr("ischecked", 'true');
}
else if ($(".allIpt").attr("ischecked") == 'true') {
SelectedData = [];
$("#datalist tbody tr").each(function () {
if ($(this).hasClass("selected")) {
$(this).find("input").click();
$(this).removeClass("selected");
}
});
$(.allIpt").attr("ischecked", 'false');
}
});
3.处理问题
测试发现,基本上可以实现预期功能,但在 先全选-取消某项选择-全不选-再全选 情况下会出现全选后,样式上正常,但数据无法获取全部的问题.打印发现全选后,取消单项的选择后 ,列表绑定的数据window.FormListID.datasource()居然发生了变化,但我有需要使用它的值做操作,如何解决呢.
//原代码
SelectedData = window.FormListID.datasource();
//解决方案
SelectedData = window.FormListID.datasource().slice();
使用slice()方法可以创建一个新的数组,其中包含原始数组的副本.这是因为slice()方法在创建副本时会复制原始数组的元素,并将它们放入新的数组中.
由于这里只涉及到数据筛选,并不会去改变数组中项的值,所以直接使用了slice进行浅拷贝.实际上它们指向还是同一个对象.