链接具有jQuery的父子关系的复选框

时间:2018-08-24 21:27:34

标签: javascript

我不确定该如何解释。希望我的代码是不言而喻的。我对javascript很陌生。

<div>
  <input type="checkbox" id="checkBox1" onclick="toggleCheckBox(this)">Check Box 1</input>
  <div>
    <input type="checkbox" id="checkBox2">Check Box 2</input>
  </div>
</div>

function toggleCheckBox(checkBox1) {
    // I've also tried 'checkBox1.parent' and 'checkBox1.parentElement'
    var cb2 = checkBox1.parentNode.getElementByTagName('div').getElementByTagName('input');

    if (checkBox1.checked == true) {
        cb2.checked = true;
    } else {
        cb2.checked = false;
    };
};

我不断得到checkBox1.parent的信息。

编辑:

最终的期望结果是:

<div id="a">
  <input type="checkbox" onclick="toggleCheckBox(this)">Check Box 1</input>
  <div>
    <input type="checkbox">Check Box 2</input>
    <input type="checkbox">Check Box 3</input>
  </div>
</div>
<div id="b">
  <input type="checkbox" onclick="toggleCheckBox(this)">Check Box 1</input>
  <div>
    <input type="checkbox">Check Box 2</input>
    <input type="checkbox">Check Box 3</input>
  </div>
</div>

以此类推。

编辑:父级不确定

我让父母去indeterminate,但是除非您单击它,否则它不会离开。

/* clicked child checkbox */
    var parentId = clickedBox.dataset.parent;
    var checkedTrue = false;
    var checkedFalse = false;
    var checkState = clickedBox.checked;
    Array.prototype.slice.call(allCheckboxes).forEach(function(checkbox) {
      /* search checkboxes having same data-parent as clickedBox and is not checked */
      if (parentId === checkbox.dataset.parent && !checkbox.checked) {
        checkedFalse = true;
        checkState = false;
        return false;
      } else if (parentId === checkbox.dataset.parent && checkbox.checked) {
        checkedTrue = true;
        return false;
      }
    });
    /* search parent checkbox of clickedBox */
    Array.prototype.slice.call(allCheckboxes).forEach(function(parentBox) {
      if (parentBox.id === parentId) {
        if (checkedTrue === true && checkedFalse === true) {
          parentBox.indeterminate = true;
          return false;
        } else {
          parentBox.checked = checkState;
          return false;
        }
      }
    });

2 个答案:

答案 0 :(得分:1)

这就是我要做的。需要注意的一点是,该解决方案不支持IE11或更低版本,因为它使用closest()通过选择器查找父节点。

https://caniuse.com/#search=closest

(您可能必须单击GIF才能播放它。)

Example

<div data-checkbox-group>
  <label><input type="checkbox" data-master-checkbox /> Master</label>
  <div>
    <label><input type="checkbox" /> Check Box 1</label><br>
    <label><input type="checkbox" /> Check Box 2</label><br>
    <label><input type="checkbox" /> Check Box 3</label><br>
    <label><input type="checkbox" /> Check Box 4</label><br>
    <label><input type="checkbox" /> Check Box 5</label><br>
  </div>
</div>
<hr>
<div data-checkbox-group>
  <label><input type="checkbox" data-master-checkbox /> Master</label>
  <div>
    <label><input type="checkbox" /> Check Box 1</label><br>
    <label><input type="checkbox" /> Check Box 2</label><br>
    <label><input type="checkbox" /> Check Box 3</label><br>
    <label><input type="checkbox" /> Check Box 4</label><br>
    <label><input type="checkbox" /> Check Box 5</label><br>
  </div>
</div>
<hr>
<div data-checkbox-group>
  <label><input type="checkbox" data-master-checkbox /> Master</label>
  <div>
    <label><input type="checkbox" /> Check Box 1</label><br>
    <label><input type="checkbox" /> Check Box 2</label><br>
    <label><input type="checkbox" /> Check Box 3</label><br>
    <label><input type="checkbox" /> Check Box 4</label><br>
    <label><input type="checkbox" /> Check Box 5</label><br>
  </div>
</div>
<hr>

<script>
  var toggleChecks = function() {
    var isMaster = this.hasAttribute('data-master-checkbox')
    var group = this.closest('[data-checkbox-group]')
    var boxes = group.querySelectorAll('input[type=checkbox]:not([data-master-checkbox])')
    var master = isMaster ? this : group.querySelector('input[type=checkbox][data-master-checkbox]')

    if (isMaster) {
      // Set all children to the value of the parent
      for(var i = 0; i < boxes.length; i++) {
        boxes[i].checked = this.checked
      }
    } else {
      // Toggle all children to the state of the master
      var checkedCount = 0
      for(var i = 0; i < boxes.length; i++) {
        if(boxes[i].checked) { checkedCount += 1 }
      }

      master.checked = checkedCount == boxes.length
      master.indeterminate = !master.checked && !checkedCount == 0
    }
  }

  var nodes = document.querySelectorAll('[data-checkbox-group] input[type=checkbox]')
  for(var i = 0; i < nodes.length; i++) {
    nodes[i].addEventListener('change', toggleChecks)
  }
</script>

答案 1 :(得分:1)

在不限制使用HTML5 data-*的情况下,此解决方案适用于大多数更新的浏览器,包括IE11。在这里,我正在使用data-parent属性(其值与{{1相同) }}(父级)链接到其父级复选框。 id的值-1表示给定的复选框没有父级。

data-parent
let allCheckboxes;
/* checkbox click handler */
let toggleCheck = function(e) {
  e.stopPropagation();
  var clicked = this;
  /* clicked ones is the parent */
  if ('-1' === clicked.dataset.parent) {
    /* search child checkboxes having data-parent=id of clicked ones */
    Array.prototype.slice.call(allCheckboxes).forEach(function(c) {
      if (c.dataset.parent === clicked.id) {
        /* set checked state same as clicked ones */
        c.checked = clicked.checked;
      }
    });
  } else {
    /* clicked ones is child checkbox */
    var parentId = clicked.dataset.parent;
    var checkState = clicked.checked;
    Array.prototype.slice.call(allCheckboxes).forEach(function(c) {
      /* search checkboxes having data-parent same as clicked ones and is not checked */
      if (parentId === c.dataset.parent && !c.checked) {
        checkState = false;
        return false;
      }
    });
    /* search parent checkbox of clicked ones */
    Array.prototype.slice.call(allCheckboxes).forEach(function(c) {
      if (c.id === parentId) {
        c.checked = checkState;
        return false;
      }
    });
  }
};
/* wait till DOM renderes and available to bind event listneres */
document.addEventListener('DOMContentLoaded', function() {
  /* fetch all checkboxes having attributes data-parent and id */
  allCheckboxes = document.querySelectorAll('input[type=checkbox][data-parent][id]');
  /* iterate over all checkboxed to bind click listeners */
  Array.prototype.slice.call(allCheckboxes).forEach(function(c) {
    c.addEventListener('click', toggleCheck);
  });
});
div {
  margin-left: 1.5rem;
}

P.S。 <div> <label><input type="checkbox" data-parent="-1" id="c1"/>Check Box 1</label> <div> <label><input type="checkbox" data-parent="c1" id="c2">Check Box 2</label> <label><input type="checkbox" data-parent="c1" id="c3">Check Box 3</label> </div> </div> <div> <label><input type="checkbox" data-parent="-1" id="c4">Check Box 1</label> <div> <label><input type="checkbox" data-parent="c4" id="c5">Check Box 2</label> <label><input type="checkbox" data-parent="c4" id="c6">Check Box 3</label> </div> </div>是无效标记。

已编辑:关于</input>,因为到目前为止,我还没有为父复选框提供任何checkState状态,所以我所做的是将{{1 }}子复选框(如果已选中),则从具有相同父ID的其他其余子复选框的列表中进行搜索。当再遇到未选中的子复选框时,我只需将父复选框标记为intermediate

使用中级:嗯,不是那么花哨,但是可以解决问题。

clicked
unchecked
let allCheckboxes;
let repeat = Array.prototype.slice;
let applyState = function(checkbox, state) {
  let classes = checkbox.parentNode.classList;
  if (!state || !checkbox.checked) {
    classes.remove("checked");
    classes.remove("intermediate");
    checkbox.dataset.state = 0;
  }
  if (1 === state || checkbox.checked) {
    classes.add("checked");
    checkbox.dataset.state = 1;
  }
  if (2 === state) {
    classes.remove("checked");
    classes.add("intermediate");
    checkbox.dataset.state = 2;
  }
}
/* checkbox click handler */
let toggleCheck = function(e) {
  e.stopPropagation();
  var clicked = this;
  applyState(clicked);
  /* clicked ones is the parent */
  if ('-1' === clicked.dataset.parent) {
    /* search child checkboxes having data-parent=id of clicked ones */
    repeat.call(allCheckboxes).forEach(function(c) {
      if (c.dataset.parent === clicked.id) {
        /* set checked state same as clicked ones */
        c.checked = clicked.checked;
        applyState(c);
      }
    });
  } else {
    /* clicked ones is child checkbox */
    var parentId = clicked.dataset.parent;
    var check = 0,
      uncheck = 0;
    repeat.call(allCheckboxes).forEach(function(c) {
      /* search checkboxes having data-parent same as clicked ones */
      if (parentId === c.dataset.parent) {
        if (c.checked) {
          check++;
        } else {
          uncheck++;
        }
      }
    });
    /* search parent checkbox of clicked ones */
    repeat.call(allCheckboxes).forEach(function(c) {
      if (c.id === parentId) {
        var state = 1;
        if (0 === check) {
          state = 0;
          c.checked = false;
        } else if (0 < uncheck) {
          state = 2;
        }
        applyState(c, state);
        return false;
      }
    });
  }
};
/* wait till DOM renderes and available to bind event listneres */
document.addEventListener('DOMContentLoaded', function() {
  /* fetch all checkboxes having attributes data-parent and id */
  allCheckboxes = document.querySelectorAll('input[type=checkbox][data-parent][id]');
  /* iterate over all checkboxed to bind click listeners */
  repeat.call(allCheckboxes).forEach(function(c) {
    c.addEventListener('click', toggleCheck);
  });
});