Akash
Akash

Reputation: 89

How can I show the selected items in the label of a Bootstrap dropdown?

I have a Bootstrap dropdown that has checkboxes in the options. When I select one checkbox, the checkbox text is showing in dropdown button.

The problem is that it is not working the way I want when multiple checkboxes are selected. When I select two checkboxes, the text of the last clicked checkbox is showing. This should not be there, instead of the checkbox text I want it to show "Selected 2 items" in dropdown button.

How can I solve this problem?

$(".dropdown-menu a").click(function() {
  var selText = $(this).text();
  $(this).parents('.btn-group').find('.dropdown-toggle').html(selText);
});

// checbox
$('#checkall').change(function() {
  $('.cb-element').prop('checked', this.checked);
});

$('.cb-element').change(function() {
  if ($('.cb-element:checked').length == $('.cb-element').length) {
    $('#checkall').prop('checked', true);
  } else {
    $('#checkall').prop('checked', false);
  }
});
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
<!-- jQuery library -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<!-- Popper JS -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js"></script>
<!-- Latest compiled JavaScript -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>

<div class="btn-group ">

  <button class="btn btn-secondary dropdown-toggle text-center" type="button" data-toggle="dropdown">
                    Dropdown button
                  </button>
  <div class="dropdown-menu">
    <a class="dropdown-item"><input type="checkbox" name="all" class="mr-2" id="checkall">Check All</br>
    </a>
    <a class="dropdown-item"><input type="checkbox" class="cb-element mr-2"> Checkbox 1</a>
    <a class="dropdown-item"><input type="checkbox" class="cb-element mr-2"> Checkbox 2</a>
    <a class="dropdown-item"><input type="checkbox" class="cb-element mr-2"> Checkbox 3</a>
  </div>

</div>

Upvotes: 3

Views: 2418

Answers (4)

Mark Schultheiss
Mark Schultheiss

Reputation: 34197

I find this checkbox/check all pattern interesting in that you should handle the click of the 'all' and the normal and set the values. This is also interesting as it is in a "drop down" visual so you have to keep that open for a better user experience when they are making selections. In addition you have "custom" labels for the drop down menu dependent upon what is checked. All an interesting behavior complex.

I don't like using global variables that might be a possible duplicate of other code and prefer to keep things in a namespace - not a fancy one and not an instance one here but still of use for those reasons.

I give an example of using a "label" element for the text of the drop down but also a "data" property to hold one so we have multiple options demonstrated.

Comments in-line to help with what it is doing.

Note that I use CSS instead of an in-line <br /> for the spacing which also allows me to style it a bit (green underline) and choose how much margin to add to the "check all"

// namespace to avoid globals
var myApp = myApp || {
  checkBoxes: {},
  originalText: "",
  toggleText: "Checked ",
  handleCheckboxEvents: function(event) {
    // keep event from bubble to 'a' tag
    event.stopImmediatePropagation()
    let checkedOnes = myApp.checkBoxes.filter(':checked');
    let checkCount = checkedOnes.length;
    let checkedAll = checkCount === myApp.checkBoxes.length;
    // use if single checked for label
    let label = checkedOnes.first()
      .closest('.dropdown-item')
      .find('.check-label').html();
    // labels can come from data also like this example
    let checkallLabel = $('.checkall-item').data('all');

    // non checked use original
    let selText = checkCount === 0 ?
      myApp.originalText :
      // one checked use its label
      checkCount === 1 ? label :
      // checked all? 
      checkedAll ? checkallLabel :
      // more than one - use number checked
      myApp.toggleText + checkCount;
    // set the text we determined
    $(this).closest('.btn-group')
      .find('.dropdown-toggle')
      .html(selText);
    $('#checkall').trigger('set-checkall', [{
      source: 'checkbox'
    }]);
  },
  handleLinkClick: function(event) {
    // keep link from being followed
    event.preventDefault();
    // keep dropdown menu from closing
    event.stopPropagation();
    let cb = $(this).find('input[type="checkbox"]');
    let isChecked = cb.prop("checked");
    cb.prop("checked", !isChecked);
    cb.trigger('change');
  },
  handleCheckAll: function(event, data) {
    // use the source of the event to set it
    if (data.source == 'checkall') {
      // toggle to what it is not
      $(this).prop('checked', !this.checked);
      $('.cb-element').prop('checked', this.checked).trigger('change');
    }
    if (data.source === 'checkbox') {
      // set depending on how many checked
      $(this).prop('checked', myApp.checkBoxes.filter(':checked').length == myApp.checkBoxes.length);
    }
  },
  // initialize and attach event handlers
  init: function() {
    this.checkBoxes = $('.cb-element');
    this.originalText = $('.btn-secondary.dropdown-toggle').html();
    // handle the checkbox click or change
    $(".dropdown-menu").find('a.dropdown-item')
      .on('click change', 'input[type="checkbox"]', this.handleCheckboxEvents);
    $(".dropdown-menu").find('a.dropdown-item')
      .on('click', this.handleLinkClick);
    // handle the checkall change by its wrapper click
    $('.checkall-item').on('click', function() {
      event.stopPropagation();
      $('#checkall').trigger('set-checkall', [{
        source: 'checkall'
      }]);
    });
    // use custom event so we can use multiple places, passing it data
    $('#checkall').on('set-checkall', this.handleCheckAll);
  }
};
myApp.init();
.checkall-item .check-label {
  margin-bottom: 1em;
  border-bottom: 1px green solid;
  display: inline-block;
  font-weight:bold;
}
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>

<div class="btn-group my-dropdown-group">
  <button class="btn btn-secondary dropdown-toggle text-center" type="button" data-toggle="dropdown">Dropdown button</button>
  <div class="dropdown-menu">
    <a class="dropdown-item checkall-item" data-all="Checked All"><input type="checkbox" name="all" class="mr-2" id="checkall"><span class="check-label">Check All</span></a>
    <a class="dropdown-item"><input type="checkbox" class="cb-element mr-2"><span class="check-label">Checkbox Fun</span></a>
    <a class="dropdown-item"><input type="checkbox" class="cb-element mr-2"><span class="check-label">Checkbox Happy</span></a>
    <a class="dropdown-item"><input type="checkbox" class="cb-element mr-2"><span class="check-label">Checkbox Now</span></a>
  </div>
</div>

Upvotes: 0

Rayees AC
Rayees AC

Reputation: 4659

Try this instead,

First of all add label with for attribute to click event in label to check the box. like this

  <a class="dropdown-item">
    <input type="checkbox" name="all" class="mr-2" id="checkall">
    <label for="checkall">Check All</label>
  </a>

Then check how many checked the dropdown item. By

var len = $('.dropdown-menu .dropdown-item input:checked').length;

if the checkall button clicked then by

if($('#checkall:checked').length){
  len= "3";
}

Finally add to dropdown button

 $(this).parents('.btn-group').find('.dropdown-toggle').html("Selected "+len+" item");

$(".btn-group .btn.btn-secondary").click(function(){

  $('.dropdown-menu .dropdown-item input').change(function () {
    var len=$('.dropdown-menu .dropdown-item input:checked').length;
    // to get number of checked checkbox

    if($('#checkall:checked').length | len == 3){
      len= "All"; // if checked all check box to value to '3'
    }

    $(this).parents('.btn-group').find('.dropdown-toggle').html("Selected "+len+" item");
    // to assign value to dropdown button
  });
  
});

// for checkall 
$('#checkall').change(function () {
   $('.cb-element').prop('checked',this.checked);
});

$('.cb-element').change(function () {
 if ($('.cb-element:checked').length == $('.cb-element').length){
  $('#checkall').prop('checked',true);
 }
 else {
  $('#checkall').prop('checked',false);
 }
});
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
<!-- jQuery library -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<!-- Popper JS -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js"></script>
<!-- Latest compiled JavaScript -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>

<div class="btn-group">
    <button class="btn btn-secondary dropdown-toggle text-center" type="button" data-toggle="dropdown">Dropdown button</button>
    
    <div class="dropdown-menu">
      <a class="dropdown-item">
        <input type="checkbox" name="all" class="mr-2" id="checkall">
        <label for="checkall">Check All</label>
      </a>
      <a class="dropdown-item">
        <input type="checkbox" id="check1" class="cb-element mr-2">
        <label for="check1">Checkbox 1</label>
      </a>
      <a class="dropdown-item">
        <input type="checkbox" id="check2" class="cb-element mr-2">
        <label for="check2">Checkbox 2</label>
      </a>
      <a class="dropdown-item">
        <input type="checkbox" id="check3" class="cb-element mr-2"> 
        <label for="check3">Checkbox 3</label>
      </a>
    </div>
</div>

Upvotes: 0

Aakif
Aakif

Reputation: 156

You can try this I have made some modifications

$(".dropdown-menu a").click(function() {
  var selText = $(this).text();

  $(this).parents('.btn-group').find('.dropdown-toggle').html("selected " + $('.cb-element:checked').length + " item");
});

// checbox
$('#checkall').change(function() {
  var dropDown = $(this).parents('.btn-group').find('.dropdown-toggle');
  $('.cb-element').prop('checked', this.checked);
  if (!$('.cb-element:checked').length)
    dropDown.html("Dropdown button");
  else
    dropDown.html("selected " + $('.cb-element:checked').length + " item");
});

$('.cb-element').change(function() {
  if ($('.cb-element:checked').length == $('.cb-element').length) {
    $('#checkall').prop('checked', true);
  } else {
    $('#checkall').prop('checked', false);
  }
});
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
<!-- jQuery library -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<!-- Popper JS -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js"></script>
<!-- Latest compiled JavaScript -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>

<div class="btn-group ">

  <button class="btn btn-secondary dropdown-toggle text-center" type="button" data-toggle="dropdown">
                    Dropdown button
                  </button>
  <div class="dropdown-menu">
    <a class="dropdown-item"><input type="checkbox" name="all" class="mr-2" id="checkall">Check All</br>
    </a>
    <a class="dropdown-item"><input type="checkbox" class="cb-element mr-2"> Checkbox 1</a>
    <a class="dropdown-item"><input type="checkbox" class="cb-element mr-2"> Checkbox 2</a>
    <a class="dropdown-item"><input type="checkbox" class="cb-element mr-2"> Checkbox 3</a>
  </div>

</div>

Upvotes: 0

FluffyKitten
FluffyKitten

Reputation: 14312

You can actually do it very easily in the code you have already - you just need to check the number of selected options before you set the label:

  1. This line will get the number of selected checkboxes:
numSelected = $(this).parent(".dropdown-menu").find('.cb-element:checked').length;
  1. Now all you need to do is check if it is > 1, and set the button to:
selText = numSelected + " selected";

That's it! (Don't forget that you also need to check if the current checkbox was deselected - you're not currently doing that).

Show "All selected" label:

If you want to get fancy, you can also see if all of the checkboxes are selected and display "all selected" instead, or if 1 is selected then show the label of that one.

totalNumOptions = $(".dropdown-menu .cb-element").length;

$(".dropdown-menu a").change(function() {

  // get selected options
  selected = $(this).parent(".dropdown-menu").find('.cb-element:checked');

  if (selected.length == totalNumOptions || $("input#checkall:checked").length == 1)
    // ALL options are selected, of "All checked" is checked
    selText = "All selected";
 
  else if (selected.length == 1) 
    // show text of the SELECTED option (NOTE: not clicked option!)
    selText = $(selected[0]).parent().text();  

  else
   // show number of selected options
    selText = selected.length + " selected";

  $(this).parents('.btn-group').find('.dropdown-toggle').html(selText);
});

Working Example:

// Get the total number of options so we can see if all are checked
// do this outside of handler so we only do it once
var totalNumOptions = $(".dropdown-menu .cb-element").length;

$(".dropdown-menu a").change(function() {

  // get number of selected options
  selected = $(this).parent(".dropdown-menu").find('.cb-element:checked');

  if (selected.length == totalNumOptions ||
    $(this).find("input#checkall:checked").length == 1)
    // ALL options are selected
    selText = "All selected";

  else if (selected.length == 1)
    //only 1 selected so show the selected option
    selText = $(selected[0]).parent().text();

  else
    // show number of selected options
    selText = selected.length + " selected";

  $(this).parents('.btn-group').find('.dropdown-toggle').html(selText);
});

// checbox
$('#checkall').change(function() {
  $('.cb-element').prop('checked', this.checked);
});

$('.cb-element').change(function() {
  if ($('.cb-element:checked').length == $('.cb-element').length) {
    $('#checkall').prop('checked', true);
  } else {
    $('#checkall').prop('checked', false);
  }

});
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
<!-- jQuery library -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<!-- Popper JS -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js"></script>
<!-- Latest compiled JavaScript -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>

<div class="btn-group ">

  <button class="btn btn-secondary dropdown-toggle text-center" type="button" data-toggle="dropdown">
                    Dropdown button
                  </button>
  <div class="dropdown-menu">
    <a class="dropdown-item"><input type="checkbox" name="all" class="mr-2" id="checkall">Check All</br>
    </a>
    <a class="dropdown-item"><input type="checkbox" class="cb-element mr-2"> Checkbox 1</a>
    <a class="dropdown-item"><input type="checkbox" class="cb-element mr-2"> Checkbox 2</a>
    <a class="dropdown-item"><input type="checkbox" class="cb-element mr-2"> Checkbox 3</a>
  </div>

</div>

Upvotes: 1

Related Questions