Reputation: 33
I've been trying to find the answer to what I think should be an easy fix but I must be looking over something. I'm very new to Javascript and not comfortable with jQuery. Also I am wanting to do this entirely in javascript as this is my 1st real attempt with it. I have a project that uses check boxes and I've had some help getting to where I am but I'm trying to use document.getElementsByClassName to cause a style change to multiple classes.
I've updated my script to the following, using the .querySelectorAll option. I've also added an else to remove the grey text color and enable the check boxes.
function bannerBernini() {
if (document.checkForm1.att_2.checked || document.checkForm1.att_5.checked || document.checkForm1.att_6.checked || document.checkForm2.att_9.checked || document.checkForm2.att_15.checked || document.checkForm3.att_23.checked)
{
var berninis = document.querySelectorAll('.picasso, .matisse');
for(var i = 0; i < berninis.length; i++) {
berninis[i].style.color="#d1d1d1";}
var not_bernini = document.querySelectorAll('#att_3, #att_10, #att_11, #att_13, #att_14, #att_16, #att_17, #att_18, #att_19, #att_20, #att_21, #att_22, #att_24');
for (var j = 0; j < not_bernini.length; j++){
not_bernini[j].disabled=true;}
}
else
{
var berninis = document.querySelectorAll('.picasso, .matisse');
for(var i = 0; i < berninis.length; i++) {
berninis[i].style.color="";}
var not_bernini = document.querySelectorAll('#att_3, #att_10, #att_11, #att_13, #att_14, #att_16, #att_17, #att_18, #att_19, #att_20, #att_21, #att_22, #att_24');
for (var j = 0; j < not_bernini.length; j++){
not_bernini[j].disabled=false;}
}}
**Now I need to figure out how to have check boxes that are shared by 2 of options but not interfere with each other. For instance;
The checkbox 'Single graphic use' only applies to Bernini but the check box 'Silver Finish' applies to both Bernini and Picasso. How do I make that one still enabled when 'Single graphic use' is checked, but not enabled if 'Clip-in Top Rail' is checked?
Here is some of the html if needed
<div id="column1" style="width:250px; padding:5px 10px 5px 10px; float:left">
<form name="checkForm1" id="checkForm1">
<span class="all"><input type="checkbox" id="att_1" name="att_1" class="all" onChange="">Single-sided</span><br />
<span class="bernini"><input type="checkbox" id="att_2" name="att_2" onChange="bannerBernini();">Visible Banner: 33.5" x 36"-78.7"</span><br />
<span class="picasso"><input type="checkbox" id="att_3" name="att_3" onChange="">Medium Duty Spring System</span><br />
<span class="matisse"><input type="checkbox" id="att_4" name="att_4" onChange="">Clip-in Top Rail</span><br />
<span class="bernini"><input type="checkbox" id="att_5" name="att_5" onChange="bannerBernini();">Adjustable Twist Locking Pole</span><br />
<span class="bernini"><input type="checkbox" id="att_6" name="att_6" onChange="bannerBernini();">Single graphic use</span><br />
<span class="all"><input type="checkbox" id="att_7" name="att_7" onChange="">Carrying case included</span><br />
<span class="all"><input type="checkbox" id="att_8" name="att_8" onChange="">Silver finish</span><br />
</form>
Thanks for any assistance you can supply.
I've updated code and here is a link to what I have. I'm close to having what I need (MANY THANKS TO EVERYONE) The only thing I can't figure out now is;
The check box 'Tape-in Bottom Rail' needs to display all the check boxes that apply to the 'bernini and picasso' classes. Right now if you click that check box it works. BUT if you also choose a check box that ONLY applies to 'bernini' OR 'picasso' then uncheck it, options become available that shouldn't. Anyone have a suggestion on how to alleviate that?
http://jsfiddle.net/g_borg/48uhn/1/
Upvotes: 2
Views: 15956
Reputation: 9891
The fact that you have to copy-paste "document.getElementById("att_XYZ").disabled=true;" a dozen times is a sign that you have the wrong approach. This is because if you wanted to add another checkbox to your HTML, you would need to morph your Javascript code as well, which is very unpleasant as it causes bugs.
Instead of naming every single element by hand, what I would do is group the elements that need to be controlled at the same time with a single class name. For example:
<span class="bernini"><input class="groupA" type="checkbox" id="att_2" name="att_2" onChange="bannerBernini();">Visible Banner: 33.5" x 36"-78.7"</span><br />
<span class="picasso"><input class="groupA" type="checkbox" id="att_3" name="att_3" onChange="">Medium Duty Spring System</span><br />
Then, in your code, you could write
var allCheckboxesFromGroupA = $('input.groupA');
foreach(allCheckboxesFromGroupA as item) {
item.disabled = true;
}
EDIT: if you don't want to use jQuery, the approach still applies:
var allCheckboxesFromGroupA = document.getElementsByClassName('groupA');
foreach(allCheckboxesFromGroupA as item) {
item.disabled = true;
}
Upvotes: 0
Reputation: 253308
I'd strongly suggest changing your approach, possibly to the following:
function bannerBernini (e){ // e is the 'change' event, passed automagically to the function.
var _self = this, // the form itself
changed = e.target, // the changed element
spans = _self.getElementsByTagName('span'),
spanClass = e.target.parentNode.tagName.toLowerCase() == 'span' ? e.target.parentNode.className : false,
children; // a stored variable for later
// if the span has a className:
if (spanClass) {
// iterate over all the spans in the form:
for (var i = 0, len = spans.length; i < len; i++){
// if the class of the parent span is *not* in the span we're iterating over:
if (spans[i].className.indexOf(spanClass) == -1){
// store the childNodes of the current span in the previously-declared variable:
children = spans[i].childNodes;
// iterate over those childNodes:
for (var c = 0, cL = children.length; c < cL; c++){
// if the childnode is an element (nodeType === 1), and is of type == 'checkbox':
if (children[c].nodeType === 1 && children[c].type == 'checkbox') {
// set the current checkbox/childNode's disabled attribute to true, or false (the checked-state of the changed element):
children[c].disabled = changed.checked;
}
}
}
}
}
}
// the form we want to act upon:
var form = document.getElementById('checkForm1');
// adding an event-listener and binding an event-handler (for that event):
form.addEventListener('change', bannerBernini);
In up-to-date browsers, though, the above can be shortened considerably to:
function bannerBernini(e) { // again the event ('e') passed automagically
// the className of the parent span element to the changed element:
var filterClass = e.target.parentNode.className;
// if we have a filterClass:
if (filterClass) {
/* we're using array.prototype.forEach to iterate over the collection
returned by "document.querySelectorAll('span')", the 'a' represents
the array element we're iterating over currently: */
[].forEach.call(document.querySelectorAll('span'), function(a){
/* setting the disabled property of the (first/only) input of
type="checkbox" to:
false, if the filterClass is 'all',
or the classList of 'a' contains the filterClass *and*
the changed-element is now checked. */
a.querySelector('input[type="checkbox"]').disabled = !(filterClass === 'all' || a.classList.contains(filterClass)) && e.target.checked;
});
}
}
var form = document.getElementById('checkForm1');
form.addEventListener('change', bannerBernini);
Upvotes: 0
Reputation: 13796
getElementsByClassName() only works on one class at a time.
You could change your function so it looks something like this:
function bannerBernini()
{
if (document.checkForm1.att_2.checked || document.checkForm1.att_5.checked || document.checkForm1.att_6.checked || document.checkForm2.att_9.checked || document.checkForm2.att_15.checked || document.checkForm3.att_23.checked)
{
var classes = ['picasso','matisse']
for (var j = 0; j < classes.length; j++)
{
var berninis = document.getElementsByClassName(classes[j]);
for(var i = 0; i < berninis.length; i++) {
berninis[i].style.color="#d1d1d1";}
{
document.getElementById("Picasso").style.display="none";
document.getElementById("Matisse").style.display="none";
document.getElementById("att_3").disabled=true;
document.getElementById("att_10").disabled=true;
document.getElementById("att_11").disabled=true;
document.getElementById("att_13").disabled=true;
document.getElementById("att_14").disabled=true;
document.getElementById("att_16").disabled=true;
document.getElementById("att_17").disabled=true;
document.getElementById("att_18").disabled=true;
document.getElementById("att_19").disabled=true;
document.getElementById("att_20").disabled=true;
document.getElementById("att_21").disabled=true;
document.getElementById("att_22").disabled=true;
document.getElementById("att_24").disabled=true;
}
}}}
What I did is that I created an array of classes you want to go through, initilized the array with the two classes you want to use and then we simply iterate through the array.
I'd recommend you try jQuery. It's easy to use and this kind of stuff is exactly what it was meant for. Using plain vanilla JS to work with the DOM is sometimes a hassle.
Upvotes: 0
Reputation: 7273
Instead of document.getElementsByClassName
use document.querySelectorAll
and pass in the CSS selector (not just the classnames):
var berninis = document.querySelectorAll('.picasso, .matisse');
for(var i = 0; i < berninis.length; i++) {
berninis[i].style.color="#d1d1d1";
}
Upvotes: 8
Reputation: 2890
To maybe clear up a little more confusion, you can have multiple class names in getElementsByClassName
, but it will select elements having all listed classes.
In other words,
document.getElementsByClassName('picasso matisse')
will select this
<span class="picasso matisse"></span>
but not these
<span class="picasso"></span>
<span class="matisse"></span>
Upvotes: 3
Reputation: 25
You could do it in two steps. First get the elements with the picasso class and then add the lements with the matisse class to the array:
var berninis = document.getElementsByClassName('picasso');
berninis.concat (document.getElementsByClassName('matisse'));
for(var i = 0; i < berninis.length; i++) { // Rest of your code here
Upvotes: 0