Обычно я не пишу готовые скрипты в ответах, но этот случай мне почему-то приглянулся.
Во-первых, нужно немного изменить код, чтобы обычные и контрольный чекбоксы чем-то разделялись, я предлагаю использовать для этого связку fieldset — legend, ибо семантика. А уж стили на все это натянуть можно любые и при необходимости добавить другие элементы оформления. Главное — fieldset с классом "js-tree-box" (вдруг, в форме будут другие филдсеты) и легенда.
Код можно засунуть в head страницы — он будет послушно ждать, пока на странице появятся чекбоксы и пользователь их кликнет.
<fieldset class="js-tree-box">
<legend>
<label><input type="checkbox" name="obl[]" value="61" />Ростовская</label>
</legend>
<label><input type="checkbox" name="gorod[]" value="346780">Азов</label>
<label><input type="checkbox" name="gorod[]" value="346400">Новочеркасск</label>
<label><input type="checkbox" name="gorod[]" value="344000">Ростов-на-Дону</label>
<label><input type="checkbox" name="gorod[]" value="347900">Таганрог</label>
</fieldset>
<fieldset class="js-tree-box">
<legend>
<label><input type="checkbox" name="obl[]" value="61" />Ростовская</label>
</legend>
<label><input type="checkbox" name="gorod[]" value="346780">Азов</label>
<label><input type="checkbox" name="gorod[]" value="346400">Новочеркасск</label>
<label><input type="checkbox" name="gorod[]" value="344000">Ростов-на-Дону</label>
<label><input type="checkbox" name="gorod[]" value="347900">Таганрог</label>
</fieldset>
…и так далее.
<script>
(function(){ // этот кусок кода автономен, выносим в отдельную область данных
function clicker(e){ // выполняется при любом кликие или изменении
var legendObject;
var fieldsetObject;
var controlCheckbox;
var trigger = e.srcElement||e.target; // кто вызвал событие?
if (!trigger.tagName || trigger.tagName.toLowerCase() != "input" || trigger.type.toLowerCase() != "checkbox") return; // если это не чекбокс — делать нам тут нечего
// пробегаемся по всем родителям
var testElement = trigger;
while (testElement){
if (!testElement.tagName) return; // неявно — корень дерева, и мы ничего не нашли
var tagName = testElement.tagName.toLowerCase();
if (tagName == "legend") { // по пути встретили legend
legendObject = testElement;
} else if (tagName == "fieldset" && /(^|\s)+js-tree-box(\s|$)+/.test(testElement.className)) { // или fieldset с нужным классом
fieldsetObject = testElement;
break;
}
testElement = testElement.parentNode; // выбираем родителя и повторяем итерацию для него
};
if (!fieldsetObject) return;
if (legendObject){ // т.е., чекбокс внутри легенды
var controlCheckboxValue = trigger.checked;
var inputs = fieldsetObject.getElementsByTagName("input");
for(var i=0; i<inputs.length; i++){
var input = inputs[i];
if (input.type.toLowerCase() == "checkbox" && input != controlCheckbox){
input.checked = controlCheckboxValue;
};
};
} else {
// находим «контрольный» чекбокс
if (legendObject = fieldsetObject.getElementsByTagName("legend")[0]){
var inputs = legendObject.getElementsByTagName("input");
for(var i=0; i<inputs.length; i++){
var input = inputs[i];
if (input.type.toLowerCase() == "checkbox"){
controlCheckbox = input;
break;
};
};
};
if (!controlCheckbox) return;
var controlCheckboxValue = true;
// пробегаемся по всем чекбоксам
var inputs = fieldsetObject.getElementsByTagName("input");
for(var i=0; i<inputs.length; i++){
var input = inputs[i];
// есть неотмеченные?
if (input.type.toLowerCase() == "checkbox" && input != controlCheckbox && !input.checked){
controlCheckboxValue = false;
};
};
// устанавливаем значение в контрольный
controlCheckbox.checked = controlCheckboxValue;
}
};
// щелчков будет намного меньше, чем элементов — отлавливаем события через bubbling
if (document.addEventListener){
document.addEventListener('change', clicker, false);
document.addEventListener('click', clicker, false);
} else {
document.attachEvent('onchange', clicker);
document.attachEvent('onclick', clicker);
};
})();
</script>
Главное, господа, разделение кода и представления. А прописывать
каждому чекбоксу уникальный id — проще застрелиться.