Оптимизация JS кода табов
JavaScript начал учить недавно, хотелось бы узнать можно ли сократить запись табов JS чтоб выглядело более оптимизировано.
document.querySelectorAll('.conditions-control__item').forEach((item) => {
item.addEventListener('click', function(e) {
e.preventDefault();
const id = e.target.getAttribute('href').replace('#', '')
document.querySelectorAll('.conditions-control__item').forEach(
(child) => child.classList.remove('conditions-control__item_active')
);
document.querySelectorAll('.conditions-content__item').forEach(
(child) => child.classList.remove('conditions-content__item_active')
);
item.classList.add('conditions-control__item_active');
document.getElementById(id).classList.add('conditions-content__item_active');
});
});
document.querySelector('.conditions-control__item').click();
* { box-sizing: border-box; }
.conditions-control__item { display: inline-block; padding: 0.5em 2em; border: 1px solid #ccc; border-bottom-width: 0; }
.conditions-control__item_active { color: red; }
<div class="conditions-wrap">
<div class="conditions-control">
<a href="#tab-1" class="conditions-control__item">tab_1</a>
<a href="#tab-2" class="conditions-control__item">tab_2</a>
</div>
<div class="conditions-content">
<div id="tab-1" class="conditions-content__item">hello tab-1</div>
<div id="tab-2" class="conditions-content__item">hello tab-2</div>
</div>
</div>
Ответы (1 шт):
const tabs = document.querySelectorAll('.conditions-control__item'),
tabsContent = document.querySelectorAll('.conditions-content__item');
const tabHandler = evt => {
evt.preventDefault();
const id = evt.target.getAttribute('href').replace('#', '');
for (const tab of tabs)
tab.classList.toggle('conditions-control__item_active', tab === evt.target);
for (const content of tabsContent)
content.classList.toggle('conditions-content__item_active', content.id === id);
};
for (const tab of tabs)
tab.addEventListener('click', tabHandler);
tabs[0].click();
* { box-sizing: border-box; }
.conditions-control__item { display: inline-block; padding: 0.5em 2em; border: 1px solid #ccc; border-bottom-width: 0; }
.conditions-control__item_active { color: red; }
.conditions-content__item:not(.conditions-content__item_active) { display: none; }
<div class="conditions-wrap">
<div class="conditions-control">
<a href="#tab-1" class="conditions-control__item">tab_1</a>
<a href="#tab-2" class="conditions-control__item">tab_2</a>
</div>
<div class="conditions-content">
<div id="tab-1" class="conditions-content__item">hello tab-1</div>
<div id="tab-2" class="conditions-content__item">hello tab-2</div>
</div>
</div>
Изменения:
- один общий обработчик клика по вкладке
- переиспользование выборок из DOM (вкладки, элементы контента) выполняемых однократно
- замена
forEachнаfor..of(имхо, так код более читаемый) - использование метода
classList.toggleвместо комбинацииclassList.remove+classList.add
Вместо цикла по всем элементам вкладок, можно просто снимать класс conditions-control__item_active у элемента текущей вкладки, а затем добавлять этот класс элементу той вкладки по которой кликнули.
То есть, заменив
for (const tab of tabs)
tab.classList.toggle('conditions-control__item_active', tab === evt.target);
например на выборку querySelector'ом:
document.querySelector('.conditions-control__item_active').classList.remove('conditions-control__item_active');
evt.target.classList.add('conditions-control__item_active');
Альтернативами выборке одного элемента с "активным" классом может быть живая коллекция (исп. document.getElementsByClassName) или запоминание текущей вкладки в переменной.
При замене цикла на две операции, переменная tabs станет не нужна: клик по первой вкладке после инициализации можно выполнить выбрав ее вызовом document.querySelector('.conditions-control__item'), т.к. результатом вернется первый элемент вкладки в документе.
Все же, я считаю что такие оптимизации здесь будут уже чересчур: циклы не добавят заметных тормозов, при этом они проще и нагляднее.
Выборки при каждом клике тоже не очень сильно затормозят, но они заодно и снижают читаемость - а контекст вопроса (как следует из уточнений) понимает под "оптимизацией" улучшение в балансе между скоростью и лаконичностью кода.
Если все же хочется больше оптимизаций именно в производительности, то вот (без циклов + делегирование обработки событий на захвате):
const CL_TAB = 'conditions-control__item',
CL_CONTENT = 'conditions-content__item',
CL_TAB_ACTIVE = CL_TAB + '_active',
CL_CONTENT_ACTIVE = CL_CONTENT + '_active',
tabsContent = [...document.querySelectorAll('.' + CL_CONTENT)];
let curTab = null,
curContent = tabsContent[0];
document.querySelector('.conditions-control')
.addEventListener('click', tabHandler, true);
document.querySelector('.' + CL_TAB).click();
function tabHandler(evt) {
if (!evt.target.classList.contains(CL_TAB)) return;
evt.preventDefault();
if (evt.target === curTab) return;
curTab?.classList.remove(CL_TAB_ACTIVE);
(curTab = evt.target).classList.add(CL_TAB_ACTIVE);
curContent.classList.remove(CL_CONTENT_ACTIVE);
const id = evt.target.getAttribute('href').replace('#', '');
(curContent = tabsContent.find(content => content.id === id))
.classList.add(CL_CONTENT_ACTIVE);
}
* { box-sizing: border-box; }
.conditions-control__item { display: inline-block; padding: 0.5em 2em; border: 1px solid #ccc; border-bottom-width: 0; }
.conditions-control__item_active { color: red; }
.conditions-content__item:not(.conditions-content__item_active) { display: none; }
<div class="conditions-wrap">
<div class="conditions-control">
<a href="#tab-1" class="conditions-control__item">tab_1</a>
<a href="#tab-2" class="conditions-control__item">tab_2</a>
</div>
<div class="conditions-content">
<div id="tab-1" class="conditions-content__item">hello tab-1</div>
<div id="tab-2" class="conditions-content__item">hello tab-2</div>
</div>
</div>