How to calculate numbers (total) based on the checked checkboxes using jQuery? - javascript

I have a a few checkboxes on my page and each checkbox has a data-price attribute.
The data-price represents a price of an item.
I'm trying to calculate a total value based on the checked boxes that is checked and also make sure the deduct the value of the UN-CHECKED checkboxes from the total value.
So far I have done this:
https://jsfiddle.net/523q4n72/1/
The issue that I have is that when I add up the price, it works fine but when I uncheck a checkbox, the total value jumps to minus and it does some strange calculations.
To test it, just check the checkboxes and uncheck one or two of them to see the issue.
$(document).on("click", ".checks", function() {
if ($(this).prop('checked') == true) {
var price = $(this).attr("data-price");
var tots = $('#tots').html();
var setPrice = Number(price) + Number(tots);
var ffp = Math.round(setPrice * 100) / 100;
$('#tots').text(ffp);
} else if ($(this).prop('checked') == false) {
var price = $(this).attr("data-price");
var tots = $('#tots').html();
var setPrice = Number(price) - Number(tots);
var ffp = Math.round(setPrice * 100) / 100;
$('#tots').text(ffp);
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h4>£<span id="tots">0.00</span></h4>
<input type="checkbox" name="something" class="checks" data-price="17.94">17.94
<input type="checkbox" name="something" class="checks" data-price="17.94">17.94
<input type="checkbox" name="something" class="checks" data-price="17.94">17.94

DRY - Don't repeat yourself.
In this case Loop each checked checkbox.
I have extracted the function so we can call it when changed AND when loading in case a checkbox was already checked
function calc() {
var tots = 0;
$(".checks:checked").each(function() {
var price = $(this).attr("data-price");
tots += parseFloat(price);
});
$('#tots').text(tots.toFixed(2));
}
$(function() {
$(document).on("change", ".checks", calc);
calc();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h4>£<span id="tots">0.00</span></h4>
<input type="checkbox" name="something" class="checks" data-price="17.94">17.94
<input type="checkbox" checked name="something" class="checks" data-price="17.94">17.94
<input type="checkbox" name="something" class="checks" data-price="17.94">17.94
Alternative way of calling and initialising
$(".checks").on("change", calc).change();

Just change:
var setPrice = Number(price) - Number(tots);
to
var setPrice = Number(tots) - Number(price);
and it should work fine. The issue is occurring as you are trying to subtract the total price from the checked element price.
UPDATED FIDDLE

A better idea is to recalculate the total sum each time from the currently checked elements (instead of adding or subtracting based on the current element)
And it is better to target the change event instead of the click.
$(document).on("change", ".checks", function() {
var checked = $('.checks:checked'),
sum = checked.get().reduce(function(prev, item) {
return prev + parseFloat(item.getAttribute('data-price'));
}, 0);
$('#tots').text( sum.toFixed(2) );
});
Updated fiddle at https://jsfiddle.net/523q4n72/3/

You were subtracting the total from the number clicked. Line 19 should read
var setPrice = Number(tots) - Number(price);
rather than
var setPrice = Number(price) - Number(tots);
:)

You must change Number(price) - Number(tots) to Number(tots) - Number(price) to have the right equation.
You redeclare everything every time, why not declare outside of the conditions ?
var price = 0;
var tots = "";
var setPrice = 0;
var ffp = 0;
$( document ).on( "click", ".checks", function() {
if ($(this).prop('checked')==true){
price = $(this).attr("data-price");
tots = $('#tots').html();
setPrice = Number(price) + Number(tots);
ffp = Math.round(setPrice * 100) / 100;
$('#tots').text(ffp);
} else if ($(this).prop('checked')==false){
price = $(this).attr("data-price");
tots = $('#tots').html();
setPrice = Number(tots) - Number(price);
ffp = Math.round(setPrice * 100) / 100;
$('#tots').text(ffp);
}
});

Here you go with a solution https://jsfiddle.net/523q4n72/5/
var total = 0;
$(document).on("click", ".checks", function() {
if ($(this).prop('checked') == true) {
total += Number($(this).data("price"));
} else if ($(this).prop('checked') == false) {
total -= Number($(this).data("price"));
}
$('#tots').text(Math.round(total * 100) / 100);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h4>£<span id="tots">0.00</span></h4>
<input type="checkbox" name="something" class="checks" data-price="17.94">17.94
<input type="checkbox" name="something" class="checks" data-price="17.94">17.94
<input type="checkbox" name="something" class="checks" data-price="17.94">17.94
Hope this will help you.

Honestly I would just approach it a different way then you have. When .checks is clicked, iterate over each checkbox and add the values of those checked (updated fiddle). It seems much easier to read, imo.
$( document ).on( "click", ".checks", function() {
//reset totals on each click
var total = 0;
//Add checked totals
$('.checks').each(function(){
if($(this).prop('checked') == true) total += $(this).data('price');
});
//Update new total
$('#tots').html(total.toFixed(2));
});

Instead of looking at the item that is (un)checked, loop the ones that are checked using the :checked selector.
It simplifies your code:
$('.checks').on('change', function() {
var total = 0;
$('.checks:checked').each(function() {
total += $(this).data('price');
});
$('#tots').text( total.toFixed(2) );
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h4>£<span id="tots">0.00</span></h4>
<input type="checkbox" name="something" class="checks" data-price="17.94">17.94
<input type="checkbox" name="something" class="checks" data-price="17.94">17.94
<input type="checkbox" name="something" class="checks" data-price="17.94">17.94

var setPrice = 0;
$( document ).on( "click", ".checks", function() {
if ($(this).prop('checked')==true){
var price = $(this).attr("data-price");
var tots = $('#tots').html();
var setPrice = Number(price) + Number(tots);
var ffp = Math.round(setPrice * 100) / 100;
$('#tots').text(ffp);
}else if ($(this).prop('checked')==false){
var price = $(this).attr("data-price");
var tots = $('#tots').html();
var setPrice = Number(tots) - Number(price);
var ffp = Math.round(setPrice * 100) / 100;
$('#tots').text(ffp);
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h4>£<span id="tots">0.00</span></h4>
<input type="checkbox" name="something" class="checks" data-price="17.94">17.94
<input type="checkbox" name="something" class="checks" data-price="17.94">17.94
<input type="checkbox" name="something" class="checks" data-price="17.94">17.94

Related

Total price calculator logic difficulty

I have two number inputs, what I want to do is to get dynamially the total price.
The problem is that when I decrease the number its still adding and doesn't work correctly. Actually my brain cannot imagine any way to code it correctly. Could someone give me any clue please?
<input type="number" name="open" id='open' min="0" max="20">
<input type="number" name="vip" id='vip' min="0" max="20">
<p> Total Price: <span id='doZaplaty'>0</span> EURO</p>
<script>
var vipPrice = 290;
var openPrice = 80;
var totalPrice = 0
$('#open').on("change", function() {
totalPrice = totalPrice + ($("#open").val() * openPrice);
$("#doZaplaty").html(totalPrice);
});
$('#vip').on("change", function() {
totalPrice = totalPrice + ($("#vip").val() * vipPrice);
$("#doZaplaty").html(totalPrice);
});
</script>
Because totalPrice = totalPrice + ($("#open").val() * openPrice); will add up previous result, as I commented.
However, you have 2 different total to take into account, so it's not easy to keep the state with only one total, because you need to subtract the previous result ,or calculate the change from previous value.
Instead, you can have 2 different total, like openTotal for result on #open and vipTotal on result for #vip, then you can use openTotal = ($("#open").val() * openPrice); to get the current state. And when you need to output the result, use $("#doZaplaty").html(openTotal + vipTotal); to show the final total.
var vipPrice = 290;
var openPrice = 80;
var openTotal = 0;
var vipTotal = 0;
$('#open').on("change", function() {
// As the totals are separated, we just need to get its current values computed.
openTotal = ($("#open").val() * openPrice);
$("#doZaplaty").html(openTotal + vipTotal);
});
$('#vip').on("change", function() {
vipTotal = ($("#vip").val() * vipPrice);
$("#doZaplaty").html(openTotal + vipTotal);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p> Total Price: <span id='doZaplaty'>0</span> EURO</p>
<input type="number" name="open" id='open' min="0" max="20">
<input type="number" name="vip" id='vip' min="0" max="20">
Because you always add to totalPrice. Try this instead (untested):
<script>
var totalPrice = 0
function GetTotalPrice(vipNum,openNum){
var vipPrice = 290;
var openPrice = 80;
var total = vipNum * vipPrice + openNum * openPrice;
return total;
}
$('#open').on("change", function(){
totalPrice = GetTotalPrice($("#vip").val(),$("#open").val());
$("#doZaplaty").html(totalPrice);
});
$('#vip').on("change", function(){
totalPrice = GetTotalPrice($("#vip").val(),$("#open").val());
$("#doZaplaty").html(totalPrice);
});
</script>
Please tyr by this simple way
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p> Total Price: <span id='doZaplaty'>0</span> EURO</p>
<input type="number" name="open" id='open' min="0" max="20">
<input type="number" name="vip" id='vip' min="0" max="20">
Set 2 hidden fields to store temp calculation value
<input type="hidden" name="totalPriceopenTemp" id='totalPriceopenTemp' value="0">
<input type="hidden" name="totalPricevipTemp" id='totalPricevipTemp' value="0">
<p> Total Price: <span id='doZaplaty'>0</span> EURO</p>
<script>
var vipPrice = 290;
var openPrice = 80;
var totalPrice = 0;
var totalPriceopenTemp = 0;
var totalPricevipTemp = 0;
$('#open').on("change", function() {
totalPriceopenTemp = ($("#open").val() * openPrice);
$("#totalPriceopenTemp").val(totalPriceopenTemp);
totalPrice = parseInt($("#totalPriceopenTemp").val())+parseInt($("#totalPricevipTemp").val());
$("#doZaplaty").html(totalPrice);
});
$('#vip').on("change", function() {
totalPricevipTemp = ($("#vip").val() * vipPrice);
$("#totalPricevipTemp").val(totalPricevipTemp);
totalPrice = parseInt($("#totalPriceopenTemp").val())+parseInt($("#totalPricevipTemp").val());
$("#doZaplaty").html(totalPrice);
});
</script>
I think It will work for you

Get Sum from each row form-field

I trying to calculate some rows (input fields) but it seams to hard for me :(.
The html code i have looks like this:
<table>
<tr>
<td>
<input name="Field_Price_1" id="Field_Price_1" value="20.55" type="text">
<input name="Field_Amount_1" id="Field_Amount_1" type="text">
<input name="Field_SubTotal_1" id="Field_SubTotal_1" type="text">
</td>
</tr>
<tr>
<td>
<input name="Field_Price_2" id="Field_Price_2" value="17.55" type="text">
<input name="Field_Amount_2" id="Field_Amount_2" type="text">
<input name="Field_SubTotal_2" id="Field_SubTotal_2" type="text">
</td>
</tr>
<tr>
<td>
<input name="Field_Price_3" id="Field_Price_3" value="94.20" type="text">
<input name="Field_Amount_3" id="Field_Amount_3" type="text">
<input name="Field_SubTotal_3" id="Field_SubTotal_3" type="text">
</td>
</tr>
<tr>
<td>
<input name="Field_Price_4" id="Field_Price_4" value="12.10" type="text">
<input name="Field_Amount_4" id="Field_Amount_4" type="text">
<input name="Field_SubTotal_4" id="Field_SubTotal_4" type="text">
</td>
</tr>
<tr>
<td>
<input name="Field_Price_5" id="Field_Price_5" value="7.45" type="text">
<input name="Field_Amount_5" id="Field_Amount_5" type="text">
<input name="Field_SubTotal_5" id="Field_SubTotal_5" type="text">
</td>
</tr>
So i would put the sum from all input fields "Field_Price_" in the following span by triggering "keyup" from each Field_Amount_".
<table>
<tr>
<td><span id="PrintSum">0.00</span></td>
</tr>
The following i tried:
var total = 0;
var Price = $('input[id^=Field_Price_]').val();
$.each($(Price), function(){
total += $(this).val();
});
$('#PrintSum').text(total);
So that won't work.
Do any know what is the problem? Thank you very much!
try something like this,Fiddle
var total = 0;
var Price = $('input[id^=Field_Price_]');
$.each($(Price), function(){
total += parseInt($(this).val());
});
$('#PrintSum').text(total);
use this javascript
$(':input[id^="Field_Price_"]').keyup(function() {
var total = 0;
var $inputs = $(':input[id^="Field_Price_"]');
$inputs.each(function (index)
{
total += parseFloat($(this).val());
});
alert(total);
$('#PrintSum').text(total);
});
jsFiddle
What about this solution:
var $prices = $('input[id^=Field_Price_]'),
$amounts = $('input[id^=Field_Amount_]');
$prices.add($amounts).on('keyup', function() {
var total = 0;
$prices.each(function() {
total += $(this).val() * $(this).next().val() || 0;
});
$('#PrintSum').text(total.toFixed(2));
})
.trigger('keyup');
http://jsfiddle.net/UFSvF/
Here changing the price as well as an amount triggers total price recalculation.
I guess you should be parse the input value to an float.
var total = 0;
var Price = $('input[id^=Field_Price_]').val();
$.each($(Price), function(){
total += parseFloat($(this).val());
});
$('#PrintSum').text(total);
On jsfiddle, an example not using jquery, but you could modify to do testing there
var prices = document.querySelectorAll("[id^=Field_Price]"),
ammounts = document.querySelectorAll("[id^=Field_Amount]"),
subTotals = document.querySelectorAll("[id^=Field_SubTotal]"),
printSum = document.getElementById("PrintSum");
function sumIt() {
var total = 0;
Array.prototype.forEach.call(prices, function (price, index) {
var subTotal = (parseFloat(price.value) || 0) * (parseFloat(ammounts[index].value) || 0);
subTotals[index].value = subTotal.toFixed(2);
total += subTotal;
});
printSum.textContent = total.toFixed(2);
}
Array.prototype.forEach.call(prices, function (input) {
input.addEventListener("keyup", sumIt, false);
});
Array.prototype.forEach.call(ammounts, function (input) {
input.addEventListener("keyup", sumIt, false);
});
sumIt();
Here is a jquery version of above, on jsfiddle
var prices = $("input[id^=Field_Price_]"),
amounts = $("input[id^=Field_Amount_]"),
subTotals = $("input[id^=Field_SubTotal_]"),
printSum = $("#PrintSum");
function sumIt() {
var total = 0;
prices.each(function(index, price) {
var subTotal = (parseFloat(price.value) || 0) * (parseFloat(amounts.eq(index).val()) || 0);
subTotals.eq(index).val(subTotal.toFixed(2));
total += subTotal;
});
printSum.text(total.toFixed(2));
}
prices.on("keyup", sumIt);
amounts.on("keyup", sumIt);
sumIt();
I will modify my pure javascript version soon to show you how your further questioning can be achieved, without the need of including jquery.
It has now been updated so you can see how it works
And a jsperf showing the construction performance of the two

Disabling checkbox works, but it's not being processed on onchange

When the checkbox is set to false by the javascript function, the next function doesn't seem to know it has been set to false, it just ignores it.
HTML
<input type="checkbox" name="accs" id="50" class="arms"/>Arm 1: $50<br/>
<input type="checkbox" name="accs" id="60" class="arms"/>Arm 2: $60<br/>
<input type="checkbox" name="accs" id="70" class="neck"/>Neck 1: $70<br/>
<input type="checkbox" name="accs" id="80" class="neck"/>Neck 2: $80<br/>
<div id="total">$500</div>
Javascript: This function disables the checkbox according to the input class
$('.arms, .neck').change(function(){
var myClass = $(this).attr('class');
$('.'+myClass).not(this).prop('checked', false);
});
Javascript next function: The block of code that processes the action if the checkbox is either enabled or disabled
var input = document.getElementsByTagName("input");
var total = document.getElementById("total");
var prev;
for(i=0;i<input.length;i++){
input[i].onchange = function(){
if (this.type != 'text'){
if(this.checked){
$("#total").fadeOut(300);
$("#total").fadeIn(300);
prev=parseFloat(total.innerHTML.replace(/[^0-9\,]+/g,"")) + parseFloat(this.id);
total.innerHTML = "$" + prev.formatMoney(0, ',', '.');
}else{
$("#total").fadeOut(300);
$("#total").fadeIn(300);
prev=parseFloat(total.innerHTML.replace(/[^0-9\,]+/g,"")) - parseFloat(this.id);
total.innerHTML = "$" + prev.formatMoney(0, ',', '.');
}
}
}
}
After clicking all the checkbox you realize that it won't go back to the original price (500) but keeps on adding up.
Fiddle
The reason I haven't used radio is because the form must send the same 'input name' for all the options
I set up a fiddle here: http://jsfiddle.net/MarkSchultheiss/r6MXA/2/
Using this markup:
<input type="checkbox" name="accs" icost="50" class="arms" />Arm 1: $50
<br/>
<input type="checkbox" name="accs" icost="60" class="arms" />Arm 2: $60
<br/>
<input type="checkbox" name="accs" icost="70" class="neck" />Neck 1: $70
<br/>
<input type="checkbox" name="accs" icost="80" class="neck" />Neck 2: $80
<br/>
<div id="total">$500</div>
and this code:
var items = $('input.arms[type="checkbox"], input.neck[type="checkbox"]');
items.change(function () {
var myClass = $(this).attr('class');
$('.' + myClass).not(this).prop('checked', false);
var total = $('#total');
var totalcost = 0;
total.fadeOut(300);
items.filter(':checked').each(function (i) {
var cost = $(this).attr('icost');
totalcost = totalcost + parseFloat(cost);
});
total.text("$" + totalcost + ".00"); // fix your format here as needed
total.fadeIn(300);
});
EDIT: manage the whole check/uncheck cycle thing with a base on the current value in the text total area.
var items = $('input.arms[type="checkbox"], input.neck[type="checkbox"]');
// get the initial total and store in a data for use later (resets as needed)
var total = $('#total');
total.data("currentvalue", parseFloat(total.text().replace(/[^0-9\,]+/g, "")));
items.change(function () {
var currenttotal = total.data("currentvalue");
var myClass = $(this).attr('class');
var thisGroup = $('.' + myClass);
var notme = $('.' + myClass + ':checked').not(this);
var notmeCost = (notme.length ? notme.attr('icost') : ($(this).is(':checked') ? 0 : $(this).attr('icost')));
notme.prop('checked', false);
currenttotal = currenttotal - notmeCost;
total.fadeOut(300);
thisGroup.filter(':checked').each(function (i) {
var cost = $(this).attr('icost');
currenttotal = currenttotal + parseFloat(cost);
});
total.data("currentvalue", currenttotal);
total.text("$" + currenttotal + ".00"); // fix your format here as needed
total.fadeIn(300);
});

jquery if checked all sum x2

I have some problem with sum
example:
if checkbox with id='sms' is checked total sum x2 else x1
<form>
<input onclick="clickCh(this)" type="checkbox" value="1.00"> $1.00<br>
<input onclick="clickCh(this)" type="checkbox" value="2.00"> $2.00<br>
<input id="sms" type="checkbox"> pay via sms<br>
<BR>
<input id="total" type="text" name="total">
</form>
</div>
<script>
var total = document.getElementById("total")
$('#sms').change(function(){
var rise = this.checked ? '2' : '1';
});
function clickCh(caller){
if(caller.checked){
add(caller)
} else {
subtract(caller)
}
}
function add(caller){
total.value = total.value*1 + caller.value*1 * rise
}
function subtract(caller){
total.value = total.value*1 - caller.value*1 * rise
}
</script>
rise's scope is only within this function:
$('#sms').change(function(){
var rise = this.checked ? '2' : '1';
});
Declare it outside then modify it:
var rise;
$('#sms').change(function(){
rise = this.checked ? 2 : 1;
});

How can I shift-select multiple checkboxes like GMail?

In GMail, the user can click on one checkbox in the email list, hold down the Shift key, and select a second checkbox. The JavaScript will then select/unselect the checkboxes that are between the two checboxes.
I am curious as to how this is done? Is this JQuery or some basic (or complex) JavaScript?
I wrote a self-contained demo that uses jquery:
$(document).ready(function() {
var $chkboxes = $('.chkbox');
var lastChecked = null;
$chkboxes.click(function(e) {
if (!lastChecked) {
lastChecked = this;
return;
}
if (e.shiftKey) {
var start = $chkboxes.index(this);
var end = $chkboxes.index(lastChecked);
$chkboxes.slice(Math.min(start,end), Math.max(start,end)+ 1).prop('checked', lastChecked.checked);
}
lastChecked = this;
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<html>
<head>
</head>
<body>
<input type="checkbox" id="id_chk1" class="chkbox" value="1" />Check 1<br/>
<input type="checkbox" id="id_chk2" class="chkbox" value="2" />Check 2<br/>
<input type="checkbox" id="id_chk3" class="chkbox" value="3" />Check 3<br/>
<input type="checkbox" id="id_chk4" class="chkbox" value="4" />Check 4<br/>
<input type="checkbox" id="id_chk5" class="chkbox" value="5" />Check 5<br/>
<input type="checkbox" id="id_chk6" class="chkbox" value="6" />Check 6<br/>
<input type="checkbox" id="id_chk7" class="chkbox" value="7" />Check 7<br/>
</body>
</html>
This is done through fairly simple javascript.
They keep track of the id of the last checked box and when when another checkbox is checked they use the shiftKey event attribute to see if shift was held while clicking the checkbox. If so they set the checked property of each checkbox in between the two to true.
To determine when a box is checked they probably use an onclick event on the checkboxes
It seems like every answer I can find online is completely dependent on jQuery for this. JQuery adds very little functionality. Here's a quick version that doesn't require any frameworks:
function allow_group_select_checkboxes(checkbox_wrapper_id){
var lastChecked = null;
var checkboxes = document.querySelectorAll('#'+checkbox_wrapper_id+' input[type="checkbox"]');
//I'm attaching an index attribute because it's easy, but you could do this other ways...
for (var i=0;i<checkboxes.length;i++){
checkboxes[i].setAttribute('data-index',i);
}
for (var i=0;i<checkboxes.length;i++){
checkboxes[i].addEventListener("click",function(e){
if(lastChecked && e.shiftKey) {
var i = parseInt(lastChecked.getAttribute('data-index'));
var j = parseInt(this.getAttribute('data-index'));
var check_or_uncheck = this.checked;
var low = i; var high=j;
if (i>j){
var low = j; var high=i;
}
for(var c=0;c<checkboxes.length;c++){
if (low <= c && c <=high){
checkboxes[c].checked = check_or_uncheck;
}
}
}
lastChecked = this;
});
}
}
And then initialize it whenever you need it:
allow_group_select_checkboxes('[id of a wrapper that contains the checkboxes]')
Recently, I wrote a jQuery plugin that provide that feature and more.
After including the plugin you just need to initialize the context of checkboxes with the following code snippet:
$('#table4').checkboxes({ range: true });
Here is the link to the documentation, demo & download: http://rmariuzzo.github.io/checkboxes.js/
Well, the post is quite old but here is a solution I've just come across:
jQuery Field Plug-In
I took the jQuery version from #BC. and transformed it into an ES6 version, since the code is actually pretty elegantly solving the problem, in case anyone still stumbles across this...
function enableGroupSelection( selector ) {
let lastChecked = null;
const checkboxes = Array.from( document.querySelectorAll( selector ) );
checkboxes.forEach( checkbox => checkbox.addEventListener( 'click', event => {
if ( !lastChecked ) {
lastChecked = checkbox;
return;
}
if ( event.shiftKey ) {
const start = checkboxes.indexOf( checkbox );
const end = checkboxes.indexOf( lastChecked );
checkboxes
.slice( Math.min( start, end ), Math.max( start, end ) + 1 )
.forEach( checkbox => checkbox.checked = lastChecked.checked );
}
lastChecked = checkbox;
} ) );
}
Got this solution from http://abcoder.com/javascript/jquery/simple-check-uncheck-all-jquery-function/ (now dead):
JavaScript and HTML code
var NUM_BOXES = 10;
// last checkbox the user clicked
var last = -1;
function check(event) {
// in IE, the event object is a property of the window object
// in Mozilla, event object is passed to event handlers as a parameter
if (!event) { event = window.event }
var num = parseInt(/box\[(\d+)\]/.exec(this.name)[1]);
if (event.shiftKey && last != -1) {
var di = num > last ? 1 : -1;
for (var i = last; i != num; i += di) {
document.forms.boxes['box[' + i + ']'].checked = true;
}
}
last = num;
}
function init() {
for (var i = 0; i < NUM_BOXES; i++) {
document.forms.boxes['box[' + i + ']'].onclick = check;
}
}
<body onload="init()">
<form name="boxes">
<input name="box[0]" type="checkbox">
<input name="box[1]" type="checkbox">
<input name="box[2]" type="checkbox">
<input name="box[3]" type="checkbox">
<input name="box[4]" type="checkbox">
<input name="box[5]" type="checkbox">
<input name="box[6]" type="checkbox">
<input name="box[7]" type="checkbox">
<input name="box[8]" type="checkbox">
<input name="box[9]" type="checkbox">
</form>
</body>
Inspired by the fine answers provided, here's a plain JavaScript version using Array.prototype to coerce nodelists to use array functions, rather than for loops.
(function () { // encapsulating variables with IIFE
var lastcheck = null // no checkboxes clicked yet
// get desired checkboxes
var checkboxes = document.querySelectorAll('div.itemslist input[type=checkbox]')
// loop over checkboxes to add event listener
Array.prototype.forEach.call(checkboxes, function (cbx, idx) {
cbx.addEventListener('click', function (evt) {
// test for shift key, not first checkbox, and not same checkbox
if ( evt.shiftKey && null !== lastcheck && idx !== lastcheck ) {
// get range of checks between last-checkbox and shift-checkbox
// Math.min/max does our sorting for us
Array.prototype.slice.call(checkboxes, Math.min(lastcheck, idx), Math.max(lastcheck, idx))
// and loop over each
.forEach(function (ccbx) {
ccbx.checked = true
})
}
lastcheck = idx // set this checkbox as last-checked for later
})
})
}())
<div class="itemslist">
<input type="checkbox" name="one" value="1">
<input type="checkbox" name="two" value="2">
<input type="checkbox" name="three" value="3">
<input type="checkbox" name="four" value="4">
<input type="checkbox" name="five" value="5">
</div>
I realy liked gyo's example and added some code so it works on all checkboxes with the same name.
I also added a MutationObserver so events are also handled on newly added checkboxes.
$(document).ready(function() {
var previouslyClicked = {};
var rangeEventHandler = function(event) {
if (event.shiftKey && previouslyClicked[this.name] && this != previouslyClicked[this.name]) {
var $checkboxes = $('input[type=checkbox][name='+this.name+']').filter(':visible');
var start = $checkboxes.index( this );
var end = $checkboxes.index( previouslyClicked[this.name] );
// console.log('range', start, end, this, previouslyClicked[this.name]);
$checkboxes.slice(Math.min(start,end), Math.max(start,end)+ 1).prop('checked', previouslyClicked[this.name].checked);
} else {
previouslyClicked[this.name] = this;
}
};
if ("MutationObserver" in window) { // https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver/MutationObserver to refresh on new checkboxes
var mutationCallback = function(mutationList, observer) {
mutationList.forEach((mutation) => {
mutation.addedNodes.forEach((node) => {
if (node.nodeName == 'INPUT' && node.type == 'checkbox') {
$(node).on('click.selectRange', rangeEventHandler);
}
});
});
};
var observer = new MutationObserver(mutationCallback);
observer.observe(document, {
childList: true,
attributes: false, // since name is dynamically read
subtree: true
});
}
$('input[type=checkbox][name]').on('click.selectRange', rangeEventHandler);
});
<html>
<head>
</head>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>
First:
<input type="checkbox" name="first">
<input type="checkbox" name="first">
<input type="checkbox" name="first">
<input type="checkbox" name="first">
<input type="checkbox" name="first">
</div>
<div>
Second:
<input type="checkbox" name="second">
<input type="checkbox" name="second">
<input type="checkbox" name="second">
<input type="checkbox" name="second">
<input type="checkbox" name="second">
</div>
</body>
</html>
Found the better solution it works for both select and deselects checkboxes.
Uses a core javascript & Jquery.
$(document).ready(function() {
var $chkboxes = $('.chkbox');
var lastChecked = null;
$chkboxes.click(function(e) {
if(!lastChecked) {
lastChecked = this;
return;
}
if(e.shiftKey) {
var start = $chkboxes.index(this);
var end = $chkboxes.index(lastChecked);
$chkboxes.slice(Math.min(start,end), Math.max(start,end)+ 1).prop('checked', e.target.checked);
}
lastChecked = this;
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<html>
<head>
</head>
<body>
<input type="checkbox" id="id_chk1" class="chkbox" value="1" />Check 1<br/>
<input type="checkbox" id="id_chk2" class="chkbox" value="2" />Check 2<br/>
<input type="checkbox" id="id_chk3" class="chkbox" value="3" />Check 3<br/>
<input type="checkbox" id="id_chk4" class="chkbox" value="4" />Check 4<br/>
<input type="checkbox" id="id_chk5" class="chkbox" value="5" />Check 5<br/>
<input type="checkbox" id="id_chk6" class="chkbox" value="6" />Check 6<br/>
<input type="checkbox" id="id_chk7" class="chkbox" value="7" />Check 7<br/>
</body>
</html>
Here is also another implementation similar to Outlooks multiple selection..
<script type="text/javascript">
function inRange(x, range)
{
return (x >= range[0] && x <= range[1]);
}
$(document).ready(function() {
var $chkboxes = $('.chkbox');
var firstClick = 1;
var lastClick = null;
var range = [];
$chkboxes.click(function(e) {
if(!e.shiftKey && !e.ctrlKey) {
$('#index-' + firstClick).prop('checked', false);
firstClick = $chkboxes.index(this) + 1;
if (firstClick !== null && firstClick !== ($chkboxes.index(this)+1)) {
$('#index-' + firstClick).prop('checked', true);
}
} else if (e.shiftKey) {
lastClick = $chkboxes.index(this) + 1;
if ((firstClick < lastClick) && !inRange(lastClick, range)) {
for (i = firstClick; i < lastClick; i++) {
$('#index-' + i).prop('checked', true);
}
range = [firstClick, lastClick];
} else if ((firstClick > lastClick) && !inRange(lastClick, range)) {
for (i = lastClick; i < firstClick; i++) {
$('#index-' + i).prop('checked', true);
}
range = [lastClick, firstClick];
} else if ((firstClick < lastClick) && inRange(lastClick, range)) {
for (i = 1; i < 100; i++) {
$('#index-' + i).prop('checked', false);
}
for (i = firstClick; i < lastClick; i++) {
$('#index-' + i).prop('checked', true);
}
range = [firstClick, lastClick];
}else if ((firstClick > lastClick) && inRange(lastClick, range)) {
for (i = 1; i < 100; i++) {
$('#index-' + i).prop('checked', false);
}
for (i = lastClick; i < firstClick; i++) {
$('#index-' + i).prop('checked', true);
}
range = [lastClick, firstClick];
}
}
});
});
This is jquery solution that I wrote and use:
All checkboxes have same class named chksel
For faster individual selection a class will carry the order
named chksel_index
Also each checkbox has an attribute named rg that contain same
index
var chksel_last=-1;
$('.chksel').click(function(ev){
if(ev.shiftKey){var i=0;
if(chksel_last >=0){
if($(this).attr('rg') >= chksel_last){
for(i=chksel_last;i<=$(this).attr('rg');i++){$('.chksel_'+i).attr('checked','true')}}
if($(this).attr('rg') <= chksel_last){for(i=$(this).attr('rg');i<=chksel_last;i++){$('.chksel_'+i).attr('checked','true')}}
}
chksel_last=$(this).attr('rg');
}else{chksel_last=$(this).attr('rg');}
})
this solution works for me, also ajax based for DataTables
https://jsfiddle.net/6ouhv7bw/4/
<table id="dataTable">
<tbody>
<tr>
<td><input type="checkbox"></td>
</tr>
<tr>
<td><input type="checkbox"></td>
</tr>
<tr>
<td><input type="checkbox"></td>
</tr>
<tr>
<td><input type="checkbox"></td>
</tr>
</tbody>
</table>
<script>
$(document).ready(function() {
var $chkboxes = $('#dataTable');
var $range = '#dataTable tbody';
var $first = false;
var $indexWrapp = 'tr';
var lastChecked = null;
var $checkboxes = 'input[type="checkbox"]';
$chkboxes.on('click',$checkboxes,function(e) {
if ($first===false) {
lastChecked = $(this).closest($indexWrapp).index();
lastCheckedInput = $(this).prop('checked');
$first=true;
return;
}
if (e.shiftKey) {
var start = lastChecked;
var end = $(this).closest($indexWrapp).index();
$( $range+' '+$indexWrapp).each(function() {
$currIndex=$(this).index();
if( $currIndex>=start && $currIndex<=end ){
$(this).find($checkboxes).prop('checked', lastCheckedInput);
}
})
}
lastCheckedInput = $(this).prop('checked');
lastChecked = $(this).closest($indexWrapp).index();
});
</script>
Here is the Elegant implementation. The idea is to store the first selected input to the lastChecked variable and when the user selects the input field with shiftKey we will run a loop and toggle the inBetween(boolean) and mark all the checkboxes with true value.
Inspired by Wesbos.
let checkboxes = document.querySelectorAll('.wrapper input[type="checkbox"]');
let lastChecked;
function logic(e) {
let inBetween = false;
if (e.shiftKey) {
checkboxes.forEach(checkbox => {
if (checkbox === this || checkbox === lastChecked) {
inBetween = !inBetween;
}
if (inBetween) checkbox.checked = true;
})
}
lastChecked = this;
}
checkboxes.forEach((checkbox, i) => checkbox.addEventListener('click', logic));
.wrapper {
display: flex;
flex-direction: column;
}
<div class="wrapper">
<input type="checkbox" name="one">
<input type="checkbox" name="two">
<input type="checkbox" name="three">
<input type="checkbox" name="four">
<input type="checkbox" name="five">
</div>

Categories