Доброго времени суток.
Столкнулся с достаточно сложной для своего уровня знаний задачей.
Работаю с проектом на Angular, больше в плане верстки. С TS не знаком, но могу разобраться с примером.
На сайте есть фильтр с 3 уровнями вложенности:
- Уровень 1
-- Уровень 2
-- Уровень 2
--- Уровень 3
--- Уровень 3
--- Уровень 3
-- Уровень 2
Если отмечен один из пунктов на 3-м уровне, чекбокс 2-го и 1-го уровня были помечены как indetermenate (3 состояние), соответственно на 2-м уровне аналогично. Если на данном уровне отмечены все чекбоксы (2 состояние), у родительского чекбокса на уровень выше чекбокс так же отмечается и убирается если убран на уровне ниже.
В верстке на jq с реализацией не возникло проблем, ну может чуть криво.
JQ$(document).ready(function () {
$('.b-filterItem_bvar').on('click', '.cbx-label', function () {
// Вложенность 3-го уровня
if ($(this).closest('.b-filterItem').hasClass('fi_b-dropdown')) {
var f_filterItem = $(this).closest('.b-filterItem'),
f_filterItemHead = f_filterItem.find('.b-filterItem_head.checkGroup'),
f_filterItemCount = f_filterItem.find('.bvar_dropdown').find('.cbx-input').length,
f_filterItem_cheked = f_filterItem.find('.bvar_dropdown').find('.cbx-input:checked').length;
if (f_filterItemCount === f_filterItem_cheked) {
// Отмечены все чекбоксы
f_filterItemHead.find('.cbx-label').addClass('checked');
f_filterItemHead.find('.cbx-input').attr("checked", "checked");
if (f_filterItemHead.find('.cbx__checkMark').hasClass('noAllChecked')) {
f_filterItemHead.find('.cbx__checkMark').removeClass('noAllChecked');
}
} else if (f_filterItem_cheked === 0) {
// Не отмечены чекбоксы
f_filterItemHead.find('.cbx-label').removeClass('checked');
f_filterItemHead.find('.cbx-input').removeAttr("checked", "checked");
if (f_filterItemHead.find('.cbx__checkMark').hasClass('noAllChecked')) {
f_filterItemHead.find('.cbx__checkMark').removeClass('noAllChecked');
}
} else if (f_filterItemCount !== f_filterItem_cheked & f_filterItem_cheked !== 0) {
// Отмечены некоторые чекбоксы
f_filterItemHead.find('.cbx-label').removeClass('checked');
f_filterItemHead.find('.cbx-input').removeAttr("checked", "checked");
f_filterItemHead.find('.cbx__checkMark').addClass('noAllChecked');
}
}
// Замена чекбокса родительской группы
var f_cbxBvar = $(this).closest('.b-filterItem_bvar'),
f_itemBodyHead = $(this).closest('.b-filterItem_body').prev('.b-filterItem_head '),
f_cbxBvarCount = f_cbxBvar.find('.cbx-input').length,
f_cbxBvarCount_cheked = f_cbxBvar.find('.cbx-input:checked').length;
if (f_cbxBvarCount === f_cbxBvarCount_cheked) {
f_itemBodyHead.find('.cbx__checkMark').removeClass('noAllChecked');
f_itemBodyHead.find('.cbx-label').addClass('checked');
f_itemBodyHead.find('.cbx-input').attr("checked", "checked");
} else if (f_cbxBvarCount_cheked === 0) {
f_itemBodyHead.find('.cbx__checkMark').removeClass('noAllChecked');
f_itemBodyHead.find('.cbx-label').removeClass('checked');
f_itemBodyHead.find('.cbx-input').removeAttr("checked", "checked");
} else if (f_cbxBvarCount !== f_cbxBvarCount_cheked) {
f_itemBodyHead.find('.cbx__checkMark').addClass('noAllChecked');
f_itemBodyHead.find('.cbx-label').removeClass('checked');
f_itemBodyHead.find('.cbx-input').removeAttr("checked", "checked");
}
});
});
В данном примере отталкиваюсь от события клика по классу .cbx-label и от него до DOMу ищу родителей и меняю их состояние.
HTML<div class="b-filterItem dropdown open">
<!-- 1 уровень -->
<div class="b-filterItem_head checkGroup">
<div class="cbx">
<div class="cbx-label">
<input class="cbx-input" id="letters_cbx_1" type="checkbox" name="" value="">
<div class="cbx__box"></div>
<div class="cbx__checkMark"></div>
</div>
</div>
<div class="b-filterItem_title">
<span class="b-filterItem_arrow f_chO"></span>
<span class="b-filterItem_name text-overflow">Типы</span>
</div>
</div>
<div class="b-filterItem_body f_chB">
<div class="b-filterItem_bvar">
<!-- 2 уровень -->
<div class="b-filterItem">
<div class="b-filterItem_head">
<div class="cbx">
<div class="cbx-label">
<input class="cbx-input" type="checkbox" name="">
<div class="cbx__box"></div>
<div class="cbx__checkMark"></div>
</div>
</div>
<div class="b-filterItem_bvar-title text-overflow">
<span class="text-overflow">Аккаунт</span>
</div>
</div>
<div class="bvar_dropdown f_chB" style="display: none;">
<!-- 3 уровень -->
<div class="bvar_dropdown-item">
<div class="app-spacer"></div>
<div class="cbx-label">
<div class="cbx">
<input class="cbx-input" type="checkbox" name="">
<div class="cbx__box"></div>
<div class="cbx__checkMark"></div>
</div>
<div class="bvar_dropdown-label">
<span class="bvar_dropdown-title text-overflow">
<span class="text-overflow">Facetime аудио</span>
</span>
</div>
</div>
</div>
<!-- 3 уровень -->
<!-- 3 уровень -->
</div>
</div>
<!-- 2 уровень -->
<!-- 2 уровень -->
</div>
</div>
<!-- 1 уровень -->
<!-- 1 уровень -->
</div>
SCSS &__box:before,
&__box:after {
content: "";
position: absolute;
width: 16px;
height: 16px;
border-radius: $borderRadius;
top: 3px;
left: 0;
}
&__box:before {
border: 2px solid $greyCbxColor;
background: transparent;
}
&__box:after,
&__box.indeterminate:after {
background: $blueColor url('../assets/icons/svg/icon-checked.svg') no-repeat center;
background-size: 80%;
color: #fff;
opacity: 0;
}
input[type="checkbox"]:checked ~ &__box:after {
opacity: 1;
}
input[type="checkbox"]:indeterminate ~ &__box:after {
opacity: 1;
background: $blueColor url('../assets/icons/svg/icon--indeterminate-wite.svg') no-repeat center;
}
input[type="checkbox"],
&-input {
position: absolute;
width: 16px;
height: 16px;
margin: 0;
top: 3px;
opacity: 0;
z-index: 1;
}
Знаю, что jq не оптимальный и много лишних движений, но он работает хорошо и без ошибок.
Просто скопировать структуру и прикрепить js файл с jq не получается так как фильтр подгружается компонентом ангуляра.
Хочу понять как без jq можно реализовать похожий функционал с 3 состояниями.
В jq по большому счету бегает вверх и вниз и навешивает классы, галочка (2 состояние) и состояние неопределенного чекбокса (indeterminate) реализованы простым показом класса cbx__checkMark.
P.s.
В примере scss состояния реализованы не с помощью класса cbx__checkMark, а на псевдоэлементах. Думаю это не должно запутать.
Хочу понять как сделать на JS без jQuery или на TypeScrypt. Буду благодарен за подсказки.