【问题标题】:Using jQuery for bidirectional cascading checkboxes使用 jQuery 进行双向级联复选框
【发布时间】:2017-12-14 22:26:23
【问题描述】:

考虑以下 HTML sn-p:这基本上是一组嵌套的复选框,使用无序列表标记来创建一般分组。代码是动态生成的,因此我可以根据需要完全控制标记。

代表以下问题的 jsfiddle 位于:http://jsfiddle.net/bwh4rj3h/

我正在尝试创建执行以下操作的 jQuery:

  1. 单击全选可切换 sn-p 中所有复选框的状态。
  2. 单击矩阵更深处的复选框将切换所有“子复选框”。例如单击 Group1 复选框将切换组下结构中所有复选框的状态。
  3. 如果未选中给定组中的任何复选框,则还必须取消选中组复选框(虽然从技术上讲,代表“部分”填充的三态复选框会很好,但这超出了我的问题范围)。例如假设项目 1-1、1-2 和 1-3 都被选中。然后应检查组 1,在此示例中,还应检查“全选”。但是,如果我取消选择第 1-2 项,现在第 1 组不是“完整的”,因此应该取消选中,“全选”也应该取消选中。

好的,所以我有一些适用于下面标记的 jQuery 代码。它工作正常。但它很丑。而且我讨厌丑陋,并且觉得我的逻辑、选择器和/或 HTML 标记存在缺陷,导致事情变得如此丑陋,并且有更好的方法。

很想得到一些反馈,说明什么是更好的方法。就我个人而言,我不必担心超过 2 级的深度,但通用的 N 级深度功能应该不会太难。事实上,如果不是第三种情况,我正在检查“孩子”的状态以查看是否所有内容都被选中/未选中,它会相对简单。我正在做一个 3 次循环(因为评估顺序)的事实让我很难过。

提前感谢您的帮助。 :)

您会注意到我正在使用 :disabled 检查。在我的最终项目中,我将一些复选框设置为禁用,因此它们对我上面列出的 3 个要求“免疫”。由于标记不包含任何禁用的项目,因此这不会影响结果。

<ul>
   <li>
       <input type="checkbox" id="chkAll" checked/><label for="chkAll">Select All</label>
   </li>
   <ul>
       <li>
           <input type="checkbox" id="chkGroup1" checked/><label for="chkGroup1">Group 1</label>
       </li>
       <ul>
           <li>
               <input type="checkbox" id="chkGp1-Item1" checked/><label for="chkGp1-Item1">Item 1</label>
           </li>
           <li>
               <input type="checkbox" id="chkGp1-Item2" checked/><label for="chkGp1-Item2">Item 2</label>
           </li>
       </ul>
    </ul>
</ul>

jQuery:

  $("input[type=checkbox]:not(:disabled)").on("change", function () {
                $(this).parent().next("ul").find("li input[type=checkbox]:not(:disabled)").prop("checked", $(this).prop("checked"));

                // Looping 3 times to cover all sublevels.  THERE HAS GOT TO BE A BETTER WAY THAN THIS.

                for (var i = 0; i < 3; i++) {

                    $("input[type=checkbox]:not(:disabled)").each(function() {

                        var uncheckedkidcount = $(this).parent().next("ul").find("li input[type=checkbox]:not(:disabled):not(:checked)").length;
                        var kidcount = $(this).parent().next("ul").find("li input[type=checkbox]:not(:disabled)").length;

                        if (kidcount > 0) {
                            $(this).prop("checked", uncheckedkidcount == 0);
                        }
                    });
                }
            }); 

【问题讨论】:

    标签: javascript jquery checkbox


    【解决方案1】:

    我已经使用不同的解决方案分叉并更新了您的 JSFiddle。我对标记进行了一些更改,我认为这会使处理更容易,但 jQuery 仍然很长。希望这会有所帮助。

    Here is the updated JSFiddle。我已经评论了 JS 来解释它是如何工作的。

    对于您的标记 - 我认为将 &lt;ul&gt; 直接嵌套在另一个 &lt;ul&gt; 中而不将其包装在 &lt;li&gt; 中是不好的做法(有关更多信息,请参阅此 stack overflow question。此外,如果您有完全控制 DOM 结构,我会添加一些类名以使 jQuery 更易于管理:

    <ul>
        <li>
            <input type="checkbox" id="chkAll" checked/>
            <label for="chkAll">Select All</label>
        </li>
        <li class="has-nested-list">
            <ul class="nested">
                <li>
                    <input type="checkbox" id="chkGroup1" checked/>
                    <label for="chkGroup1">Group 1</label>
                </li>
                <li class="has-nested-list">
                    <ul class="nested">
                        <li>
                            <input type="checkbox" id="chkGp1-Item1" checked/>
                            <label for="chkGp1-Item1">Item 1</label>
                        </li>
                        <li>
                            <input type="checkbox" id="chkGp1-Item2" checked/>
                            <label for="chkGp1-Item2">Item 2</label>
                        </li>
                    </ul>
                </li>
            </ul>
        </li>
    </ul>
    

    为了在有嵌套列表时不使用多个项目符号,我们可以添加这个简单的 CSS 规则

    .has-nested-list {
        list-style-type: none;
    }
    

    jQuery 全部在小提琴中,我不会发布它以帮助使其更简短。

    希望这会有所帮助。

    干杯

    【讨论】:

    • 这做得很好。你是对的,在某些部分的 DOM 嗅探非常讨厌,我听到你关于我将
        标记直接嵌套在
      • 标记中的方式。我当时想知道,所以谢谢你的链接。我将把这个答案留几天(我没看到你什么时候回答,所以我很抱歉......我以为我会收到一封电子邮件通知),而不是更高级的东西,我会接受这个.非常感谢您抽出宝贵的时间,语法。我刚刚花了一个小时为别人回答另一个问题...... Stack Overflow 是件好事!
    • 很高兴我能帮上忙。 Stackoverflow 是自 Google 以来互联网上发生的最好的事情 :)
    【解决方案2】:

    我在尝试实现同样的事情时偶然发现了你的问题。

    我是这样解决的:https://codepen.io/nexx/pen/VyLLPM

    它支持多级嵌套复选框,并遵循问题中提到的逻辑。希望它可以帮助那些在同一件事上挣扎的人。作为初学者,我很想听听反馈:)

     <ul class="categories-list">
      <li class="category"><input type="checkbox"><label>Parent category</label>
        <a class='collapse-category' href=''>expand</a>
        <ul class="subcategories">
          <li class="category"><input type="checkbox"><label>Child category </label></li>
          <li class="category"><input type="checkbox"><label>Child category </label></li>
          <li class="category"><input type="checkbox"><label>Child category </label></li>
          <li class="category"><input type="checkbox"><label>Child category </label></li>
          <li class="category"><input type="checkbox">
          <label>Child category with children</label>
            <a class='collapse-category' href=''>expand</a>
            <ul class="subcategories">
              <li class="category"><input type="checkbox"><label>Child category </label></li>
              <li class="category"><input type="checkbox"><label>Child category </label></li>
              <li class="category"><input type="checkbox"><label>Child category </label></li>
              <li class="category"><input type="checkbox"><label>Child category </label></li>
              <li class="category"><input type="checkbox">
              <label>Child category with children</label>
                <a class='collapse-category' href=''>expand</a>
                <ul class="subcategories">
                  <li class="category"><input type="checkbox"><label>Child category </label></li>
                  <li class="category"><input type="checkbox"><label>Child category </label></li>
                  <li class="category"><input type="checkbox"><label>Child category </label></li>
                  <li class="category"><input type="checkbox"><label>Child category </label></li>
                  <li class="category"><input type="checkbox"><label>Child category </label></li>
                </ul>
              </li>
            </ul>
          </li>
        </ul>
      </li>
    </ul> 
    

    jQuery

    $.fn.checkboxParent = function() {
    //get parent checkbox element
      var checkboxParent = $(this)
      .parent("li")
      .parent("ul")
      .parent("li")
      .find('> input[type="checkbox"]');
      return checkboxParent;
    };
    
    $.fn.checkboxChildren = function() {
      //get checkbox children
      var checkboxChildren = $(this)
      .parent("li")
      .find(' > .subcategories > li > input[type="checkbox"]');
      return checkboxChildren;
    };
    
    
    $.fn.cascadeUp = function() {
      var checkboxParent = $(this).checkboxParent();
      if ($(this).prop("checked")) {
        if (checkboxParent.length) {
          //check if all children of the parent are selected - if yes, select the parent
          //these will be the siblings of the element which we clicked on
          var children = $(checkboxParent).checkboxChildren();
          var booleanChildren = $.map(children, function(child, i) {
            return $(child).prop("checked");
          });
          //check if all children are checked
          function and(a, b) {
            return a && b;
          }
          var allChecked = booleanChildren.reduce(and, true);
          if (allChecked) {
            $(checkboxParent).prop("checked", true);
            $(checkboxParent).cascadeUp();
          }
        }
      } else {
        if (checkboxParent.length) {
          //if parent is checked, becomes unchecked
          $(checkboxParent).prop("checked", false);
          $(checkboxParent).cascadeUp();
        }
      }
    };
    $.fn.cascadeDown = function() {
      var checkboxChildren = $(this).checkboxChildren();
      if ($(this).prop("checked")) {
        if (checkboxChildren.length) {
          //all children are checked
          checkboxChildren.prop("checked", true);
          checkboxChildren.each(function(index) {
            $(this).cascadeDown();
          });
        }
      } else {
        if (checkboxChildren.length) {
          //children become unchecked
          checkboxChildren.prop("checked", false);
          checkboxChildren.each(function(index) {
            $(this).cascadeDown();
          });
        }
      }
    };
    
    $("input[type=checkbox]:not(:disabled)").on("change", function() {
      $(this).cascadeUp();
      $(this).cascadeDown();
    });
    
    $(".category a").on("click", function(e) {
      e.preventDefault();
      $(this)
        .parent()
        .find("> .subcategories")
        .slideToggle(function() {
        if ($(this).is(":visible")) $(this).css("display", "flex");
      });
    });
    

    css(萨斯):

    .categories-list
      margin: 40px 0px
      padding: 0px 10px
      width: 300px
      .category
        font-size: 14px
        display: flex
        flex-direction: column
        position: relative
        padding: 3px 0px 3px 22px
        border-bottom: 1px solid #f5f5f5
        font-weight: 300
        display: flex
        label
          width: 100%
        a
          color: #95939a
          position: absolute
          right: 0px
          z-index: 1000
        input[type="checkbox"]
          margin: 0px 10px 0px 0px
          position: absolute
          left: 0px
          top: 7px
        .subcategories
          margin-left: 0px
          display: none
          padding: 5px
          flex-direction: column
          .category
            padding-left: 22px
            flex-direction: column
            &:last-child
              border-bottom: none
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-03-15
      • 2021-11-07
      • 2016-09-26
      • 2014-03-28
      • 1970-01-01
      • 1970-01-01
      • 2021-01-23
      • 1970-01-01
      相关资源
      最近更新 更多