-
Урок 1.
00:03:43
Как проходить данный курс
-
Урок 2.
00:15:37
Настраиваем сборку проекта и разбираемся с ТЗ
-
Урок 3.
00:30:45
Работа с модальными окнами
-
Урок 4.
00:25:09
Работа с табами (вкладками) на странице
-
Урок 5.
00:31:10
Работа с формами отправки данных
-
Урок 6.
00:18:46
Работа с формой-калькулятором, часть 1
-
Урок 7.
00:38:54
Работа с формой-калькулятором, часть 2
-
Урок 8.
00:22:02
Работа с таймером
-
Урок 9.
00:11:08
Реализуем модуль с показом изображений
-
Урок 10.
00:11:32
Улучшаем наш проект (анимации, правильное поведение overflow)
-
Урок 11.
00:17:48
Знакомство с проектом и работа с модальными окнами
-
Урок 12.
00:18:01
"Ловим" скролл до конца страницы и модальное окно подарка
-
Урок 13.
00:27:45
Работа со слайдерами на странице
-
Урок 14.
00:39:32
Работаем с формами отправки данных
-
Урок 15.
00:25:28
Создаем маску для номера телефона
-
Урок 16.
00:08:20
Простая "подгрузка" элементов
-
Урок 17.
00:24:12
Подгружаем элементы с сервера
-
Урок 18.
00:19:49
Создаем калькулятор расчета общей суммы
-
Урок 19.
00:16:21
Фильтрация элементов на странице
-
Урок 20.
00:12:09
Замена изображений при наведении
-
Урок 21.
00:26:20
Создаем аккордеон
-
Урок 22.
00:14:25
Работаем с бургер-меню
-
Урок 23.
00:41:27
Создаем плавный скролл на сайте
-
Урок 24.
00:23:22
Реализация Drag & Drop загрузки изображений
-
Урок 25.
00:27:44
Знакомство с проектом и реализация главного слайдера
-
Урок 26.
00:08:30
Всплывающий со временем блок
-
Урок 27.
00:27:12
Создаем видеоплеер в модальном окне
-
Урок 28.
00:12:21
Наследование классов. Создаем общий прототип слайдера и главные слайдеры
-
Урок 29.
00:39:22
Реализация вторичных слайдеров (меньших размеров)
-
Урок 30.
00:23:11
Реализация блока с различиями
-
Урок 31.
00:26:05
Работаем с формами отправки данных
-
Урок 32.
00:11:07
Многостраничность. Как избегать ошибок в коде.
-
Урок 33.
00:10:44
Главный слайдер второй страницы, всплытие событий
-
Урок 34.
00:30:49
Создаем динамический видеоплеер
-
Урок 35.
00:18:39
Функционал загрузки файлов, разворачивания контента и итоги проекта
-
Урок 36.
00:04:09
Введение
-
Урок 37.
00:33:27
Сборка и архитектура проекта
-
Урок 38.
00:22:19
Работа с css-классами и обработчиками событий
-
Урок 39.
00:32:26
Создаем методы для работы с элементами, часть 1
-
Урок 40.
00:13:47
Создаем методы для работы с элементами, часть 2
-
Урок 41.
00:19:47
Создаем анимации fadeIn/fadeOut
-
Урок 42.
00:42:33
Работа со стилями + создаем готовые компоненты кнопок для библиотеки
-
Урок 43.
00:05:20
Создаем анимацию fadeToggle
-
Урок 44.
00:24:15
Создаем Dropdown menu как готовый компонент
-
Урок 45.
00:30:27
Создаем компоненты модального окна и карточки товаров
-
Урок 46.
00:30:06
Динамическое создание модальных окон
-
Урок 47.
00:16:47
Создаем компонент табов (вкладок)
-
Урок 48.
00:15:28
Создаем компонент аккордеона
-
Урок 49.
00:42:43
Создаем компонент слайдера
-
Урок 50.
00:08:07
Создаем сервисы для работы с сервером
-
Урок 51.
00:25:43
Проект только с использованием нашей библиотеки
-
Урок 52.
00:03:16
Итоги и полезные ссылки
-
Урок 53.
00:08:33
Настройка редактора кода
-
Урок 54.
00:07:06
Как работать с JSHint
-
Урок 55.
00:11:20
Локальные сервера
-
Урок 56.
00:31:45
Как работать с сервером в JavaScript ч.1
-
Урок 57.
00:20:05
Как работать с сервером в JavaScript ч.2
-
Урок 58.
00:26:27
Как работать с сервером в JavaScript ч.3
-
Урок 59.
00:44:59
Создаем свою мини-библиотеку в ООП стиле ч.1
-
Урок 60.
00:24:27
Создаем свою мини-библиотеку в ООП стиле ч.2
-
Урок 61.
00:20:34
Пишем парсер на JavaScript
-
Урок 62.
00:17:47
Разбор тестовых заданий: Формируем сценарий
-
Урок 63.
00:19:45
Разбор тестовых заданий: Заполнение матрицы спиралью
try catch блок используется не для таких "примитивных" надобностей. Самое основное, конструкция используется для правильной обработки ошибки. Когда вызов функции может бросить любую ошибку, а не вернуть null.
Что касается querySelector - функция возвращает null, если ничего не найдено. В этом случае, достаточно поставить обычный guard, то есть, if (!hanson) return;
1) Автор, использует try catch блок, такое ощущение, как будто, если один блок (слайд) у нас установлен в display: block, а всем остальные в display: none, то HTML будет отсутствовать на странице. блок с классом .hanson будет в любом случае, потому что css параметры не влияют на это. Следовательно, неконтроллируемого состояния у нас не может быть. Максимум, это к свойству объекта будет присвоен null.
2) Как я писал ранее, если мы обращаемся к переменной, то мы можем установить guard
3) Непонятно, что Иван имел ввиду под ООП-ным проектом. Чем дальше, тем проект меньше напоминает ООП. Мало создать объект. Мы должны больше придерживаться абстракций, а тут, происходит хард кодинг...
Делаем npm install, потом сразу апдейтим npm update
Инсталлируем: npm install --save-dev @babel/plugin-proposal-class-properties
Далее, открыавем gulpfile.js, находим поле options: { (в двух местах, build-js и build-prod-js) и добавляем в него после present: [...],
plugins: [
"@babel/plugin-proposal-class-properties"
]
должно получиться типа:
options: {
presets: [['@babel/preset-env', {
debug: true,
corejs: 3,
useBuiltIns: "usage"
}]],
plugins: [
"@babel/plugin-proposal-class-properties"
]
}
Непонятно, какое ООП без инкапсулирования?
Хрен знает, что там Ванька задумал, пока как-то так
https://github.com/hazartilirot/slider_spa/blob/main/src/js/modules/slider.js
1) По всплывающей кнопке для проброса на верх. animated, fadein лишнее - так как у нас по умолчанию плавно исчезает/появляется кнопка. Кроме опасити, нужно установить визэбилити на хидэн. Иначе кнопки видно не будет, а поинтер на курсоре останется.
window.addEventListener('scroll', () => {
const scrollTop = document.documentElement.scrollTop | 0;
toTheTopBtn.style.opacity =
scrollTop >= mainSectionHeight + headerHeight ? '1' : '0';
toTheTopBtn.style.visibility =
scrollTop >= mainSectionHeight + headerHeight ? 'visible' : 'hidden';
})
2) Что происходит в этом блоке никто не знает.... Я, конечно, догадываюсь, но, меня бесит, когда человек тратит моё время на набор текста, при этом жадный на объяснения. Как по мне - вставь этот блок кода, но потрать больше времени на объяснения. При чём, лучше, если это будет графическое объяснение, т.к. контейнеры прозрачные, и с какими параметрами мы работаем сразу не въедешь.
while (anchorElement.offsetParent) {
anchorElementParent += anchorElement.offsetTop;
anchorElement = anchorElement.offsetParent
}
3) Везде используется выражение let scrollTop = Math.round( document.body.scrollTop || document.documentElement.scrollTop)
при этом, в конце, без проверок, используется
document.documentElement.scrollTop += speed;
document.body.scrollTop += speed;
4) history.replaceState(history.state, document.title, location.href.replace(/\/#[a-z]+$/g, '')) - зачем используется эта функция, если мы прэвэнтим дефолтное поведение? Линк будет без энкера.
5) Нефиговый if на пять условий, который для меня, на 28:30 минуте, пока абсолютно непонятный. У нас динамически меняется document.documentElement.scrollTop, и также, нам, по-идее, известная конечная точка, которую мы получаем в while лупе. Иначе, что там делает второй параметр в smoothScroll? Отсюда вытекает следующий вопрос, а зачем нам prevScrollTop вообще?
6) Зачем такой объёмный код в котором чёрт-знает-что происходит? Пару строчек делают абсолютно тоже самое.
const toTheTopBtn = document.querySelector('.pageup');
toTheTopBtn.addEventListener('click', (e) => {
e.preventDefault();
window.scrollTo({top: 0, left: 0, behavior: 'smooth'})
})
Ванька фрик))) Нужно досмотреть. Может там какая-то гениальная комбинация?)
Поставь задачи.
Объясни с какими проблемами человек может столкнуться
Как решить эту проблему (на словах, не на коде)
Какие особенности.
Научи разбивать таски на мелкие задачи
Протестируй тщательно работоспособность
Какой-то прогресс, время, какие-то непонятные вычисления, скорость - которая от большего значения становиться только медленее, позиционирование раздела без учёта padding-top. У меня складывается такое впечатление, что Ваня, в процессе своего обучения, старается научить ещё и нас. Жду недождусь когда закончу этот курс....
Не понимаю смысла аттачить 25 ивэнт лысынэс, которые бросают ошибку, если нажать на энкэ с псотым хэш тегом. Один бабблинг и всё путём.
В общем, кто хочет нормальное решение, прошу
https://github.com/hazartilirot/portraits_canvas/blob/main/src/js/modals/navigateToSection.js
Нейминг меня не перестаёт удивлять.)
класс often-questions - вообще-то, нужно обозначать как FAQ (Frequently Asked Questions). Это правильно, и сразу понятно.
Классический Аккордион - это следующая выбранная категория, закрывает текущее выпадающую информацию (подменю), и открывает новую. Не закрывается категория тогда, когда есть подкатегория с последующим выпаданием при условии, если на неё нажали. В первом случае, с имплементацией CSS правильно работает. Вторая, имплементация на JS - полная туфта.
Ванька, всё чешет про какие-то алгоритмы. Где они?
Моя версия будет потяжелее для понимания, но интереснее)
https://github.com/hazartilirot/portraits_canvas/blob/main/src/js/modals/makeAnyCanvasFrame.js
Два раза травёрсим, туева хуча querySelector(ов), addEvenListener(ов).
Нет, конечно, всё работает. Но это, по моему мнению, говно код, который не может писать человек с пятью годами опыта. Скажите мне, что это шутка? Такое можно показать на первых уроках по JS DOM, и то, в контексте, практика обработчиков событий с аннотацией - так, никогда не делайте. :)
Вот, не стал делать как Ванька, и получилось красиво)
https://github.com/hazartilirot/portraits_canvas/blob/main/src/js/modals/portfolioMenu.js
https://github.com/hazartilirot/portraits_canvas
forms.js - файл работающий с формами.
inputChecker.js - файл работающий с проверками input полей. В нём две функции, код читается легко, в отличие от твоего кода с масками, где кучка вложенных условий.
regex.js - хранит регулярные выражения по всем input полям. Можно легко адаптировать.
Чтобы запретить пользователю вставлять текст (который противоречит условиям), достаточно добавить аттрибут onpaste="return false" в input.
По 16 уроку, опять ненужные переборы. Всё делается проще. Мы за один проход перезначаем целую строку с разными классами. Мало того, если я не ошибаюсь, перед удалением самой кнопки, мы должны удалить EventListener, который остаётся сидеть в памяти. То есть, мы должны или использовать removeEventListener или же, мы можем воспользоваться опциями и передать третий аргумент {once: true} - который удалит событие автоматически как только у нас пользователь кликнит один раз на кнопку.
button.addEventListener('click', () => {
cards.forEach(card =>
card.className = 'col-sm-3 col-sm-offset-0 col-xs-10 col-xs-offset-1 animated fadeInUp');
button.remove();
}, {once: true})
а что касается реализации через .json файл, лаконичнее использовать axios. Вместо, innerHTML, использовать insertAdjacentHTML, он универсальнее.
Нам не нужен дополнительный селектор, достаточно кнопки. В db.json файле, убрать лишний {"styles": }, достаточно [] и всё что внутри.
button.addEventListener('click', async () => {
const { data } = await axios('assets/styles.json');
data.forEach(({ src, title, link }) =>
button.previousElementSibling.insertAdjacentHTML('beforeend', `
${ title }
Подробнее
`))
button.remove()
}, {once: true})
15-ый урок. Маска - это полная ерунда. В ней нет никакого смысла. Правильнее контролировать каждую цифру особенно те, которые касаются кода оператора. Я показывал регулярные выражения ранее, целый массив валидации.
08:00 минута. Ванька даже не соображает о чём говорит. Это не диапазон. Диапазон
[a-zA-Z] указывается дашом. В квадратных же скобках указывается любое из перечисленных условий (совпадений). То есть, [_d] - может быть или underscore или же любая цифра, причём 100 в данном случае будет рассматриваться не вместе, а по-отдельности. Один ноль ноль.
Тут, есть курс хороший, называется MASTERING REGULAR EXPRESSIONS IN JAVASCRIPT. Вань, посмотри, хоть минимально начнёшь разбираться.)
А если пользователь загрузит файл, у которого имя будет содержать несколько точек в названии файла? :) Использовать точки в имени - это распространенная практика. Такие вещи нужно делать через регулярные выражения и проверять/сплитить файл через расширение файлов.
Ну, и там на 15:00 минуте, ситаксис просто убил
let api;
item.closest('.popup-design') ? api = path.designer : path.question
Уж если писать такую хрень тернарным оператором, тогда правильнее
let api = item.closest('.popup-design') ? path.designer : path.question;
Ох, Ванька.... ты убиваешь меня.... откуда ты взялся на мою голову....
Вот же ботинок) Пять лет опыта - я, блин, в ужасе от таких учителей. Всё стараюсь, не комментировать произношение английский слов, типа, Хайден, Майнут.... это же вообще позорище какое-то. Advanced level(у) он решил научить людей.
Если есть состояние в котором мы храним данные, то подсчёт стоимости должен начинаться с учётом выбора остекление (на верхнем слайдере, который по дефолту деревянный) и, тёплый/холодный сохраняться в нашем состоянии, собственно тот, на который мы нажимает "посчитать". Получается, что ты перешёл в алюминиевое остекление, нажал на тёплый просчитать - а потом заново нужно выбирать) БРЕЕЕЕЕЕЕЕД! )
Таймер сделан неплохо, есть несколько нюансов
1) Почему нельзя изначально экспортировать так:
export default () => {
блок кода
}
и зампортировать в main.js
import timer from "./modules/timer";
2) Что такое первый параметр id, который передаётся в main.js в функцию timer? Зачем он нужен я так и не смог разобраться))))))
const typeOfWindow = document.querySelector('.balcon_icons');
сажаем на него один event listener и далее, собираем dataset.id -шки каждого элемента.
typeOfWindow.addEventListener('click', ({target}) => {
state.form = target.closest('.balcon_icons_img').dataset.id
})
то есть, в , добавляем для каждого
data-id="0", data-id="1", data-id="2" и т.д.
Один слушает, closest объединяет вложенные теги и bubbling поднимает id на самый верх.
О, боги.... такой курс, я смотрю впервые.))))))) Ну, Ваня, ну, даёшь)))))))
+380671111111 или
+38067111-11-11 или
38067111-11-11 или
067111-11-11 или
(067) 111-11-11 и т.д.
всё остальное вызывает ошибку. Ошибка или исчезает сама через 3 секунды или, при следующем нажатии (например, если пользователь нажал быстро три раза 111, такая строка нам не подходит, т.к. она должна начинаться с +, 3, 0), проверит на присутствие ошибки, если она уже присутствует, то предыдущая будет удалена, а новая, встанет вместо неё с новым таймером.
const phones = document.querySelectorAll('input[name=user_phone]');
phones.forEach(i => {
i.addEventListener('input', () => {
const regexArray = [
/^\+$/,
/^\+?3$/,
/^(\+?38)?$/,
/^(\+?38)?\($/,
/^(\+?38)?0$/,
/^(\+?38)?\(?0$/,
/^(\+?38)?\(?0[9675]$/,
/^(\+?38)?\(?0[9675][2356790]$/,
/^(\+?38)?\(?0[9675][2356790]\)?$/,
/^(\+?38)?\(?0[9675][2356790]\)?[\-\s]?$/,
/^(\+?38)?\(?0[9675][2356790]\)?[\-\s]?\d$/,
/^(\+?38)?\(?0[9675][2356790]\)?[\-\s]?\d{2}$/,
/^(\+?38)?\(?0[9675][2356790]\)?[\-\s]?\d{3}$/,
/^(\+?38)?\(?0[9675][2356790]\)?[\-\s]?\d{3}[\-\s]?$/,
/^(\+?38)?\(?0[9675][2356790]\)?[\-\s]?\d{3}[\-\s]?\d$/,
/^(\+?38)?\(?0[9675][2356790]\)?[\-\s]?\d{3}[\-\s]?\d{2}$/,
/^(\+?38)?\(?0[9675][2356790]\)?[\-\s]?\d{3}[\-\s]?\d{2}[\-\s]?$/,
/^(\+?38)?\(?0[9675][2356790]\)?[\-\s]?\d{3}[\-\s]?\d{2}[\-\s]?\d$/,
/^(\+?38)?\(?0[9675][2356790]\)?[\-\s]?\d{3}[\-\s]?\d{2}[\-\s]?\d{2}$/,
];
let statusMessage;
if (!regexArray.some(regex => regex.test(i.value))) {
i.value = i.value.substring(0, i.value.length - 1);
const form = i.parentNode;
statusMessage = document.createElement('div');
statusMessage.classList.add('status');
form.appendChild(statusMessage);
if (statusMessage.previousSibling.className === 'status')
statusMessage.previousSibling.remove();
statusMessage.textContent = 'Use a correct number order for mobile phones'
setTimeout(() => statusMessage.remove(), 3000);
}
})
})
что касается модального окна с таймером 60 секунд - тоже быдло код (или правильнее сказать, быдло имплементация). Зачем нам просто показывать окно? Нам нужно следить или происходит какая-то активность. То есть, например, пользователь скроллит, набирает текст (имя или телефон) или же, двигает курсором мыши. Зачем нам выводить модалку через 60 секунд при любом случае? Это неудобно. Поэтому, мы следим, и если существует хоть какая-то активность, мы ресетим таймер снова на 60 секунд и выводим его исключительно только, если активность пользователя на сайте отсутствует как таковая.
const showModalByTimer = (selector, time) => {
let id;
const resetTimer = () => {
clearTimeout(id);
id = setTimeout(() => {
document.querySelector(selector).style.display = 'block';
document.body.style.overflow = 'hidden';
}, time);
};
window.addEventListener('load', resetTimer, true);
const events = [
'mousedown',
'mousemove',
'keypress',
'scroll',
'touchstart',
];
events.forEach(e => document.addEventListener(e, resetTimer, true));
};
showModalByTimer('.popup', 60000);