Vue.js & checked 半选

引子

上周项目中有一常见的场景需求:一组 checkbox,然后一 checkbox 控制该组的全选/全不选状态,如该组未全选中需展示成半选中的状态。可能我描述的不够清楚,反正就是如下的样子(我用 jQuery 写了个 demo)。实现起来并不复杂,为了保持跨浏览器样式一致性,之前是其他 DOM 元素去模拟 checkbox,半选状态是用添加特殊的 className 然后去实现对应的样式。为了提升效率(其实是懒),这次我直接用了系统控件。结果问题来了,原生控件半选模式怎么展示?学了这么多年 HTML,抓耳挠腮貌似没有表示 checkbox 半选状态的属性。

Solution

鲁迅曾经说过:

世界上没有百度不出来的东西,如果有,那就换 Google!
via 心里MMP的鲁迅

一番面向 Google 编程之后,是我自己孤陋寡闻了,实际上有这么一个属性 indeterminate,将其设置成 ture 即可让 checkbox 看起来是未完全选中的样子。indeterminate 并不是新出现的,很久以前它还是 IE 的一个私有属性,IE5 就支持,后来的浏览器基本上都支持,同人逼死官方啊。另外这个属性需要注意了,虽然都翻译成属性,但这是特指的是 DOM 对象属性而不是 HTML 属性。还不理解赶快了解下 jQuery 中 attr()prop() 的区别。

直接添加 HTML 属性(attribute) 这么做是无效的。

<!--这样是无效的-->
<label><input type="checkbox" id="checkbox-demo" indeterminate="true"> 这样是无效的</label>

你要改变的是 DOM 对象的属性(property)才可以。

<label><input type="checkbox" id="checkbox-demo"> 这样才行</label>
<script>
    var checkbox = document.getElementById("checkbox-demo");
    checkbox.indeterminate = true;
</script>

那么与标题中的 Vue.js 有半毛钱关系?因为我们的项目是基于 Vue.js v1.x 版本,但是该版本的 Vue.js 并没有添加对 checkbox 属性 indeterminate 的支持。也就是说,如下的代码是无效的:

<label><input type="checkbox" id="checkbox-demo" v-indeterminate="true"> 这是无效的</label>
<label><input type="checkbox" id="checkbox-demo" :indeterminate="true"> 这也是无效的</label>

且在实际场景中,所有的 checkbox 都是通过遍历生成的,所以拿到 DOM 引用是个问题。幸好有 directive,自定义指令中 this.el 可以指向绑定的 DOM 对象,我们可以通过实现一个特定的自定义指令来兼容之:

<div id="app">
  <label>
    <input type="checkbox" id="theCheckbox"
      v-indeterminate="setting"
      v-model="value"
    />
    <span>A checkbox</span>
  </label>
</div>

<script typp="javascript">
    new Vue({
      el: "#app",
      data: {
        setting: false,
        value: false
      },
      directives: {
        // In Vue 1.x
        indeterminate: function(value) {
          this.el.indeterminate = Boolean(value)
        }
      }
    });
</script>

Vue.js 2.x 版本就好办了,直接这么办即可:

<input type="checkbox" :indeterminate.prop="true">

另外 indeterminate 状态在 CSS 中是有伪类的。:indeterminate 可表示状态不确定的表单元素:

/* Selects any <input> whose state is indeterminate */
input:indeterminate {
  background: lime;
}

问题

  1. indeterminate 属性在 iOS Safari 并不支持
  2. indeterminate 属性在各个浏览器中演示表现有差异

参考

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注