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() { this.handler = function() { var val = threeStateByClick(this.el); this.set(val); }.bind(this); this.el.addEventListener('click', this.handler); }, unbind: function() { this.el.removeEventListener('click', this.handler); }, update: function(val) { threeStateByVal(this.el, val); }, }); 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); }