1

I am trying to make a function that will:

  1. run through all of the checkboxes I have checked in the html document
  2. only allow 'checkboxLimit' to be checked (i.e. 4 checkboxes out of the 10 can be checked in the document)
  3. return an array of the id's of the checked checkboxes (which I call cbl)

Currently, my issue has something to do with the scope of the onclick function.

function getCheckboxes(checkboxLimit) {
    // clear cbl
    cbl = ['','','',''];

    // contstuct list of checkbox tags 
    inputs = document.getElementsByTagName('input');
    var checkboxes = [];
    for ( var i = 0 ; i < inputs.length ; i++ ) {
        if (inputs[i].type == 'checkbox') 
        checkboxes.push(inputs[i]);
    }

All of the code above is functional, but the code below is where I run into issues. The function that is executed after 'onclick' works nicely, because if I alert cbl in the function, it works as I like (shown below). However, once cbl is alerted after the 'onclick' function, it is no longer the same.

    // construct list of checked checkboxes (limited)
    for ( var i = 0 ; i < checkboxes.length ; i++ ) {
        // if any checkbox is clicked
        checkboxes[i].onclick = function() {
            var checkCount = 0;
            // run through each of the checkboxes
            for ( var j = 0 ; j < checkboxes.length ; j++ ) {
                // if index checkbox is checked
                if ( checkboxes[j].checked == true ) {
                    // add to count
                    checkCount++;
                    // if count is above limit, uncheck
                    // otherwise add to list        
                    if ( checkCount > checkboxLimit)
                        this.checked = false;
                    else
                        cbl[checkCount-1] = checkboxes[j].id;
                }
            }
            // alert that displays cbl how I want
            alert(cbl);
        }
        // alert that does not display cbl how I want
        alert(cbl);
    }
}

So is there some way I can get past this scope issue? I would prefer staying away from JQuery, but whatever can get me to have functional code will work at this point.

5
  • 1
    I am unable to understand the checkboxes[i].onclick. Why is that in a loop? Have you tried putting a custom class like class='mycheckbox' and binding the onclick function to that? Looping through checkboxes and binding function seems odd. Commented Sep 21, 2014 at 23:09
  • 1
    A reference of how it is done with buttons. Can be used on checkboxes too. stackoverflow.com/questions/13818839/… Commented Sep 21, 2014 at 23:11
  • what do you expect cbl to be when it doesn't display how you want ? Commented Sep 21, 2014 at 23:13
  • So the first alert shows the id's of the checkboxes that are checked. For example: checkbox1,checkbox2,checkbox3,checkbox4 Whereas the function outside of the onclick is alerting the cleared ['','','',''] that I assigned in the first line of the function. So as it works right now, the cbl in the function is not affected at all by onclick. Commented Sep 21, 2014 at 23:22
  • You would need a closure inside your loop like (function(i){checkbokes[i].onclick = function(){}})(i); or you'll be at the end of your loop, but that's not the behavior you're looking for anyways. I'm going to make you a function that will allow you to test for a max and minimum. Hold on. Commented Sep 21, 2014 at 23:23

3 Answers 3

1

Try something more like this:

function checkboxValidate(collection, min, max){
  var mn = typeof min === 'undefined' ? 0 : min;
  var mx = typeof max === 'undefined' ? Infinity : max;
  var cb = [], n = 0;
  for(var i=0,l=collection.length; i<l, i++){
    var c = collection[i];
    if(c.type === 'checkbox' && c.checked){
      cb.push(c); n++;
    }
  }
  if(n < mn){
    // somthing.innerHTML = 'Minimum Requirement Failure';
    return false;
  }
  else if(n > mx){
    // somthing.innerHTML = 'Maximum Requirement Failure';
    return false;
  }
  else{
    return cb;
  }
}
anotherElement.onclick = function(){
  var checkedArray = checkboxValidate(document.getElementsByTagName('input'), 0, 3));
  if(checkedArray && checkedArray[0]){
    // checkedArray has each checkbox Element - more properties than just `id`
  }
  else{
    // checkedArray would be false or not have any checked with min 0
  }
}

Adjust as needed.

Sign up to request clarification or add additional context in comments.

Comments

0

A non cross-browser crude sample. Perhaps you can get something from it:

Fiddle

function limitCheck(n) {
    var x = [];
    document.addEventListener('click', function(e) {
        var i, t = e.target;
        if (t.type === 'checkbox') {
            if ((i = x.indexOf(t.id)) > -1) {
                console.log('remove ' + i + ', ' + t.id);
                x.splice(i, 1);
            } else if (x.length < n) {
                x.push(t.id);
            } else {
                console.log('LIM');
                e.preventDefault();
                e.stopPropagation();
            }
            console.log(x);
        }
    });
} 

limitCheck(4);

Without debug:

function limitCheck(n) {
    var x = [];
    document.addEventListener('click', function(e) {
        var i, t = e.target;
        if (t.type === 'checkbox') {
            if ((i = x.indexOf(t.id)) > -1) {
                x.splice(i, 1);
            } else if (x.length < n) {
                x.push(t.id);
            } else {
                e.preventDefault();
                e.stopPropagation();
            }
        }
    });
} 

Comments

0

You can put a click listener on an ancestor of the checkboxes, probably they're in a form so use that. You also need to account for a reset button, and for buttons that are checked by default (so you also need to run the function on page load to pre-populate the list):

var checkboxLimit = 4;
var cbl;

function limitCheckboxes(form, event) {

  // Clear checkbox list
  var cblTemp = [];

  // get element that the click came from
  var tgt = event.target || event.srcElement;

  // See if it was a reset
  var reset = tgt.type == 'reset';

  // Count checked checkboxes
  // If click was from reset, reset the checkbox first
  var cb, cbs = form.getElementsByTagName('input');

  for (var i=0, iLen=cbs.length; i<iLen; i++) {
    cb = cbs[i];

    if (cb.type == 'checkbox') {
      cb.checked = reset? cb.defaultChecked : cb.checked;
      if (cb.checked) {
        cblTemp.push(cb.id);
      }
    }
  }

  // If too many, remove the last one that was checked,
  // uncheck it and show a message
  if (cblTemp.length > checkboxLimit && tgt.type == 'checkbox') {
    cblTemp.splice(cblTemp.indexOf(tgt.id), 1);
    tgt.checked = false;
    console.log('removed ' + tgt.id);
  }

  // update cbl
  cbl = cblTemp;

  // Debug
  console.log(cbl);
}

You can also get the checked checkboxes in one go using querySelectorAll:

  var cb, cbs = document.querySelectorAll('input[type="checkbox"]');

and the for loop is simpler:

  for (var i=0, iLen=cbs.length; i<iLen; i++) {
    cb = cbs[i];
    cb.checked = reset? cb.defaultChecked : cb.checked;

    if (cb.checked) {
      cblTemp.push(cb.id);
    }
  }

It would be even better to use a class, so you can have other checkboxes that aren't part of the restricted set.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.