Skip to content

Commit e73090a

Browse files
committed
[LES-17.5/st-compl] working-with-styles
Practice with "classList" prop, "add/remove/toggle/contains()" meth's. Worth noting: - "showing/hiding" the text block. - working with "tabs". - working with "accordion". - all this lesson/practice (note the solutions). FS-dev: B-3 / JS basic
1 parent dec664e commit e73090a

File tree

7 files changed

+397
-0
lines changed

7 files changed

+397
-0
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
4+
<head>
5+
<meta charset="UTF-8">
6+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
7+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
8+
<title>DOM</title>
9+
<link rel="stylesheet" href="./styles.css">
10+
<script src="./index.js" defer></script>
11+
</head>
12+
13+
<body style="background-color: #1c1b21; text-align: center">
14+
<img src="./logo.svg" alt>
15+
<div class="panel">I love this!</div>
16+
<input class="input" id="input">
17+
<button class="button">Change</button>
18+
<div class="notification notification_hide">Changed!</div>
19+
</body>
20+
21+
</html>
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
'use strict';
2+
3+
const panelMessage = document.querySelector('.panel');
4+
const input = document.querySelector('.input');
5+
const button = document.querySelector('.button');
6+
const notificationMessage = document.querySelector('.notification');
7+
let notificationTimer; // переменная для хранения ID таймера
8+
9+
// DRY
10+
function changeMessage() {
11+
const inputValue = input.value;
12+
13+
if (!inputValue) {
14+
return;
15+
}
16+
17+
panelMessage.textContent = inputValue;
18+
input.value = '';
19+
notificationMessage.classList.remove('notification_hide');
20+
21+
// очищаем предыдущий таймер
22+
clearTimeout(notificationTimer);
23+
24+
// устанавливаем новый таймер (заносим в переменную ID таймера)
25+
notificationTimer = setTimeout(() => {
26+
notificationMessage.classList.add('notification_hide');
27+
}, 1000);
28+
}
29+
30+
// изменения сообщения через "click" по кнопке
31+
function changeMessageByClick() {
32+
changeMessage();
33+
}
34+
35+
button.addEventListener('click', changeMessageByClick);
36+
37+
// изменения сообщения через "keydown/Enter" в поле input
38+
function changeMessageByKeydown(event) {
39+
// без объекта event не обойтись, т.к. в нём определяем code кнопки.. "Enter"
40+
if (event.code === 'Enter') {
41+
changeMessage();
42+
}
43+
}
44+
45+
input.addEventListener('keydown', changeMessageByKeydown);
Lines changed: 25 additions & 0 deletions
Loading
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
4+
<head>
5+
<meta charset="UTF-8">
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
7+
<title>DOM - practice</title>
8+
<link rel="stylesheet" href="./style.css">
9+
<script src="./index.js" defer></script>
10+
</head>
11+
12+
<body>
13+
<div class="card">
14+
<h3>Заголовок карточки</h3>
15+
<p>Этот текст виден всем и всегда.</p>
16+
<div class="hidden" id="details">
17+
<p>А это дополнительная информация, которая изначально скрыта от пользователя.</p>
18+
</div>
19+
<button id="toggle-btn">Читать далее</button>
20+
</div>
21+
22+
<div class="tabs-container">
23+
<div id="tabs-buttons">
24+
<button class="tab-btn active" data-tab="tab1">Котики</button>
25+
<button class="tab-btn" data-tab="tab2">Собачки</button>
26+
<button class="tab-btn" data-tab="tab3">Еноты</button>
27+
</div>
28+
<div id="tabs-content">
29+
<div class="tab-pane active" id="tab1">Содержимое про котиков 😺</div>
30+
<div class="tab-pane" id="tab2">Содержимое про собачек 🐶</div>
31+
<div class="tab-pane" id="tab3">Содержимое про енотов 🦝</div>
32+
</div>
33+
</div>
34+
35+
<div class="accordion">
36+
<div class="accordion-item">
37+
<h3 class="accordion-header">Раздел 1</h3>
38+
<div class="accordion-content">
39+
<p>Содержимое первого раздела. Оно изначально скрыто.</p>
40+
</div>
41+
</div>
42+
<div class="accordion-item">
43+
<h3 class="accordion-header">Раздел 2</h3>
44+
<div class="accordion-content">
45+
<p>Содержимое второго раздела. Оно также скрыто.</p>
46+
</div>
47+
</div>
48+
<div class="accordion-item">
49+
<h3 class="accordion-header">Раздел 3</h3>
50+
<div class="accordion-content">
51+
<p>Содержимое третьего раздела. И оно тоже скрыто.</p>
52+
</div>
53+
</div>
54+
</div>
55+
</body>
56+
57+
</html>
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
'use strict';
2+
3+
// Задание 1:
4+
// Создать простую механику "Показать/Скрыть" для текстового блока.
5+
/*
6+
<div class="card">
7+
<h3>Заголовок карточки</h3>
8+
<p>Этот текст виден всем и всегда.</p>
9+
<div id="details" class="hidden">
10+
<p>А это дополнительная информация, которая изначально скрыта от пользователя.</p>
11+
</div>
12+
<button id="toggle-btn">Читать далее</button>
13+
</div>
14+
*/
15+
// Твоя задача:
16+
// Напиши JavaScript код, который:
17+
// 1. При клике на кнопку с id="toggle-btn".
18+
// 2. Находит блок с id="details" и переключает у него класс hidden.
19+
// 3. Бонус (необязательно): Попробуй сделать так, чтобы текст на кнопке менялся с "Читать далее" на "Скрыть", и обратно, в зависимости от того, виден ли скрытый текст.
20+
21+
const details = document.querySelector('#details');
22+
const toggleBtn = document.querySelector('#toggle-btn');
23+
24+
// function showHideAddInfo() {
25+
// details.classList.toggle('hidden');
26+
//
27+
// if (details.classList.contains('hidden')) {
28+
// toggleBtn.textContent = 'Читать далее';
29+
// } else {
30+
// toggleBtn.textContent = 'Скрыть';
31+
// }
32+
// }
33+
//
34+
// toggleBtn.addEventListener('click', showHideAddInfo);
35+
36+
// ?? альтернативное решение
37+
toggleBtn.addEventListener('click', () => {
38+
// 1. Проверяем, был ли элемент скрыт ДО переключения
39+
const wasHidden = details.classList.contains('hidden');
40+
41+
// 2. Меняем текст кнопки в зависимости от того, что было
42+
toggleBtn.textContent = wasHidden ? 'Скрыть' : 'Читать далее';
43+
44+
// 3. Переключаем класс
45+
details.classList.toggle('hidden');
46+
});
47+
48+
// Задание 2:
49+
// Нужно написать JavaScript-код, который при клике на любую из кнопок-вкладок (.tab-btn) делает следующее:
50+
// 1. Убирает класс active со всех кнопок.
51+
// 2. Убирает класс active со всех панелей с контентом (.tab-pane).
52+
// 3. Добавляет класс active только той кнопке, по которой кликнули.
53+
// 4. Находит соответствующую панель с контентом и добавляет ей класс active. (Подсказка: кнопки и панели связаны. У кнопки есть data-tab="tab1", а у панели id="tab1").
54+
/*
55+
<div class="tabs-container">
56+
<div id="tabs-buttons">
57+
<button class="tab-btn active" data-tab="tab1">Котики</button>
58+
<button class="tab-btn" data-tab="tab2">Собачки</button>
59+
<button class="tab-btn" data-tab="tab3">Еноты</button>
60+
</div>
61+
<div id="tabs-content">
62+
<div class="tab-pane active" id="tab1">Содержимое про котиков 😺</div>
63+
<div class="tab-pane" id="tab2">Содержимое про собачек 🐶</div>
64+
<div class="tab-pane" id="tab3">Содержимое про енотов 🦝</div>
65+
</div>
66+
</div>
67+
*/
68+
69+
const tabsContainer = document.querySelector('.tabs-container');
70+
const allTabBtn = document.querySelectorAll('.tab-btn');
71+
const allTabPanel = document.querySelectorAll('.tab-pane');
72+
73+
function activeManipulation(event) {
74+
const target = event.target;
75+
76+
if (target.tagName === 'BUTTON') {
77+
allTabBtn.forEach((tabBtn) => {
78+
tabBtn.classList.remove('active');
79+
});
80+
81+
allTabPanel.forEach((panel) => {
82+
panel.classList.remove('active');
83+
});
84+
85+
target.classList.add('active');
86+
const tabValue = target.dataset.tab;
87+
88+
// allTabPanel.forEach((panel) => {
89+
// if (panel.id === tabValue) {
90+
// panel.classList.add('active');
91+
// }
92+
// });
93+
94+
// ?? что бы не перебирать все элементы.. можно сразу по ID отрабатывать
95+
const targetPanel = document.getElementById(tabValue);
96+
targetPanel.classList.add('active');
97+
}
98+
}
99+
100+
tabsContainer.addEventListener('click', activeManipulation);
101+
102+
// Задание 3:
103+
// Создать работающий "аккордеон".
104+
/*
105+
<div class="accordion">
106+
<div class="accordion-item">
107+
<h3 class="accordion-header">Раздел 1</h3>
108+
<div class="accordion-content">
109+
<p>Содержимое первого раздела. Оно изначально скрыто.</p>
110+
</div>
111+
</div>
112+
<div class="accordion-item">
113+
<h3 class="accordion-header">Раздел 2</h3>
114+
<div class="accordion-content">
115+
<p>Содержимое второго раздела. Оно также скрыто.</p>
116+
</div>
117+
</div>
118+
<div class="accordion-item">
119+
<h3 class="accordion-header">Раздел 3</h3>
120+
<div class="accordion-content">
121+
<p>Содержимое третьего раздела. И оно тоже скрыто.</p>
122+
</div>
123+
</div>
124+
</div>
125+
*/
126+
// Напиши JavaScript, который при клике на любой заголовок (.accordion-header):
127+
// 1. Переключает класс active у нажатого заголовка.
128+
// 2. Бонус (сложная часть): Сделай так, чтобы одновременно мог быть открыт только один раздел. То есть, при открытии нового раздела, любой другой, который был открыт до этого, должен закрыться.
129+
// Намёк: Обрати внимание на CSS. Тебе не нужно напрямую стилизовать или скрывать .accordion-content. Вся магия происходит, когда ты добавляешь/убираешь класс active у заголовка .accordion-header.
130+
131+
const accordion = document.querySelector('.accordion');
132+
const allAccHeaders = document.querySelectorAll('.accordion-header');
133+
134+
function openCloseAccordion(event) {
135+
const target = event.target;
136+
137+
// определение "именно" заголовка
138+
if (target.classList.contains('accordion-header')) {
139+
// проверка состояния (закрыт.. открыт)
140+
const isAlreadyActive = target.classList.contains('active');
141+
142+
// закрытие всех элементов
143+
allAccHeaders.forEach((header) => {
144+
header.classList.remove('active');
145+
});
146+
147+
// исходя из первичного состояния, далее.. открытие или закрытие
148+
if (!isAlreadyActive) {
149+
target.classList.add('active');
150+
}
151+
}
152+
}
153+
154+
accordion.addEventListener('click', openCloseAccordion);

0 commit comments

Comments
 (0)