Reputation: 89
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
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
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
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
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:
numSelected = $(this).parent(".dropdown-menu").find('.cb-element:checked').length;
> 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