function threeStateByClick(cb) { var val = true; if (cb.readonly) { // 本来はcb.indeterminate(値が保持されないのでreadonlyを代用) cb.readonly = cb.indeterminate = false; val = cb.checked = false; } else if (!cb.checked) { cb.readonly = cb.indeterminate = true; val = null; } return val; } function threeStateByVal(cb, val) { if (val === null) { cb.readonly = cb.indeterminate = true; cb.checked = false; } else { cb.readonly = cb.indeterminate = false; cb.checked = val; } } Vue.directive('three-state', { //twoWay: true, bind: function(el, binding, vnode) { this.handler = function() { var val = threeStateByClick(el); Vue.set(vnode.context, binding.expression, val); }.bind(this); el.addEventListener('click', this.handler); }, unbind: function(el) { el.removeEventListener('click', this.handler); }, update: function(el, binding) { threeStateByVal(el, binding.value); }, }); var vm = new Vue({ el: "#sample", data: { cbVal: false, }, computed: { cbVal_toString: { get: function() { return this.cbVal + ""; }, set: function(val) { this.cbVal = eval(val); }, }, }, });
<div id="sample"> <label> <input type="checkbox" v-three-state="cbVal"> 3-state checkbox </label> <br/> <select size="1" v-model="cbVal_toString"> <option value="true">true</option> <option value="false">false</option> <option value="null">null</option> </select> </div>
input[type=checkbox]{ -webkit-transform: scale(2); transform: scale(2); }