Welcome to the Treehouse Community

The Treehouse Community is a meeting place for developers, designers, and programmers of all backgrounds and skill levels to get support. Collaborate here on code errors or bugs that you need feedback on, or asking for an extra set of eyes on your latest project. Join thousands of Treehouse students and alumni in the community today. (Note: Only Treehouse students can comment or ask questions, but non-students are welcome to browse our conversations.)

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and a supportive community. Start your free trial today.

JavaScript Build an Interactive Form

Neil Bircumshaw
seal-mask
.a{fill-rule:evenodd;}techdegree
Neil Bircumshaw
Full Stack JavaScript Techdegree Student 14,597 Points

error message to show when check boxes are not checked and submit button is clicked.

So, I'm trying to get a red error message to show when not a single checkbox is checked, to notify to the user they must check a box. I've essentially looped through the checkboxes and if any are checked, then the message stays hidden, else the message is shown. Funnily enough it works only when the last checkbox is ticked, if any other checkbox is ticket the message still shows regardless, telling the user they need to check a checkbox even though they have. Any thoughts?

Just as a side note, this code is wrapped in a click handler already.

HTML:

<fieldset class="activities">
        <legend id ="register">Register for Activities</legend>
        <label id = "main_conf" ><input type="checkbox" class="checkbox" id ="all"name="all" value="200"> Main Conference — $200</label>
        <label id = "js_frames"><input type="checkbox" class="checkbox" id ="js-frameworks"name="js-frameworks" value="100"> JavaScript Frameworks Workshop — Tuesday 9am-12pm, $100</label>
        <label id = "js_libs"><input type="checkbox" class="checkbox" id = "js-libs"name="js-libs" value="100"> JavaScript Libraries Workshop — Tuesday 1pm-4pm, $100</label>
        <label id = "express_work"><input type="checkbox" class="checkbox" id="express" name="express" value="100"> Express Workshop — Tuesday 9am-12pm, $100</label>
        <label id = "node_js"><input type="checkbox" class="checkbox" id="node"name="node" value="100"> Node.js Workshop — Tuesday 1pm-4pm, $100</label>          
        <label id = "build_tool"><input type="checkbox" class="checkbox" id="build-tools"name="build-tools" value="100"> Build tools Workshop — Wednesday 9am-12pm, $100</label>
        <label id = "npm_work"><input type="checkbox" class="checkbox" id="npm"name="npm" value="100"> npm Workshop — Wednesday 1pm-4pm, $100</label>

      </fieldset>

JS:

const activities = document.getElementsByClassName("checkbox");
const register_message = document.createElement("span");
    const register_parent = document.getElementById("register");

for(let i = 0; i < activities.length; i++) {
        if(activities[i].checked){
        register_message.style.display = "none";
        }

        else{ e.preventDefault();
         register_message.textContent = "Please pick an activity you would like to study.";
         register_message.style.color = "#cc0000";
         register_message.style.display = "block";
         register_message.className = "warning";
         register_parent.appendChild(register_message);

7 Answers

Hi Neil,

So the problem is that if an earlier checkbox is checked, the if condition will be true and you'll hide the message. But then you'll continue looping over the later checkboxes which aren't checked. This means the else block will execute and this will show the message despite an earlier checkbox being checked.

I recommend that you change the logic of the program a little bit. The for loop can be responsible for seeing if any boxes are checked and then after the loop you can do your if/else check to see if the message should be displayed or not.

Here's some comments to help guide you that you can translate to working code:

// initialize a boolean flag variable to false - assume that no checkboxes are checked

// loop over the checkboxes
    // if checked
        // set the boolean flag to true since we have at least one checked now
        // break out of the loop - we know at least one was checked, there's no point in checking the rest.

// if/else to decide if the message should be displayed or not based on the boolean flag set earlier

Let me know if there's anything you don't understand here.

Neil Bircumshaw
seal-mask
.a{fill-rule:evenodd;}techdegree
Neil Bircumshaw
Full Stack JavaScript Techdegree Student 14,597 Points

Okay I'm still struggling a little bit haha. I tired this and it didn't seem to run

let flag = false;
        for(let i = 0; i < activities.length; i++) {
        if(activities[i].checked){flag = true;
        if(flag = true){register_message.style.display = "none";}

        else{ e.preventDefault();
         register_message.textContent = "Please pick an activity you would like to study.";
         register_message.style.color = "#cc0000";
         register_message.style.display = "block";
         register_message.className = "warning";
         register_parent.appendChild(register_message);
        }}
        }

do I need to add a break statement or somthing to break out the loop?

Neil Bircumshaw
seal-mask
.a{fill-rule:evenodd;}techdegree
Neil Bircumshaw
Full Stack JavaScript Techdegree Student 14,597 Points

in fact it's sort of working, but when I go to uncheck the check button again and then they all become unchecked when I hit submit the message doesn't come up any more. Any idea? This is my code.

for(let i = 0; i < activities.length; i++) {
        if(activities[i].checked){flag = true;}
        if(flag === true){register_message.style.display = "none";}

         else{ e.preventDefault();
         register_message.textContent = "Please pick an activity you would like to study.";
         register_message.style.color = "#cc0000";
         register_message.style.display = "block";
         register_message.className = "warning";
         register_parent.appendChild(register_message);
        }
        }

You want to do your if/else check after the loop.

Something more like this:

let boxChecked = false;

for (let i = 0; i < activities.length; i++) {
    if (activities[i].checked) {
        boxChecked = true;
        break;
    }
}

if (boxChecked) {
    // hide the message
} else {
    // show the message and prevent default action
}
Neil Bircumshaw
seal-mask
.a{fill-rule:evenodd;}techdegree
Neil Bircumshaw
Full Stack JavaScript Techdegree Student 14,597 Points
for(let i = 0; i < activities.length; i++) {
        if(activities[i].checked){boxChecked = true;break;}}

        if(boxChecked){register_message.style.display = "none";}

         else{ e.preventDefault();
         register_message.textContent = "Please pick an activity you would like to study.";
         register_message.style.color = "#cc0000";
         register_message.style.display = "block";
         register_message.className = "warning";
         register_parent.appendChild(register_message);
        }

like this? Because this code still doesn't seem to work D: it works when I go on the page and don't select anything, then the message pops up. it works when I check something - the message doesn't appear, but then when I uncheck something, the message doesn't come back again when all check boxes are unchecked, is it because the loop has already run?

This code is being run each time the submit button is pressed correct?

Check my code again in the comment. Are you missing the initialization of the boolean variable to false before the loop?

I don't see that in your most recent code here.

Neil Bircumshaw
seal-mask
.a{fill-rule:evenodd;}techdegree
Neil Bircumshaw
Full Stack JavaScript Techdegree Student 14,597 Points

Yes the "boxChecked" variable has just been moved to the top with all my other variables, but it's still there.

Yes, each time I hit the submit button which is what the event is bound to it will run the code. However as stated it doesn't seem to run after I check the box then uncheck the box and hit submit

Ok, but are you initializing it to false right before the for loop starts as I have it in my code examples?

The behavior you're describing would happen if it's not getting set back to false each time.

The overall idea is that when the submit button is clicked, it should set that boolean value to false and then loop through all of them to see if it should be set to true.

Neil Bircumshaw
seal-mask
.a{fill-rule:evenodd;}techdegree
Neil Bircumshaw
Full Stack JavaScript Techdegree Student 14,597 Points

I found the problem. I moved the let variable: let boxChecked = false; outside the click handler. I put it at the top along with my other variables. When I put it just above my loop it then seems to work! Why exactly is this? Shouldn't it be able to reach outside the function to get the boxChecked variable because of the scope?

Yes, it would still be able to access that variable if you moved it outside the click handler.

However, if you put it at the top of the script where it will only be run one time then it will only get set to false initially but never again. Once it gets set to true then it could never be false again.

By having it in the click handler then it gets set to false every time before you loop and check if any boxes are checked.