I am validating some check boxes and would like for the user to be able to select only 4 (from 7 possible) and disable the others if the current box is being checked(if there are already 3 checked) or enable the everything if the current box is being unchecked. I'm really not sure where's the problem. This is my first experience with JavaScript...
function verify_selected(selected_check_box_id) {
var count = 0;
var selected_check_boxes = new Array();
var check_boxes = new Array();
var inputs = document.getElementsByTagName("input");
for( var i in inputs ) {
if( inputs[i].type == "checkbox" ) check_boxes.push( inputs[i] );
}
// get current checkbox
for( var i in check_boxes ) if( check_boxes[i].id == selected_check_box_id ) var current_check_box = check_boxes[i];
var current_check_box_is_checked = current_check_box.checked;
// get all "checked"
for( var i in check_boxes ) {
if( check_boxes[i].checked ) {
selected_check_boxes.push( check_boxes[i] );
count += 1;
}
}
if( current_check_box_is_checked ) {
// checking
if( count < 4 ) {
current_check_box.checked = true;
// count = 4 - disabling
if( count == 4 ) {
for( var i in check_boxes ) {
if( !check_boxes[i].checked ) check_boxes[i].disabled = true;
}
}
}
else current_check_box.checked = false;
} else {
// unchecking
// count is < 4 -> enabling
for( var i in check_boxes ) {
check_boxes[i].disabled = false;
}
}
}
Any help is welcome,
thanks in advance.
There were a couple of things wrong. Lets give the good version first.
I also put up a demo at: http://jsbin.com/ajimi
function verify_selected(currentCheckbox) {
var count = 0;
var selected_check_boxes = []; // this will be fine...
var check_boxes [];
var inputs = document.getElementsByTagName("input");
for( var i in inputs ) {
if( inputs[i].type == "checkbox" ) check_boxes.push( inputs[i] );
}
// get all "checked"
for( var i in check_boxes ) {
if( check_boxes[i].checked ) {
count += 1;
}
}
if( currentCheckbox.checked && (count == 4)) {
for( var i in check_boxes )
if( !check_boxes[i].checked )
check_boxes[i].disabled = true;
} else {
for( var i in check_boxes )
check_boxes[i].disabled = false;
}
}
In the original version, you've got a piece of code which looked like:
if (count < 4) {
if (count == 4) {
Not gonna happen. So, that was corrected.
As you saw also in another answer, we changed the function to take out looking for an ID. Rather than figuring out the ID in some separate function (I assume you're tracking the "last clicked" by some other function which occurs), just use the this modifier to pass it into the function.
Alright, last but not least, what this would look like in jQuery. Hopefully this will help a little as to understanding how it works and why it's worth using:
(see example: http://jsbin.com/ihone)
function limitSelected(e) {
// get all of your checkboxes
var checkBoxes = $(e.currentTarget).parent().children().filter('input:checkbox');
// get the number of checkboxes checked, if 4, we'll disable
var disableCheckBoxes = (checkBoxes.filter(':checked').length == 4);
// enable checkboxes if we have < 4, disable if 4
checkBoxes.filter(':not(:checked)').each(function() {
this.disabled = disableCheckBoxes;
});
}
// when the document is ready, setup checkboxes to limit selection count
// if you have a particular div in which these checkboxes reside, you should
// change the selector ("input:checkbox"), to ("#yourDiv input:checkbox")
$(function() {
$('input:checkbox').click(limitSelected);
});
The other thing I will note about this version is that it works on the group of checkboxes within a div, as opposed to your version which will pick up checkboxes on the entire page. (which is limiting.
From a brief skim, your code seems much too complex for the task.
Can I suggest using something like jquery? You can quite easily select the relevant check boxes using the psudeo-selector ':checked'. Also, have a look at this check box tutorial.
If you don't want to use a library, I'd suggest first creating a function that can count the number of checked check boxes. Then create a function that can disable or enable all unchecked check boxes. Finally, combine the two, and register a function to trigger on the click event for the check boxes.
As cofiem said, your code looks rather complex for what you want to achieve; I recommend breaking it down into a few, smaller functions, to re-use code and make less complex.
First, implement a function to get all of the checkboxes on the page:
function getCheckboxes()
{
var inputs = document.getElementsByTagName("input");
var checkboxes = new Array();
for(var i=0;i<inputs.length;++i) {
if(inputs[i].type=="checkbox")
checkboxes.push(inputs[i]);
}
return checkboxes;
}
Then a function to enable/disable the checkboxes:
function setDisabled(state) {
var checkboxes = getCheckboxes();
for(i=0;i<checkboxes.length;++i) {
//Only unchecked checkboxes will need to be enabled/disabled
if(!checkboxes[i].checked)
checkboxes[i].disabled = state;
}
}
Now implement your function to verify whether the checkboxes need to be enabled or disabled:
function verify_selected(checkbox) {
var checkboxes = getCheckboxes();
var count=0;
for(i=0;i<checkboxes.length;++i) {
if(checkboxes[i].checked)
count++;
}
if(count>=4)
setDisabled(true);
else
setDisabled(false);
}
I have changed your function declaration to pass the actual checkbox object rather than an identifier string; this is much easier to call the function:
<input type="checkbox" onClick="verify_selected(this);">
//Insert 7 of these..
As you can see the code is much easier to read and maintain, and it is much less complex.
Related
I have a table with a lot of td's. What would be the most efficient way to select selection 1 to selection 2 and everything in-between?
On selection 1 i give the selected td an id and on selection 2 i give that selection another id in order to later on look for those id's.
Current code:
// small helpers
function _for(e,f) { var i, len=e.length; for(i=0;i<len;i++){ f(e[i]); }}
function _id(e) { return document.getElementById(e); }
// VARs
var main = _id('monthTables'),
td = main.querySelectorAll('td'),
go = false,
stop = false,
i = 0,
s1i = 0,
s2i = 999;
// Loop throught td's
_for(td, function(e){
if( e.id == 'sel1' ) { go = 1; s1i = i; }
if( e.id == 'sel2' ) { stop = 1; s2i = i; }
if( s1i < s2i && go ) {
if(go) { e.classList.add('range'); }
}
if( stop ) { go = 0; }
}) // end loop
Live example (select two dates):
http://cdn.rawgit.com/tommiehansen/lightRange/master/test.html
The code is good in my opinion, maybe you could add few optimizations (like stopping the loop when found the second selected id )
Example for the loop:
function _for(e,f) {
var i, len=e.length;
for(i=0;i<len;i++){ if(f(e[i]) === false) break; }
}
so it will break when the function f returns false
// Loop throught td's
_for(td, function(e){
//...
if( stop ) return false;
}) // end loop
Also i suggest you to use getElementsByTagName instead of querySelectorAll if you want a bit more performance ..but maybe that's an evil micro optimization
An other optimization would be to start looping from the first selected td, maybe storing the index when you select the td and using that value in that code you posted as initial index
Probably a beginner question, but I have an array json object that I'm looping through to pull out certain values. If these values equal "NO", then I want to hide a div using jquery. This should be simple enough, but I don't know how many items the array can contain so I'll have to make sure to dynamically grab all of the number/value pairs.
My JSON is similar to below:
"account" : [{
"ID":1,
"transferAllowed": "NO"
},{
"ID":2,
"transferAllowed": "YES"
}]
My loop:
//define variable that references the values I need from the json object
var account = this.json.account;
for ( var i = 0; i < account.length; i++ ) {
//compare all values of transferAllowed however many that may be. If they all equal No, then hide div with jquery $("#div").hide()
}
Any help is appreciated. Thanks.
Try flipping the problem, like defining a "null hypothesis" for a science experiment: to disprove that all items say "NO", you need only find a single value that doesn't say "NO".
This means that you only need a simple check in the loop to find your answer:
var account = this.json.account;
// Start with the hypothesis that everything will say "NO"
var allDisallowed = true;
for ( var i = 0; i < account.length; i++ ) {
if ( account[i].transferAllowed != "NO" ) {
// Hypothesis disproved!
allDisallowed = false;
}
}
if ( allDisallowed ) {
$("#div").hide();
}
Since you only need one value to be allowed, you can actually stop at the first one you see using a break; statement:
for ( var i = 0; i < account.length; i++ ) {
if ( account[i].transferAllowed != "NO" ) {
// Hypothesis disproved!
allDisallowed = false;
// No need to finish the loop
break;
}
}
Alternatively, if you wrap the loop up in a function, you can save defining some variables:
function areAllDisallowed( account ) {
for ( var i = 0; i < account.length; i++ ) {
if ( account[i].transferAllowed != "NO" ) {
// At least one element is not a "NO", so result is false
return false;
}
}
// We will only reach this line if we never return false
// Therefore, we know that all elements say "NO"
return true;
}
if ( areAllDisallowed(this.json.account) ) {
$("#div").hide();
}
(As pointed out in a comment, the position of var i in the for loop could cause confusion later, but I've left it as-is to make only the changes that answer your specific question.)
try this :)
var account = this.json.account;
var hide_count = 0;
for ( var i = 0; i < account.length; i++ ) {
if(account[i].transferAllowed=="NO") hide_count++;
}
if(hide_count == account.length){
$("#div").hide();
}
I have a html multi-select select box. When i select/de-select values in a change event is fired on it.
Whats the best way to know from the event that the event is because of an element being selected or deselected? I don't want its value, just whether the change event is because of 'selection' or 'de-selection'.
Making a few assumptions here, you have a multi-select and you just need to know if there has been a selection or not.
Then, you just need to check the length of selected options with a cached value.
See this fiddle: http://jsfiddle.net/2uudx/1/
Not the most elegant or optimized way, but a direction.
var sel = 0;
$('#mySelect').on("change", function() {
var newsel = $(this).find(":selected").length;
if (newsel > sel) {
// selected
} else {
if (newsel < sel) {
// de-selected
} else {
// no-change
}
}
sel = newsel;
});
If you really need to compare the values which were selected or de-selected, then instead of caching the length, you cache the selected values in an array. On change, compare the currently selected values to the cached array.
Hope that gives a suitable pointer.
Update:
Using values to compare more accurately, as mentioned above by way of arrays.
Here is a sample fiddle: http://jsfiddle.net/u9XE4/3/
var sel = [];
$('#s1').on("change", function() {
var newsel = $(this).val();
var same = compareArray(newsel, sel);
if (same) {
// no-change
} else {
if ((!same) && ((newsel.length == sel.length) || (newsel.length < sel.length))) {
// de-selected
} else {
// selected
}
}
sel = newsel;
});
function compareArray(a, b) {
$.each( a, function( key, value ) {
var index = $.inArray( value, b );
if( index != -1 ) {
return true;
}
});
return false;
}
hope that helps.
I have a form that is a table built with php to pull a bunch of information from the database. In these, i have a checkbox that, when clicked, pulls the value of the estimatedCost and throws it into a JavaScript function that calculates it and keeps a running total of all the objects checked.
What i'm trying to do is create a check all and uncheck all option that will still pass the needed variables into the other javascript functions. Let me demonstrate with some code:
This draws the checkbox next to the title.
foreach($replace as $key => $value)
{
$jScript = 'onclick=\'calcTotals("'.$replace[$key]['estimatedCost'].'","'.$replace[$key]['optionItem_id_replaceOrRepair'].'","'.$replace[$key]['service_title'].'","'.$replace[$key]['maintenanceitem_id'].'");\'';
$checkbox = sprintf('<input type="checkBox" name="ids[]" id="%s" value="%s" %s>', $replace[$key]['maintenanceitem_id'], $replace[$key]['maintenanceitem_id'], $jScript).' ';
$replace[$key]['title'] = $checkbox.$replace[$key]['title'];
$replace[$key]['estimatedCost'] = $replace[$key]['estimatedCost'];
}
This is the current check all and uncheck all links:
echo '<a href="#" onClick=\'setCheckboxes("budgeting", true); return false;\'>Check All</a> | ';
echo '<a href="#" onClick=\'setCheckboxes("budgeting", false); return false;\'>Uncheck All</a>';
Now the current functions i have in javascript:
function setBudgetCheckboxes(the_form, do_check) {
var elts = (typeof(document.forms[the_form].elements['ids[]']) != 'undefined')
? document.forms[the_form].elements['ids[]']
: (typeof(document.forms[the_form].elements['ids[]']) != 'undefined')
? document.forms[the_form].elements['ids[]']
: document.forms[the_form].elements['ids[]'];
var elts_cnt = (typeof(elts.length) != 'undefined')
? elts.length
: 0;
if (elts_cnt) {
for (var i = 0; i < elts_cnt; i++) {
elts[i].checked = do_check;
var name = document.getElementById(name);
} // end for
} else {
elts.checked = do_check;
} // end if... else
return true;
}
And the other, which handles the clicks one at a time:
function calcTotals(amount, type, service, name) {
if(amount[0] == '$') {
amount = amount.substr(1,amount.length);
}
var id = type+"_"+service+"_selected";
var grand_id = "Grand_selected";
var grand_service_id = "Grand_"+service+"_selected";
var type_id = type+"_selected";
var checked = document.getElementById(name).checked;
var multiplier = -1;
if(checked) {
multiplier = 1;
}
amount = amount * multiplier;
addBudgetValue(amount, id);
addBudgetValue(amount, grand_id);
addBudgetValue(amount, grand_id+"_h");
addBudgetValue(amount, type_id);
addBudgetValue(amount, grand_service_id);
addBudgetValue(amount, grand_service_id+"_h");
}
function addBudgetValue(amount, id) {
var current_value = document.getElementById(id).innerHTML;
var curtmp = 0;
if(current_value == "$0") {
current_value = amount;
}
else {
curtmp = parseFloat(current_value.substr(1,current_value.length));
current_value = (curtmp+parseFloat(amount));
}
var newVal = "$"+Number(current_value).toFixed(2);
if(newVal == "$0.00")
newVal = "$0";
document.getElementById(id).innerHTML = newVal;
}
So the question is this: How do you get the check all to check all the boxes, and pass the information into the calcTotals function so that the values are added correctly?
Here's a jsfiddle that does what (I think) you're looking for: http://jsfiddle.net/kz9gU/1/
I'm using a custom data-* attribute to store the estimated cost (spec compliant as per HTML5), so it's defined in the checkboxes' tags. Each checkbox then calls an updateTotal() function when checked/unchecked, but doesn't pass any arguments. updateTotal() then loops through the checkboxes, reading each amount from the data-cost attribute, and displays the sum.
The check/uncheck functions then don't need to calculate any values; they just check/uncheck everything and call updateTotal() afterwards.
Edit: Heh, just realized you're not using jQuery. I'm just starting to take jQuery for granted, even though I actually don't use jQuery myself. What you see in the fiddle is probably the longest piece of jQuery-based JS, I've actually written :)
At any rate, it's almost always a good idea to use some sort of library to smooth out browser differences, and jQuery is king of the heap right now.
But library or not, the code should give you an idea of how you could proceed.
I hope I understood you currectly, you want a check/uncheck all feature? http://jetlogs.org/jquery/jquery_select_all.html after that get the checkbox array with php and pass the information, in PHP do a loop to check the values of the array and execute your statements.
I have a form and unfortunately built it without the help of external libraries (which I am now cursing myself for not including). So my form validation isn't just form.validate() or something similar, it's going to have to be an archaic javascript method (time constraints means I cannot implement external libraries because it'd involve rebuilding the form).
All I want is to check all the fields have been filled in - no email validation or post-code validation etc.
I tried a simple version:
if((document.getElementById("fieldA").value != "") || (document.getElementById("fieldB").value != "")){
alert("form okay");
}else{
alert("form not okay");
}
but this doesn't work. The alternative to this would be to nest 45 if statements detecting each field individually but this is tedious and unfeasible.
you can loop trough elements in the form with
document.forms[0].elements
like
var d = document.forms[0].elements
var l = d.length;
for(var i = 0; i < l; i ++) {
var element = d[i];
var type = element.type;
var value = element.value;
var class = element.className;
}
dropdown:
document.forms[0].select.value
radiobuttons:
for (i=0;i<document.forms[0].radios.length;i++) {
if (document.forms[0].radios[i].checked) {
var value = document.forms[0].radios[i].value;
}
}
thanks to external libraries we don't need that to do ourselves these days;)
Your boolean logic is wrong - you want AND (&&) not OR (||) if you want to make all the fields required. As it stands, the validation only checks to see if one field has been filled in.
I think that Caspar's answer is excellent. As an addition to it, what we have on old forms is a function to get elements by class name (not written by us):
function getElementsByClassName(className, tag, elm){
var testClass = new RegExp("(^|\\s)" + className + "(\\s|$)");
var tag = tag || "*";
var elm = elm || document;
var elements = (tag == "*" && elm.all)? elm.all : elm.getElementsByTagName(tag);
var returnElements = [];
var current;
var length = elements.length;
for(var i=0; i<length; i++){
current = elements[i];
if(testClass.test(current.className)){
returnElements.push(current);
}
}
return returnElements;
}
Then we put a class="validate" on each form element that needs validating and have this function run on form submit (where getFormElementValue is a function that handles the various different form elements as in Caspar's answer):
function validate(){
var elementArray = ( getElementsByClassName('validate') ) ;
for ( i=0; i<elementArray.length; i++){
if( getFormElementValue( elementArray[i] ) == '' ){
alert( 'Form not OK' );
return false;
}
}
}
The nice thing about this is that you can easily define which elements are compulsory and which are not without resorting to a list of names/IDs.