Toggle Input Sections with JavaScript
Ever been in a situation where you’ve needed to disable an entire group of <input /> elements? Perhaps a scenario where you have one radio button that controls its children (grouped elements) and another radio element that controls the other group? You’re in for a treat!
I have constructed an unobtrusive and idiot proof method for you to implement into your JavaScript library.
First there are two prerequisites.
- You’ll need a copy of my getElementsByClass() function
- You’ll also need a copy of Scott Andrew’s addEvent() function
If you don’t feel like reading up on Scott Andrew (or you already know what I’m talking about), then don’t worry, I’ll have a copy for you soon enough.
The Premise and idea behind ‘Unobtrusiveness’
Basically, I wanted a way to be able to drop in an external js file, and let my xHTML take of the rest. The purpose, of course, is to add an enhancement layer and not let my radio button groups rely on JavaScript. This you should know is a big no-no in usability culture.
My second goal was that it needed to be easy to implement. This, of course, requires more thought process in the beginning. I figure if I could explain this to my wife, then it’s a hit. With that said, let’s get started! :)
A typical Scenario
Let’s take a look at the following xHTML.
<div class="toggleSection">
<div class="toggleGroup">
<label for="topCheck1">The Enabler</label>
<input id="topCheck1" type="radio" name="a" class="toggleEnable" checked="checked" />
<label><input type="checkbox" class="toggleField" /></label>
<label><input type="checkbox" class="toggleField" /></label>
<label><input type="checkbox" class="toggleField" /></label>
</div>
<div class="toggleGroup">
<label for="topCheck2">The Enabler</label>
<input id="topCheck2" type="radio" name="a" class="toggleEnable" />
<label><input type="checkbox" class="toggleField" /></label>
<label><input type="checkbox" class="toggleField" /></label>
<label><input type="checkbox" class="toggleField" /></label>
</div>
<div class="toggleGroup">
<label for="topCheck3">The Enabler</label>
<input id="topCheck3" type="radio" name="a" class="toggleEnable" />
<label><input type="checkbox" class="toggleField" /></label>
<label><input type="checkbox" class="toggleField" /></label>
<label><input type="checkbox" class="toggleField" /></label>
</div>
</div>
Pretty straight forward I should hope. Take careful note of the class names on each element. You will see that the entire section is nested with <div class=’toggleSection’> and each group is nested with <div class=’toggleGroup’>.
Upon further inspection, you’ll also see that the ‘enablers’ (in this case, the radio buttons) have a class of ‘toggleEnable’ and each field inside (which are optional) have a class of ‘toggleField’. With all this said, the function should take care of the rest based on this set up.
elementToggler.js and the bunch
Alright, let’s break it down. Rather than posting the code on this page, I’ll send you to the direct source to take a peak at a each externals js files:
- elementToggler.js
- common.js (which includes getElementsByClass)
- functionAddEvent.js
Assuming elementToggler interests you the most, you’ll notice two functions. The first function that is called is attachEnablers() which (in this case) attaches the toggleSections() function to be fired upon the click event to the radio buttons. Then on page load, we run the toggleSections() function to put the document in its correct state.
With all that said, you should already have a pretty good idea what we’ve set up here. Which leads me to believe you can figure out the rest just by playing with the demo.
See Section Toggler in action!
Instructions
Just incase of a panic attack and you’re ready to lose all patience on how to implement it. Follow these steps carefully (Don’t worry - it’s too easy to mess up)
- Separate your sections with div.toggleSection
- Separate your groups (within sections) with div.toggleGroup
- Create your element enablers with input.toggleEnable
- Add .toggleField to any element that wants to be part of the action inside your toggle groups
Special Notes
- Take careful notice on the use of the style property in the toggleSections function. I’ve fiddled and faddled to get this to work with className.replace() - which would obviously be preferable - but there were quite a few browser bugs (in both IE and Firefox) which there seems to be a memory leak. It simply would just stop working after about 5 to 10 clicks.
- There are instructions in the elementToggler.js file just incase my website is down (which makes it pointless of me even noting this)
- Hey McFly, say Hi to you Mom for me




August 19th, 2005 at 6:51 am
That’s some pretty slick js. I didn’t quite understand before you wrote the post when you were talking about enabling and disabling sets of inputs, but now it all makes sense and I can see using this. In fact, I’ll be creative and try to find a place to implement it. :)
August 19th, 2005 at 9:16 am
Haha. It’s too bad it wasn’t what you had in mind. But I’m glad you can still make use of it. Now you have me interested in what your friend did for you.
August 19th, 2005 at 9:41 am
Looks pretty cool, nice example too, that clearly shows the need for such a script :)
August 19th, 2005 at 10:32 am
re: Toggling class names instead of inline styles.
The trick here is that you have to cast your class names for an element to a string explicitly.
Basically, there are two different approaches to take with this.
1) Add the inactive class to your markup by default, then let Javascript replace the appropriate classes for selected states.
2) Leave your markup untouched, and explicitly add the appropriate class depending on the state of the “toggle field”.
For option 1, you can replace this:
groups[k].style.color = ‘black’;
With:
groups[k].className = (groups[k].className).toString().replace(’soft’, ‘full’);
But remember that is assuming all your classes with the class “toggleGroup” also have been given the class “soft” (in your markup).
If you don’t want to touch your markup, then you will need to add one additional check to add the class “soft” or “full” initially. Otherwise the replace statement I showed you above will never have anything to replace.
The quick and dirty way to do it is by putting the following just under the replace statement:
if ((groups[k].className).toString().indexOf(’full’)
August 19th, 2005 at 11:43 am
Here’s the greasemonkey script that we (cough) didn’t write. It works on the geocaching.com map page to undisable the zoom controls. For some reason the “click on the map” controls (pan, zoom in, zoom out) don’t work after all the disabled attributes are removed, but I guess that’s server-side. Fun, fun.
August 19th, 2005 at 12:20 pm
> undisable …
Nice ;) That’s sorta like enable, right?
It sure is weird that the geocaching people are holding such basic features behind lock and key, that’s weak sauce.
GMaps could do all that stuff and more, and their API is free to use so it wouldn’t cost Geocaching anything to allow people to use their rich feature set.
August 19th, 2005 at 12:30 pm
Neat. But I think you should’ve used fieldsets instead of DIVs, it’s the fieldset role to group fields after all.
It just seems… more logical… (give those poor divs a break)
August 19th, 2005 at 12:59 pm
@Justin:
groups[k].className = (groups[k].className).toString().replace(’soft’, ‘full’);This is where the problem was occuring. notice the “Note” in the article about .replace().
not to mention, with the example that you gave, there may very well be other class names associated with it…meaning, it needs to be able to take in <div class=’someClass bold full‘> and you wouldn’t want to replace the entire class string. And so, even with the correct code that I originally wrote for it (not posted in article body)…
groups[k].className = groups[k].className.replace(’ full’,”);groups[k].className =+ ‘ soft’;
Both IE and Firefox choked after about 5 to 10 clicks randomly.
August 19th, 2005 at 1:02 pm
Justin, I failed to mention how valuable your input is…just wanted to let you know that ;)
I think we’re on the same page on setting the inline JS style. I know, it’s not efficient per sé, but it’s full proof.
August 19th, 2005 at 1:04 pm
@Masklinn
Actually, you’re right. I’ll modify the script and the demo appropriately. Thanks for pointing this out. It’s been a long day and I should have noticed this.
// Edit: What I went ahead and did is changed the div.toggleGroup’s to be fieldset’s. But for the case of possibly div.toggleSection’s (the broader section wrappers), I left them as div’s since there are often times the fieldset may not always entail the entire section.
August 19th, 2005 at 1:19 pm
Dustin, look a little closer at my method of toggling the class name vs. your method.
I sent you the complete file (via email) using my example methods, which works in IE and Firefox (even after lots of clicks).
You cannot call the “replace” method on an elements className attribute. You have to cast the value of className to a string, then call the replace method on that.
When you do something like this:
groups[k].className =+ ‘ soft’;
You run a very high risk of allowing your className value to be overrun with space-separated values of “soft”. You should always check that the class doesn’t already exist before doing such a thing.
Firefox has a nice feature that allows you to see the source for your HTML, after it’s been altered by the DOM, you’ll want to leverage that ability. Once you’ve done a few clicks to toggle the class names, click and drag your mouse across the screen to highlight the affected elements. With the region highlighted, right click and select “View Selection Source”. If you see something that looks like:
…
Then you know you’ve got a problem.
August 19th, 2005 at 1:21 pm
Arghhh, there’s a div in there wrapping my “…” above. I forgot you allowed HTML in these comments.
Dustin, could you convert that to encoded HTML?
August 19th, 2005 at 1:26 pm
RE: fieldsets vs. divs.
Fieldsets are not any better, imo. They present their own layout/presentation problems that do not exist when using divs. If you don’t believe me, try playing around with fieldsets, legends, and CSS properties like “float” and “background-color”… you’ll be pulling your hair out in no time.
Using a fieldset is a preference, not a best practice. I don’t think it bears any relavence on the validity of this example.
August 19th, 2005 at 2:24 pm
alrighty. I finally understand what’s going on. I was appending .full or .soft even if there was nothing to replace. Thus…
//Update!
The code should be strong.
On the deal with fieldset vs. div, I’ll leave in the fieldset in the js. If it’s that important for someone they can manipulate the js to say either preference they have. Or to have the catch all, just use the ‘*’ if they want full flexibility. I’m not going to choose a side on this matter. I would say, however, semantically fieldset would be preferable, but it does - in fact- have the problems Justin mentioned.
So voila. I hope it’s still useful as it was in the beginning.
Btw, Jason, I gotta check out that greasemonkey script you posted.
August 19th, 2005 at 8:44 pm
Please do…but it’s really basic. You guys are true codemonkeys. If I can get javascript to do what I want it to do, I smile, nod, and move on. Now design on the other hand…that’s where I get picky.
I started playing around with a fun implementation of your script, but work got busy today and I never finished it. Tonight has consisted of bouncing from HomeDepot to BedBathandBeyond to WalMart and then a late dinner at Fazoli’s. …and just so you know…drilling holes in a steel bed frame is NOT an easy task. Maybe I’ll get back to playing with code by Monday. :)