Дивовижний світ веброзробки – Telegram
Дивовижний світ веброзробки
2.91K subscribers
83 photos
7 videos
1 file
183 links
Дивовижний світ веброзробки — тепер і в твоєму телеграмі. Анонси відео з YouTube-каналу «Сергій Бабіч та Дивовижний світ веброзробки», стріми, авторські статті та цікаві знахідки.

youtube.com/@babichweb

Реклами та інтеграції обговоримо
Download Telegram
Товариство, сьогодні хочу нарешті відзвітувати за минулі збори. Нагадаю, що ми разом з вами придбали РЕБ для 109 окремого гірсько-штурмового батальйону, а згодом — комплект навчальних гранат для Житомирського військового інституту.
Прикладаю відеоподяку від бійців та світлини з тренувань.

І приєднуюсь до подяки. Саме завдяки вам я маю сенс в існуванні цього каналу, бо той факт, що я можу ще й допомагати разом з вами ЗСУ, надзвичайно мене мотивує.

Дякую вам!

P.S. Бійці дали дозвіл на публікацію відео з обличчями, якшо шо
🔥366
This media is not supported in your browser
VIEW IN TELEGRAM
І ще навздогін — також ми з вами збирали й придбали Mavic для 183 навчального центру!

Цей Mavic допомагає бійцям вчитися тікати від ворожих скидів, тож лише задумайтесь, скільки вберегли життя й здоровʼя ті, здавалось би, невеличкі 20 гривень вашого донату.

А на ділі — кожна, кожна гривня критично важлива.

Дякую вам, товариство!
🔥246👍2
— …так, ну шо, цей тест нарешті пофіксив, тепер треба про всяк випадок інші запустить…
— …
— …
— СССССУКА!

(з польових нотаток)
😁8110
#мислення_розробника
Тести, тести, тести… Вони стали частиною моєї практики доволі нещодавно. На початку моєї карʼєри про них і не знали, а далі зазвичай все зводилося до класичного "у нас немає часу і ресурсів".

А потім вони зненацька увійщли в моє життя, як то кажуть — "увірвалися з ноги". Ми мали підтримувати 95% покриття тестами на UI, що призводило до доволі очікуваного результату — покривалися рядки коду, а не логіка чи поведінка. Відповідно, і якість таких тестів була передбачуваною.

Моє ж ставлення до девелоперського тестування суттєво змінилося після переходу до DataRobot. Тут теж вимагається додавати тести до функціоналу, але з однією суттєвою відмінністю — вони мають бути доцільні.

Питання юніт та інтеграційних тестів на фронтенді доволі неоднозначне. Що тестувати? Як? Наскільки ретельно? Трактування може відрізнятися від команди до команди, від ліда до ліда. Але є дуже проста рамка, яка дозволяє мені лишатися в межах здорового ґлузду.

Почнемо з юнітів. Ними я покриваю саме логіку. Не поведінку. Для цього дуже зручно користуватися підходом "one hook to rule them all", бо в такому випадку я можу перевірити основні сценарії, не покладаючись на розмітку і .toBeInTheDocument, якщо мене цікавить, чи правильна комбінація даних на виході.

Але, якщо подумати, то це вже й не юніт, а, по суті, інтеграційний тест. Бо в такому випадку я перевіряю якраз правильність роботи різних функцій та розрахунків разом. А от справжні юніт-тести — це вже рівент окремих функцій та, вимушено, кастомних хуків, які виконують щось одне.

І покриваю я зазвичай далеко не усі кейси. Мені достатньо перевірити так званий happy-path, потім пусті дані і нарешті невалідні. Якісь супер екзотичні комбінації ми залишаємо на випадок, якщо воно дійсно потім десь вилізе багом, а до того приймаємо, що система може надати нам лише очікуваний набір параметрів.

Чому я не перевіряю екзотику? Бо якщо таке трапляється, то це, швидше за все, баг в іншому компоненті системи, і виправляти треба саме його. Тести моїх модулів перевіряють рамки, очікувані саме для них. Я не повинен передбачати якісь чудернацькі сценарії поза межами відповідальности мого функціоналу.

Тож таке просте правило — якщо на умовному проді в мою систему прийшли неочікувані дані, то першим припущенням буде, що некоректно працює саме модуль, який їх надає. Це дозволяє мені залишати власні тести сфокусованими і не перевіряти чужі помилки.

Щодо інтеграційних UI тестів. Тут дещо складніше скласти ту рамку, бо не завжди зрозуміла межа відповідальності. Ще менш зрозуміла межа між тим, де це ще інтеграційний тест кількох UI компонент, а де вже повноцінний end-to-end.

Якщо користуватися підходом з єдиним state-hook в компоненті, то мені не треба перевіряти UI-flow, аби дізнатися, чи змінює натискання кнопки видимість якогось елемента. Для цього є типу інтеграційний тест для цього хука, де я змінюю один набір даних і перевіряю, чи змінився стан потрібним мені чином.

І переконатися, що UI реагує правильно, дуже просто. Якщо тест, в якому ми перевірили зміну стану, працює правильно, то для перевірки UI нам достаньо просто замокати різні зліпки цього стану і дивитися, чи вірно відбувається рендер HTML. Це позбавляє мене необхідності емулювати e2e-тести.

Покриття усіх сценаріїв тут теж стає не обовʼязковим. Якщо у нас правильно протестовані попередні шари: функції, хуки, стейт, то нам достатньо перевірити кілька ключових комбінацій даних для відображення UI.

А ще у нас є золоте правило — додавати тест на баг-фікси. Це треба в тому випадку, якщо ми усе-таки не передбачили якийсь валідний інпут, і таки існує якась комбінація вхідних параметрів, яка може зламати логіку. Але якщо використовувати ось такий шаровий підхід до тестів, то це означатиме, що такий випадок доволі рідкісний і стосується саме edge-cases. А едж-кейси на те й едж, що їх важко передбачити.

Ну якось так. Пишіть тести, вчіть теорію тестування — ви собі потім подякуєте. А за нагоди поговоримо про тести та AI.

@babichdev

P.S. Росія — кончена країна кончених людей. Головне ж для нас — триматися купки. Обіймаю усіх.
53🔥11👍6
#css_in_action
Коли я вперше побачив ефект розмиття тла на сторінці, коли відкриваєш якесь модальне вікно, моєму захвату й здивуванню не було меж. Здавалося, ми вступали до нової ери користувацьких інтерфейсів — футуристичних, майже фізичних, осяжних.

Але тоді я навіть не здогадувався, яких зусиль коштував це ефект розробникам. В кращому випадку — noscript-фільтр, у гіршому — скриншот тла через canvas та розмиття вручну. Мені здається, я маю дякувати богам веброзробки, що свого часу мене оминула доля додавати розмиття на сторінку.

Проте час не стоїть на місці і, як багато інших поширених проблем, цю задачу тепер можна виршіти одним рядком CSS. Багато хто з вас уже знайомий з властивістю backdrop-filter, головною відмінністю якої від просто filter є те, що розмиття вмісту відбувається не всередині елементу, а поза ним.

Якщо не поставили вподобайку — далі читати не дозволяю.

Ця властивість відразу спростила код навколо наших модальних вікон (а dialog спрощує ще більше, але про це — іншим разом). Однак, як часто трапляється, далі цього конкретного використання справа не заходить.

А дарма. У backdrop-filter є й інші можливості, наприклад, зміна яскравості чи контрасту, обернення кольорів, маніпуляції з насиченістю та навіть поворот відтінку на колірному колі.

… {
backdrop-filter: blur(10px);
backdrop-filter: brightness(150%);
backdrop-filter: contrast(150%);
backdrop-filter: grayscale(150%);
backdrop-filter: invert(150%);
backdrop-filter: opacity(0.6);
backdrop-filter: saturate(200%);
backdrop-filter: sepia(150%);
backdrop-filter: hue-rotate(50deg);
}


Ба більше, синтаксис дозволяє компонувати різні фільтри, тож можна отримувати дійсно неймовірні та неочікувані результати:

backdrop-filter:
contrast(150%)
grayscale(150%)
invert(150%);


Варто памʼятати, що аби ефект запрацював, елемент, у якого є backdrop-filter, не повинен мати суцільного тла. Якщо важливо явно відділити його від елементів поза ним, і при цьому застосувати якийсь цікавий фільтр, дещиця прозорості фону не завадить. А найкраще ефект від фільтрів видно саме при повністю прозорому тлі.

А ще надзвичайно важливо мати на увазі те, що backdrop-filter обчислюється в реальному часі на GPU, і може потребувати значний обʼєм ресурсів. Зокрема, якщо фільтр накладено на велику площу (так, той самий бекдроп під модалкою). Ситуація може поігршитись, якщо на тлі відбувається анімація, тоді бравзер змушений постійно оновлювати зображення позаду елемента, щоб відповідати цій анімації. Ну й трошки навантаження може додати тінь на цьому ж елементі — чим вона більша, тим більше ресурсів жує бравзер.

Це не означає, що ваш ноутбук згорить, щойно ви відобразите анімацію з фільтром (радше клієнтський), але зважати на це теж варто. Не у всіх же МакБуки з М4.

MDN: backdrop-filter

Маленьке демо: https://jsfiddle.net/b0Lry2jm/3

***
@babichdev
113👍44🔥1
#коштозбір
Товариство, маємо новий запит від військових.

Для роти матеріально-технічного забезпечення 115 бригади потрібно два детектори дронів "Чуйка".

Детектори треба для бензовозів, які заправляють тяжку техніку на Покровському напрямку.

Наразі це 49 400 гривень, будем брати по мірі накопичення коштів.

Буду вдячним за кожну гривню, товариство!

🔗Посилання на банку
https://send.monobank.ua/jar/AeXQ6YRf2X

💳Номер картки банки
5375411202918178
14
#нове_відео
Товариство, вирішив оживити один зі старих дописів про aspect-ratio, і створив коротеньке відео. Запрошую до перегляду!

https://youtu.be/TxqfBsvJlC8

Там 4 хвилини всього, подивіться зараз, не відкладайте)

***
На детектори дронів зібрано 2 236 грн з 49 400 грн. Долучайтесь!
26🔥19👍1
Буду цілу ніч вертіться і крутиться,
Але домчить до Києва мене
по рейках Укрзалізниця.

До зустрічі на React+ Fwdays'25 ;)
65👍17
Товариство, давно хотів вам розповісти про одну благочинну ініціативу, яка триває вже кілька років. Я назвав її Незамовлений Гепі Міл. Народилась ця ідея на початку повномасштабки. Ми всі тоді були шоковані, а найбільше — тим, що росіяни абсолютно спокійно вбивали дітей. Як вони це роблять і зараз.

І я захотів почати робити щось на вшанування памʼяти дітей, загиблих від рук росіян. І водночас допомогти тим дітям, які цієї допомоги дійсно потребують. Так і народився "ГепіМіл" — коли я раз на тиждень пересилаю зібрані кошти до "Міста Добра" — найбільшого прихистку України для жінок, дітей та бабусь.

Керівницю центру я знаю особисто. Те, що вона робить — це титанічна і часто невдячна праця, і найменше, що ми можемо зробити для цього центру — це раз на тиждень трошечки їм допомогти.

А чого Незамовлений Гепі Міл, спитаєте ви? Бо у нас в сімʼї колись була традиція щопʼятниці замовляти ГепіМіли, сідати в залі та усі разом дивитися якогось кіна чи мультика. А потім МакДональдси закрилися, а потім знову відкрилися, і ми знову отримали змогу замовити ГепіМіла та збиратися купки за мультиком. А от діти, що загинули — вже ніколи не зможуть замовити їх собі. Звідси й назва — Незамовлений ГепіМіл.

Тож був би надзвичайно радий, якби до цієї ініціативи почали долучатися і ви, любе моє товариство.

Детальніше тут: https://news.1rj.ru/str/babichxbabich/106

Дякую вам!
28
Сьогодні на React+ Fwdays'25. Хто ще є?)
63🔥16
#мислення_розробника

useLess

Спробую коротенько викласти основні думки моєї доповіді на React+ Fwdays'25. Відразу попереджаю, що до самих хуків доберемся далеко не відразу.

Отже, найперша теза — на мою особисту думку, архітектура React безбожно застаріла. І саме цим пояснюється постійне намагання вигадати чергові чудодійні покращення та магічні "оптимізації".

Теза друга — якщо ваш застосунок повільно працює, то у переважній більшості випадків React тут ні до чого, а проблема знаходиться по ту сторону монітора.

Думка третя — без розуміння недоліків інструменту неможливо ефективно ним користуватися. Якщо вашим першим рішенням буде втулити черговий useMemo — для мене це очевидна ознака, що ви взагалі не знаєте, як працює React, і які у нього є проблеми.

Теза четверта: кожен хук — точка доданої складності. React мусить в неочевидний для нас спосіб відслідковувати порядок їх виклику, робити додаткові перевірки, витрачати ресурси на перевірку залежностей і все це тримати в доволі кучерявій структурі даних з доволі кучерявою логікою відтворення.

Пʼята думка — завжди треба ставити під сумнів сферу відповідальности UI-компонентів. Чи мають тягнутися дані саме тут? Чи потрібне це обчислення саме тут? І так далі. Потрапивши під вплив магічного мислення "хуки оптимізують", дуже легко втратити здоровий ґлузд і перетворити маленький компонент на звалище.

І нарешті, остання теза — use Less. Менше хуків, більше логіки. Треба писати на JS, а не на хуках, тобто змінити напрямок мислення. Бо в першу чергу наша задача зробити так, аби воно працювало, а про всілякі плюшечки й рюшечки варто думати в кінці.

Звичайно ж, це не панацея. Що спрацює для одного випадку, зашкодить в іншому. В одному місці можна викинути useMemo, в іншому без нього ніяк.

Головний принцип, за яким завжди мають прийматися архітектурні рішення — IT DEPENDS. Тому кожен випадок треба розглядати окремо.

Але центральною думкою завжди має бути — "Нашо воно тут?". Якщо ви не можете виправдати useCallback чи useEffect — їм тут не місце.

Наостанок ще побіжно згадаю про React Compiler, що вийшов буквально кілька днів тому. Так от, друзі, радіти тому, що от нарешті можна викинути useMemo чи useCallback, ще зарано.

RC вводить примусове кешування. До того ж доволі прямолінійне. Це один з моментів, які найперше кинулися мені в очі. Однозначно вилізуть моменти, коли що він, що useMemo будуть заважати.

А найбільшою проблемою його, на мою особисту думку, є те, що RC вводить ще один додатковий, до того ж прихований, рівень складності.

Він буде покривати багато моментів, але із ним вам треба бути ще обережнішими, бо на додачу до жонглювання хуками доведеться ще й тримати в голові, чи увімкнутий тут Compiler, чи ні.

А найсумніше в усій цій історії те, що найбільша проблема так і не буде ніколи вирішена. Я говорю про сам React, усі зусилля з оптимізації якого зводяться до того, аби хоч якось обліпити подорожниками й пластирями його підхід з віртуальним DOM. Це було проривне рішення для 2013 року, але у 2025 році це вже просте латання дірок.

Якщо ж спробувати ще більше скоротити зміст доповіді, то вийде щось на кшталт:

Спочатку думайТЕ, а вже потім пишіТЕ.

Гарного усім початку робочого тижня! Якщо цікаво більше подискутувати про те, чому React — гімно (як і все інше), можете дати мені про це знати вподобайкою та донатом на детектори дронів для 115 бригади.

Подякував!

@babichdev
69👍14🔥11🤔1
Товариство, аби не пропустити найкращі срачі дискусії, долучайтесь до чату при каналі:

https://news.1rj.ru/str/babichdevchat
8
#нове_відео
Товариство, нове відео на каналі!

Вирішив розповісти трошки про події вказівника у бравзері. Що буде, якщо клацнуть, тицьнуть, скрольнуть і поводить — розповідаю, ще й показую на прикладах з кодом. А ще одним оком глянемо на Pointer Events. Приємного перегляду!

За підтримку й мотивацію зробити це відео дякую компанії Logitech.

https://www.youtube.com/watch?v=i0K3CMp8Ogc
39🔥14👍2
#js_in_action
Pointer Events
В останньому відео про події миші я побіжно розглянув і Pointer Events, які стали логічним продовженням розвитку Mouse Events. Сьогодні ж хочу подивитися до них дещо детальніше (але не дуже).

Тривалий час, аби підтримувати пристрої з дотиком, нам доводилось користуватися одночасно двома різними інтерфейсами: Mouse Events та Touch Events. Це привносило додаткову складність та могло призводити до прикрих багів та халеп, найпростішою з яких було просто забути продублювати логіку.

І ось у 2013 році Microsoft, на диво для всіх, зробила щось неочікуване: створила Pointer Events — єдину модель взаємодій для всіх типів вказівників. Згодом її стандартизували у W3C, а нині її підтримують усі сучасні браузери, включно з Safari.

Що таке Pointer Events
Pointer Events — це уніфікована модель DOM-подій, у якій поняття "вказівник" (pointer) охоплює мишу, дотик та стилус. Замість трьох різних наборів подій ми маємо одну логічну систему:

element.addEventListener('pointerdown', e => {
console.log(e.pointerType, e.pressure);
});


Події pointerdown, pointermove, pointerup, pointerenter, pointerleave працюють однаково для всіх пристроїв.

У кожної взаємодії є власний pointerId (щоб відстежувати кілька дотиків), pointerType ("mouse" | "touch" | "pen"), pressure (сила натиску), а також координати, кнопки, модифікатори — поєднуючи класичні MouseEvent та специфічні для дотиків властивості.

click, до речі, лишили як універсальну подію.

Один код — усі пристрої
Основна перевага Pointer Events — універсальність. Можна реалізувати drag’n’drop, малювання або будь-який інший інтерактив без окремих костилів для миші та дотиків.

Якщо користувач натискає пальцем, викликається pointerdown; якщо мишею — теж pointerdown.

Для мультитачу кожен дотик має свій pointerId, що дозволяє легко опрацьовувати складні жести чи визначати кількість активних точок.

Крім того, Pointer Events підтримують так званий pointer capture — механізм, який дозволяє "прив’язати" вказівник до елемента навіть після того, як курсор або палець вийде за його межі.

element.setPointerCapture(e.pointerId);


Тонке налаштування
Бравзери, що підтримують Pointer Events, усе ще генерують події миші (mousedown, mouseup, click) для сумісності.

Тобто при торканні кнопки може спрацювати і pointerdown, і mousedown. Якщо ви вже перейшли на нову модель, це може призвести до непотрібного дублювання.

Розв’язання просте: якщо викликати event.preventDefault() на pointerdown, браузер не згенерує певні сумісні мишачі події (принаймні для touch і pen). Але це стосується лише "контактних" подій на кшталт mousedown. Події штибу mousemove чи mouseenter в такий спосіб, на жаль, не блокуються.

Також системну взаємодію на кшталт zoom та panning через JS взагалі неможливо перехопити, лише за допомогою CSS:
canvas {
touch-action: none; /* вимикає зум і прокрутку */
}


Mouse vs Pointer — що вибрати?
Якщо ваш застосунок орієнтований лише на десктоп — старі події миші цілком ок. Вони простіші, і багато бібліотек досі працюють саме з ними.

Якщо ж ваш продукт має мобільну авдиторію, або ви працюєте з Canvas, жестами чи стилусом — Pointer Events однозначно кращі.

Проте, якщо у вас є специфічні кейси (наприклад, HTML5 Drag’n’Drop), де Pointer Events ще не інтегровані — можна тимчасово поєднати обидві моделі.

Щодо drag'n'drop — щоб не заглиблюватись, гляньте в спеку, вони побудовані повністю на MouseEvents, dragstart стартує після звʼязки mousedown + mousemove.

Підсумок
Не варто сприймати Pointer Events як просто "ще один API". Це логічна еволюція, що прибирає штучний поділ між пристроями й робить взаємодію у вебі дійсно універсальною.

Колись ми писали купу різних обробників для однієї кнопки, а тепер достатньо одного — pointerdown.

Як на мене, PointerEvents є одним з тих прикладів, коли розвиток специфікації не намагається вигадати щось кардинально нове, а поступово й логічно надбудовує нові можливості, залишаючи при цьому старі досі актуальними.

Що почитати:
📖 W3C Pointer Events Level 3
📖 MDN: Pointer Events
📖 Chrome Developers Blog: "Pointing the way forward"

@babichdev
55👍10🔥2
#коштозбір
Коротенький апдейт по нашому збору на детектори дронів для 115 бригади.

Ми встали в чергу на виготовлення, тому маємо трошки часу на те, щоб дозбирати кошти. Але, поки минав час, загальна вартість дещо підросла.

В общім, зараз саме час закинути 20 гривень на збір, а взагалі було б файно його закрити, бо в черзі вже інші запити від військових.

🔗Посилання на банку
https://send.monobank.ua/jar/AeXQ6YRf2X

💳Номер картки банки
5375411202918178

Дякую вам за кожен донат!
14
Дивовижний світ веброзробки pinned «#коштозбір Коротенький апдейт по нашому збору на детектори дронів для 115 бригади. Ми встали в чергу на виготовлення, тому маємо трошки часу на те, щоб дозбирати кошти. Але, поки минав час, загальна вартість дещо підросла. В общім, зараз саме час закинути…»
#dom_api
Не буду стверджувати, що обхід DOM-дерева є повсякденною рутиною, але іноді така потреба постає, і тоді перед нами постає вибір: або ми це робимо рекурсивно і вручну, або нормально. Питання лише в тому, що про нормальний спосіб мало хто знає. От я знаю про нього вже кілька років, а на практиці довелося використати буквально днями.

Мова йтиме про TreeWalker. Як я вже казав, про його існування я знав давно, але прикладних задач для нього не мав. Аж тут сталося цікаве співпадіння: по-перше, Дмитро Тарасенко розповідав про свою маленьку бібліотечку для скрапінгу на React+ Fwdays'25, чим нагадав мені про існування TreeWalker взагалі, і, по-друге, днями я брав участь в досить цікавому лайвкодингу, де мені довелося в прямому етері, так би мовити, вперше з цим API попрацювати. Тож в двох словах розповім про нього.

Якщо коротко, то TreeWalker дозволяє послідовно обходити дерево вузлів DOM. Але є кілька дуже важливих нюансів. Перше, можна фільтрувати тип вузлів за власними правилами. Друге — він не обмежує нас в напрямку руху обходу, на відміну від рекурсії.

Так-так, він завжди памʼятає поточну ноду і дозволяє рухатися як вверх-вниз, так і по сусідах, а не лише вперед і вниз.

Це робить його дуже гнучким інструментом для швидкого обходу дерева, бо з ним можна не лише мандрувати в будь-якому напрямку, а зупинити обхід у будь-який момент та потім, згодом, відновити його з того ж місця.

Виглядає ініціалізація приблизно так:
const walker = document.createTreeWalker(
// корінь обходу
document.body,
// тип вузлів, які враховуємо
NodeFilter.SHOW_ELEMENT,
// кастомний фільтр
{
acceptNode(node) {
return node.tagName === 'P'
? NodeFilter.FILTER_ACCEPT
: NodeFilter.FILTER_SKIP;
}
}
);


і потім перебираємо як нам до вподоби:
let node = walker.currentNode;

while (node) {
console.log(node.tagName);
node = walker.nextNode();
}


TreeWalker не є альтернативою NodeIterator, а радше його "прокачаною" версією. NodeIterator також послідовно рухається деревом, але строго вперед або назад, більше нагадуючи ручний рекурсивний підхід, але з можливістю "перемотки".

З вас вподобайка і поширення, навіть якщо досі ні чорта не зрозуміло.

Найчастіше TreeWalker використовується тоді, коли потрібний саме обхід дерева, а не просто querySelectorAll. Наприклад, коли нам потрібно знайти фрагменти дерева, що відповідає певній структурі, чи елемент за певними правилами, які не можна описати селектором. І так, на відміну від querySelector*, він дозволяє обходити саме вузли, не лише елементи.

Звичайно ж, без підводних каменів нікуди. По-перше, TreeWalker не бачить Shadow DOM. Ну, на те він і Shadow, звичайно.

По-друге, фільтр acceptNode викликається для кожного вузла, тому при великому DOM варто уникати зайвих перевірок.

І, по-третє, TreeWalker працює з живим DOM, тому, певно, варто уникати операцій з деревом під час обходу, інакше він може несподівано затягнутись.

TreeWalker — це маленький, але дуже потужний API. Він дозволяє пройтись DOM не "згори донизу", а саме так, як тобі зручно, дозволяючи самотужки задавати правила, перестрибувати від сусідів до сусідів, від батьківських вузлів до дочірніх і усе це — з кнопками "play", "pause", "rewind" і "forward" прямо з коробки. Така собі міжпросторова рекурсія з подорожами в часі (так, я полюбляю безглузді метафори).

Я навіть не буду питати, чи ви вже використовували його в проді, а якщо використовували — отримайте від мене щиру заздрість, що працюєте з такими цікавими задачами )

Що почитати:
📖 MDN: TreeWalker
📖 MDN: NodeIterator

Що почитати душнілам:
📖 W3C: Document Object Traversal
📖 WHATWG: TreeWalker

P.S. Товариство, детектори дронів самі себе не куплять, запрошую закинути пару гривень на збір.
🔥6624👍12🤔1
А я, між іншим, вже 14 років щабадабадабадабадада. З них щонайменше 5 алалалалалалала.

Як вважаєте, варто податися?
😁112🔥8👍1
Щойно записав нове відео, що вийде на Геловін. Про що? Не буду відкривати усі таємниці, але вам від нього однозначно стане моторошно і ви точно почнете остерігатися слова "рефакторинг"… Бу!

Поки ж я його монтуватиму, а ви чекатимете на нього із нетерпінням, хочу нагадати, що збір на детектори дронів для 115 бригади ще досі не завершено, тож саме зараз чудова нагода долучитися до нього та закинути кілька гривень.

Дякую за кожен ваш внесок, товариство!

🔗Посилання на банку
https://send.monobank.ua/jar/AeXQ6YRf2X

💳Номер картки банки
5375411202918178
18🔥3