Будни разработчика
14.7K subscribers
1.18K photos
334 videos
7 files
2.01K links
Блог Lead JS-разработчика из Хельсинки
Автор: @bekharsky

По рекламе: https://telega.in/channels/htmlshit/card?r=GLOiHluU или https://t.me/it_adv

Чат: https://t.me/htmlshitchat

№5001017849, https://www.gosuslugi.ru/snet/679b74f8dad2d930d2eaa978
Download Telegram
#такое дня

Январь — время заполнения годовых Performance Review aka оценки эффективности работы.

Я, честно, в русскоязычном пространстве не работал в компаниях, где они проводились бы. Как в вашей компании это называется?

Как правило, степень упоротости глубины проработки проблем в отчётах зависит от... да ни от чего она не зависит. Бывает, что в компании на 10 человек проводится оценка по Методу 360 градусов, а бывает, что в огромной корпорации опираются только на число закрытых PR-ов и мнение менеджера.

В любом случае, очень часто приходится писать отчёт на самого себя. С одной стороны, сам себя не похвалишь — никто не похвалит, с другой — почти все мы подвержены синдрому самозванца.

Сегодня я услышал интересное мнение:
— Я просто везде отметил «выше ожиданий» и в комментарии попросил менеджера объяснить, почему он так не считает.

Позиция, как минимум, смелая. Она точно лучше чем отчёт, котором просто бы стояло «Соответствую ожиданиям» и всё.

Но насколько ж сильна вера в менеджера...

В любом случае, мне интересно, проходят ли и как подобные раунды оценки в ваших компаниях?

#work #review
8👍4🤩1
This media is not supported in your browser
VIEW IN TELEGRAM
#codepen дня

Давно собирался написать игру по Гарри Поттеру, но не знал, как?

Не расстраивайся, я принёс решение! Steve Gardner и его прекрасное создание Spell Caster!

Вот: https://codepen.io/ste-vg/full/zYerxoR

Я обожаю подобные примеры. Игра буквально укладывается в несколько экранов кода, но красива до безобразия.

Из технологий — Three.js и стейт-машина Stately, позволяющая описывать состояния приложения через удобные диаграммы.

Много комментариев по коду, интересная реализация распознавания жестов. Хоть сейчас бери и в магазин выкладывай :)

Я залип, в общем, как в игре, так и в её исходниках.

#webgl #threejs #game #бородач
👍10
#тип дня

Типом дня назначается вон тот в кожаной куртке. Да-да, о тебе говорю!

TL;DR: вычисленный тип функции с дженерик-аргументом можно сузить, декларируя тип как const.

Кроме шуток, разве тебя не бесит, что указываешь вот дженерик тип аргумента функции, производишь манипуляции над переданным объектом — а в ответ тебе вычисленный базовый тип?

Непонятно? Давай так:

function wrapNames <T>(names:T[]) {
return names.map<{name: T}>(name => ({name}));
}

const [first, second] = wrapNames(['Sergey', 'Alex'])


Какой вычисленный тип получат first и second?

Очевидно, такой:

{
name: string;
}


Но... это же довольно неудобно. А если мы схему валидируем, например? Или форму описываем. Или тесты пишем в конце-концов. Почему при заранее известном константном типе входных данных мы получаем вычисленную строку?

К счастью, начиная с TypeScript 5.0 можно объявлять дженерики как константные типы! Иначе говоря, работать с максимально узкими (narrow) типами. Это, собственно, аналог указания as const у предопределённой константы.

Использование простое:

function wrapNames <const T>(names:T[])

И на выходе тип у first и second будет такой:

{
name: "Sergey" | "Alex";
}


Песочница: пуньк.

Давайте исходя из полученной информации создадим валидатор по известной схеме данных и дальше поработаем с ним:


// initValidator
function parse<const T extends { name: string; surname: string }>(users: T[]) {
return (name: T['name']) => users.find(u => u.name === name)?.surname;
}

// validate
const getSurname = parse([
{
name: 'Joe',
surname: 'Doe',
age: 30,
},
{
name: 'John',
surname: 'Dohn'
}
]);


Определение getSurname приобретёт такой вид:

getSurname: (name: "Joe" | "John") => string | undefined


То есть, обратите внимание, не просто строку на вход, а конкретные, существующие в нужных параметрах строки.

Теперь это вполне себе напоминает удобную работу с коллекциями данных, не стыдно и форму провалидировать.

Песочница: пуньк.

Штука относительно новая, к ней ещё придётся немного привыкнуть. Но уже можно пробовать применять.

Ах да, ссылка на соответствующий PR в TypeScript: https://github.com/microsoft/TypeScript/pull/51865

#typescript #generic #const
👍253
#фишка дня

Сегодня на ваших экранах новая серия сериала "Не боги горшки обжигают".

На сей раз отличился Kent C. Dodds, которого уж никак нельзя упрекнуть в незнании фронтенда 🙂

Но давайте разбираться. Что же случилось?

Если пройти по ссылке, то можно понять, что случилась типичная для интернет-каталогов ситуация: карточка товара со ссылкой куда-нибудь ещё.

И да, если раньше всем было вообще пофигу на валидацию — рендерит и ладно — то нынче в дело вступают SSR/SSG и они не позволяют гидрацию невалидного HTML 🥁

А попасть в ситуацию такую ну просто слишком легко: достаточно обозвать пару компонентов как-то иначе, чем просто a, и всё, потерялись. Нет, кнопки тоже нельзя. Нет, любой интерактивный контент нельзя.

Так что же делать-то? Псевдоэлементы к спасению!

Верстаем как обычно, вставляем просто две ссылки: одну на товар, вторую куда-нибудь ещё. И растягиваем псевдоэлемент первой на всю ширину. Как-то так: https://codepen.io/alinaki/pen/OJdJQyB

Все довольны, котята спасены!

#css #trick #card #validation #бородач
👍7
This media is not supported in your browser
VIEW IN TELEGRAM
#фишка дня

Как заставить элемент изменять свою ширину... ступенчато?

Ну, например, вам нужно, чтобы шахматная сетка при любых условиях оставалась цельной.

И сегодня у нас аж несколько вариантов, как этого добиться.

1. От Аны Тюдор: https://codepen.io/thebabydino/pen/zYbZpBq

Используем функцию модуля:

width: calc(95vmin + -1*mod(95vmin, 15px));


Но вот беда, в Chrome модуль пока не поддерживается... но не унываем! Есть ещё два варианта от Темани Афифа aka CSS Challenges.

2. Эффективное значение функции выше это, буквально, округление. Но так и такая функция в CSS имеется! Как-то так:

min-width: round(95vmin,15px);


Пример: https://codepen.io/alinaki/pen/VwRMpPY

Но... Chrome опять в обломе.

Да, и в первом и во втором вариантах на коне Firefox и Safari. Как я уже недавно писал, парни из Chrome взяли привычку реализовывать только то, что можно красиво оформить в пост. И реализовали тригонометрические функции, забыв про базу.

Потому нам ничего не остаётся кроме как...

3. CSS Grid конечно же: https://codepen.io/t_afif/pen/KKEXwmR

И секрет будет в правиле:

grid-template-columns: repeat(auto-fill, 15px);


Всем шахмат, котаны :)

#css #trick #math
👍244
#статья дня

Smashing Magazine — один из старейших онлайн-журналов по веб-разработке. Статьи там как правило подбираются весьма подробные и полные, объясняющие все аспекты даже, казалось бы, простых эффектов.

Сегодня в наш канал залетает как раз такая статья: 3D-эффекты для картинок. Блеск, параллакс и поворот.

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

Сразу ссылки на кодпены с примерами, чтоб далеко не ходить:
1. https://codepen.io/t_afif/pen/VwEJqKV
2. https://codepen.io/t_afif/pen/qBJyXNy
3. https://codepen.io/t_afif/pen/yLRRBKj

Ну и, конечно, сама статья: https://www.smashingmagazine.com/2023/07/shines-perspective-rotations-css-3d-effects-images/

Будет полезно не только лишь всем.

#css #3d #image #бородач
3👍2🤩2
#детектив дня

Сегодня в чат пришёл подписчик с забавной проблемой.

Дано: форма с двумя полями, одно просто как HTML-тег, второе — React-компонент. Ну, очевидно, компонент тоже содержит поле ввода.

При нажатию на клавишу Ввод событие onSubmit не происходит, форма не отправляется. Хотя, вроде как, всем известно, что это — стандартное поведение.

Было замечено, что при удалении React-компонента всё отлично работает. И первым предположением стало, что React что-то испортил, или песочница кривая.

Кто-то догадался сделать ванильный пример, поведение сохранилось.

То есть, вот такое:
<form onSubmit="alert('aha')">
<input type="text"/>
<input type="text"/>
</form>

...просто-напросто не работает. Стали копать.

И докопались до спецификации 1995 года для HTML 2.0!

Вот она: https://www.w3.org/MarkUp/html-spec/html-spec_8.html#SEC8.2

И там написано следующее:
When the user indicates that the form should be submitted (using a submit button or image input), the form data set is processed according to its method, action URI and enctype.

Перевожу: чёрным по белому сказано, что форма должна отправляться по кнопке submit (или специальной картинке, но то такое).

То есть, наше предположение о том, что форма всегда должна отправляться по нажатию Ввода — уже неверное.

Но, буквально, в следующем же абзаце:
When there is only one single-line text input field in a form, the user agent should accept Enter in that field as a request to submit the form.

Так-так-так, а что тут у нас? А тут написано, что единственное текстовое поле — это частный случай и нажатие клавиши ввода должно рассматриваться как отправка формы!

Удивительное рядом, короче. По всей видимости, такое поведение связано с обработкой событий focus и blur, когда полей больше одного.

Короче, так как же решить проблему и не добавлять кнопку?

Очень просто, вставить в форму следующее:
<input type="submit" hidden/>

По-моему, пахнет лёгким бредом, но уж как есть.

И да, за много лет веб-разработки мне ни разу не понадобилась форма без кнопки, но расследование вышло забавным.

#html #form #submit
🤩30👍234👎1
#фишка дня

Как легко и быстро добиться эффекта фаски на кнопке без использования вложенных и псевдоэлементов?

Конечно же, использовать градиентный бордер!

— Ну да, конечно, это кто делает градиентные границы без вложенных элем
ентов?

Ну как, кто. Мы и делаем. Всё довольно просто:

border: 0.5vw solid transparent;
background: linear-gradient(#17181c, #17181c) padding-box, linear-gradient(to right, #1e1e22, #121316, #1e1e22) border-box;


1. Задаём нужную нам ширину, используя обычный прозрачный border.
2. Как только мы наложили границу, мы получили доступ к двум свойствам боксовой модели: border-box и padding-box.

Условно, border-box — это весь размер элемента, включая границу. А padding-box — всё до границы.

Итого, первый фон-градиент распространяется на контент, а второй — заходит на созданный нами прозрачный бордер.

нельзя сделать прозрачную кнопку.
можно использовать вообще любую картинку или тип градиента (линейный, радиальный, конический).

Пример: https://codepen.io/alinaki/pen/zYbPBEd

Кто-то готов сделать фаску ещё более выраженной? :)

#css #bevel #emboss #border
👍16
#такое дня

Уже довольно поздно. Отметьтесь реакциями, кто ещё онлайн.

Наберём достаточное количество — скину историю смешного бага, который у нас недавно произошёл по невнимательности.

Не наберём — скину её утром. Никуда она не денется.

Upd. Хорошо разогнались. Сейчас всё будет.
🤩67👍36🤡41🤬1
#баг дня

Преамбула

Некий сервис, назовём его Doodle Lampolytics, заставил всех своих клиентов переползти с модели подсчёта просмотров страниц на модель подсчёта событий.

Суть проблемы сервиса очевидна: постраничная модель слабо подходила для серьёзной аналитики данных в веб-приложениях. Даже элементарные лендинги стали гораздо сложнее, чем просто переходы по ссылкам.

И если раньше мы в нагрузку к «просмотру» передавали так называемые индивидуальные размерности и метрики, привязанные к сессии, то теперь — каждое событие может нести индивидуальный их набор.

Не в последнюю очередь это связано с ужесточением запретов на слежку за посетителями сайтов.

Поскольку в типичном проекте зачастую присутствует не одна сотня событий/страниц, то переезд сопровождается знатным перетряхиванием всей аналитики.

Суть

Суд да дело, приходим мы к идее передачи события с неким контекстом:
trackEvent('event_name', context) {}

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

Передаём туда, например, productId и orderId — и получаем трекинг истории конкретного продукта или заказа. Удобно, когда продукт сильно кастомизированный. Можно накинуть туда optionId и следить, какие продукты какими опциями нашпиговывают.

Таким образом, мы отсылаем объекты вида:
{
productId,
optionId,
orderId,
locale,
}


Потом объект из camelCase конвертируется в snake_case, как того требует документация, и дёргается некий API. Запомните этот момент.

И всё, поехали в продакшен. События уходят, счастье. Заодно, те же самые события, за некоторым исключением, можно пихать в сервисы вроде Sentry.

И всё бы ничего, но интерфейс Doodle Lampolytics настолько проклят, что оказалось намного проще выгружать данные оттуда сразу в Doodle LargeQuery и SQL-ем их запрашивать.

Но в какой-то момент времени мне это радикально надоело. Мне нравилась простота старого интерфейса, и я решил разобраться в новом. Проблема была в том, что я там не мог найти не только половину событий, но и их параметры.

Я почитал документацию, нашёл где создавать свои размерности и метрики на основе передаваемых параметров — в админке — захожу туда и наблюдаю следующую картину:
product_id,
product_ıd,
option_id,
option_ıd

— Какого хера?! — заорал я, я же был в офисе.

Но параметры с ı вместо i исчислялись десятками, в то время как остальные — десятками тысяч. И я зачем-то успокоился. Подумал, что это косяк ламполитики, с которым можно разобраться попозже.

Но сегодня я зашёл в наш прекрасный чат, зачем-то ткнул в аватарку одного из подписчиков и увидел у него в истории некую песню с названием Bakı.

И тут до меня резко дошло, какой я идиот.

Прежде чем читать дальше, вы можете посмотреть на код функции, которой мы преобразуем camelCase в snake_case: https://github.com/jonschlinkert/snakecase/blob/master/index.js

Особо внимательные, наверное, уже догадались, в чём проблема. А ведь мы даже тесты написали.

Поскольку не все пройдут по ссылке, я приведу кусочек:
const lowercase = (input = '', options) => input.toLocaleLowerCase(options?.locale);


Ничего не смущает? Я подожду.

...

Да, правильно. Если забыть передать локаль (язык клиента, сервера в нашем случае), то будет взята переменная среды.

И для кого-то из наших клиентов в этой переменной оказался турецкий (или узбекский) язык.

В которых нет буквы i, вместо неё — ı.

Вы все правильно поняли, мы не полностью контролируем среду выполнения. Это так называемый Doodle AppsScript. Он исполняется на языке, в котором открыт офисный инструмент посетителя.

Естественно, мы локаль передать забыли. Хороший вопрос, почему мы вообще не проверили это поведение изначально... но тут как есть.

Стыдно? Стыдно. Зря я винил Doodle Lampolytics.
🤩23👍93
#заметка дня

Иногда полезно спорить с мэтрами по поводу разных штук. Не стоит всё подряд принимать на веру.

Вот, например, Кори Хаус, весьма известный консультант по React, предложил буквально следующее: чтобы не запутаться в бесконечных хуках useEffect в React, вместо неименованной функции передавайте именованную.

То бишь, вместо:

useEffect(() => {
// do stuff
}, [...deps]);


Предлагается
useEffect(function doSomething() {
// do stuff
}, [...deps]);

На первый взгляд выглядит разумно, не правда ли?

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

Так вот, товарищи. Это далеко не самое лучшее решение, да ещё и многословное. Гораздо более разумно будет создать свой собственный хук.

Что такое свой собственный хук? Вы не поверите, это буквально тот же самый useEffect, но вынесенный в функцию:
function useDoSomething(deps) {
useEffect(() => {
// some effect
}, [...deps]);
}


Да, внутри функции кастомного хука можно держать useState, обращаться к внешним источникам данных и возвращать что угодно. Это, например, рекомендуемый способ использования того же React Query и официальная рекомендация команды React для переиспользования логики: https://react.dev/learn/reusing-logic-with-custom-hooks

Почему такой подход лучше?

Потому что, обладая всеми преимуществами варианта, предложенного Кори, он не только сокращает размер основного компонента но и легко тестируется!

Да, представьте себе, вы задолбаетесь правильно тестировать компонент с даже двумя-тремя встроенными хуками. А вынесенные отдельно — не только легко мокаются и подменяются, но и прекрасно тестируются сами по себе.

Так что команда React могла бы назвать раздел документации не "как переиспользовать", а "как тестировать". Это важнее.

В общем, не забываем про голову, котаны. Готов выслушать ваши сомнения, впрочем 🙂

#react #javascript #hooks
👍19🤡1
#такое дня

Решил принести вам на ночь. В 2013 году в официальной документации React предлагалось использовать jQuery для AJAX-запросов, а в качестве mock-сервера использовать встроенный в Python HTTP-сервер.

Правда, я, помню, использовал для таких задач сервер, встроенный в PHP... ну кто что умел.

С тех пор прошло 10 лет. Где мы теперь, котаны? :)
🤩6
#инструмент дня

Раз уж упомянули пару постов назад Кори Хауса, то давайте ещё чего-нибудь по его рекомендации.

И сегодня это инструмент для поиска дубликатов кода: jscpd.

Сразу ссылка на GitHub: https://github.com/kucherenko/jscpd

Как получаются дубликаты, клоны кода? Да от плохих абстракций. Сами по себе они ничем не плохи, ну есть у вас повторы там-сям, кому от этого плохо?

Ну, пока лежат — никому. Но если в одном таком куске кода баг — это лишь вопрос времени, когда он себя проявит в другим таком же куске. А чинить-то его везде надо.

Ну и на фронтенде банально раздувается размер бандла. Тоже такое себе.

Вообще, клонированный код это показатель того, что где-то у вас плохо с абстракциями, одна и та же логика применяется к, казалось бы, разным сущностям и так далее. Само по себе, это не так плохо, не просто же так в оппозиции к DRY (do not repeat yourself) существует WET (write everything twice).

Но иметь инструмент для поиска совсем уж лишнего — весьма неплохо.

А для тех, кому надо докопаться до сути, имеется целое исследование по поиску клонов кода, на 115 страниц: https://research.cs.queensu.ca/TechReports/Reports/2007-541.pdf

Но все, что я могу сказать — это что jscpd является реализацией детектора копий первого типа 🫠

Пользуется кто-нибудь чем-то подобным в своих проектах? Делитесь.

#js #clone #lint
👍14
#фишка дня

Даже две, но об одном и том же: как сделать "альбомный" бэкдроп у картинки.

Что такое бэкдроп? Это фон, который полностью зависит от контента, например, заливается усредненным цветом. Вы наверняка видели похожие эффекты в разных аудиоплеерах, отсюда и название — альбомный. Да и на ютубе раньше было модно заполнять края у вертикальных видео тем же размытым видео.

Итак, два варианта.

1. От Артура Бьена. Надо предупредить, он немного упоротый:
а) заполняем фон контейнера однопиксельными копиями картинки:
background-image: url(var(--bg));
background-size: 1px 1px;
background-repeat: repeat;

б) накладываем сверху псевдоэлемент с небольшим тюном цвета:
div::after {
--backdrop: invert(0.2) contrast(0.8) saturate(1.7) blur(8px);
backdrop-filter: var(--backdrop);
}

в) если что-то надо наложить сверху, стоит использовать режим смешивания overlay, чтобы не вляпаться в идентичный цвет.
mix-blend-mode: overlay;

Пример: https://codepen.io/alinaki/pen/MWxrjZV

2. Всё бы хорошо, но эти бесконечные копии изображения... ох. Поэтому, есть достаточно интересный вариант от Темани Афифа aka CSS Challenges.

Будем использовать border-image. Отгрызаем полоски изображения по границе и повторяем их:
div: before {
content:"";
border- image:
var (--img) 2/
calc(50% - var (--w)/2 + var (--b)) /
calc (1.5*var(--b));
filter: contrast(.8) blur(var(--b));
}

Полученный эффект лучше всего смотрится на небольшого размера блоках: https://codepen.io/alinaki/pen/LYaeRvw

По-моему, стоит поиграть с настройками размытия ещё чуток, но эффект точно интереснее.

Если вы не знакомы с border-image, вот свежая статья от автора эффекта, собственно: https://www.smashingmagazine.com/2024/01/css-border-image-property/

Ну и, конечно, можно просто размыть картинку саму за собой. Но это было бы слишком скучно. Во втором варианте даже присутствует эффект Philips Ambilight.

#css #trick #backdrop #blur
25👍2🤩2
This media is not supported in your browser
VIEW IN TELEGRAM
#статья дня

На связи опять стилизация скроллбаров. Мы только недавно обсудили, как избежать некоторых связанных с этим багов в Safari, как подоспел Chrome 121 с интересным обновлением.

И что он нам принёс? Вы не поверите, поддержу стандартных правил scrollbar-width и scrollbar-color.

В Firefox они с 2018 года, а Chrome всё это время использовал правила от WebKit. Впрочем, все пользовались и было пофигу.

Пример:

.scroller {
--scrollbar-color-thumb: hotpink;
--scrollbar-color-track: blue;
--scrollbar-width: thin;
--scrollbar-width-legacy: 10px;
}

/* Modern browsers with `scrollbar-*` support */
@supports (scrollbar-width: auto) {
.scroller {
scrollbar-color: var(--scrollbar-color-thumb) var(--scrollbar-color-track);
scrollbar-width: var(--scrollbar-width);
}
}


Естественно, по этому поводу блог разработчиков Google Chrome разродился огромной статьёй, в которой прошёлся по типам скроллбаров (оверлей и классический), по анатомии скроллбара (трек, движок/лифт, кнопки) и по особенностям совместимости со старыми правилами с префиксом -webkit (Safari всё там же, да).

Ссылка на статью: https://developer.chrome.com/docs/css-ui/scrollbar-styling

Мне стандарт не нравится, он весьма ограничен по своим возможностям и результат получается так себе. Надеюсь, это временно и релиз означает, что дело сдвинулось с мёртвой точки.

#css #scrollbar
👍11
This media is not supported in your browser
VIEW IN TELEGRAM
#фишка дня

Стопудово вы делали эффекты как на видео через три div-а или span-а. Ну просто потому что трансформации на SVG это абсолютная боль.

Типа такого: https://codepen.io/alinaki/pen/abXpvyQ

Да, пример очень простой, но даже это на SVG бывает проблемно санимировать.

Хотя, казалось бы, для этого и предназначено.

А вся проблема в том, что для SVG определение координат для преобразований происходит немного иначе, нежели чем для элементов. Выходов из ситуации есть несколько.

Первый, от Аны Тюдор: исправить viewBox, поставив вместо 0, 0 (левый верхний угол) — -width/2,-height/2, соответственно, исправив остальные координаты.

Второй, интереснее, от Джея: указать следующие правила в CSS:

transform-box: fill-box;
transform-origin: 50% 50%;


Правило transform-box исправит положение координатной сетки, от которой мы уже сменим дефолтную точку отсчёта для преобразований — transform-origin.

По-умолчанию, кстати, transform-box установлен как view-box. То есть, в нашем примере, заполняет лишь 24 пикселя по каждой стороне 🤡

А вот и, собственно, пример:
https://codepen.io/alinaki/pen/YzBNyEz

Не бойтесь анимировать SVG, котаны. Просто не полагайтесь на дефолт.

#css #transition #svg #бородач
Please open Telegram to view this post
VIEW IN TELEGRAM
👍18
#новость дня

Итак, мы писали Джаваскрипт в браузере, в консоли, на серверах, на микроконтроллерах, в играх, в кофеварках, в аудиоплеерах, телевизорах, в макросах офисных пакетов, в NoSQL базах данных...

Но до сих пор не писали его в хранимых процедурах MySQL!

Итак, встречайте: JavaScript Stored Programs in MySQL. И соответствующий пост в блоге Oracle: https://blogs.oracle.com/mysql/post/introducing-javascript-support-in-mysql

Зачем? Ну для разных целей:
1. Извлечение данных, очевидно
2. Форматирование
3. Примерный поиск
4. Валидация данных
5. Собственные алгоритмы сжатия, сериализации и десериализации данных

Пример:


CREATE FUNCTION gcd_js (a INT, b INT) RETURNS INT
LANGUAGE JAVASCRIPT AS $$

let [x, y] = [Math.abs(a), Math.abs(b)];
while(y) [x, y] = [y, x % y];
return x;

$$;

Работает всё, ожидаемо, на GraalVM. Почему ожидаемо? Ну потому что Oracle и Java/JVM :)

Что, котаны, пишем стартап на MySQL? 😬

#mysql #sql #js
🤡10🤩4👍1
#заметка дня

Есть такой элемент — template. Название говорит само за себя: это шаблон.

Пишете в него некий HTML/SVG код, потом клонируете и вставляете куда вам надо.

На этом месте апологеты фреймворков ухмыльнулись, но вы же не одни на свете, да?


const content = template.content. firstElementChild.cloneNode(true);


Важно понимать, что содержимое шаблона — это фрагмент (обёртка над реальными нодами, если грубо), поэтому обращаться с ним надо соответственно.

Почему для этой цели не использовать просто скрытый div или — привет из прошлого — script type="text/template"?

Ну второе вообще не в кассу, это просто абьюз. А первое:
- Контент внутри темплейта не будет загружаться, пока не склонирован явно: ни картинки, ни стили, ни скрипты
- Валидатор игнорирует содержимое, хоть dd храните там, хоть td
- Поисковые системы знают про template и точно не индексируют. А с div hidden всё не так однозначно.
- Ну и именование говорит само за себя. Сахар все любят.

#html #template #бородач
👍113
#тип дня

Кричащий банан 🍌 (да-да) принёс шикарную фишку TypeScript: как типизировать ключ объекта без каста.

Решение: заранее объявите тип переменной перебора как keyof typeof. Всё, вы великолепны.

Пруф на TS Playground.

#typescript #cast #бородач
👍224
#число дня

В дружественном чате прошло очередное ежеквартальное обсуждение разрешений макетов и экранов.

Я не буду пока репостить свои статьи на тему, лишь задам вопрос.

Кто догадается, что такое 1536x864 на этой годовой статистике разрешений экранов мониторов?

Между прочим, 11% дисплеев.

https://gs.statcounter.com/screen-resolution-stats/desktop/worldwide

При встрече угощу кофе того, кто догадается первым.

Ну или кого вообще встречу.

#css #resolution
👍6
#статья дня

Злые Марсиане выпускают крутейшие статьи с завидной регулярностью. Вот очередная: https://evilmartians.com/chronicles/html-best-practices-for-login-and-signup-forms

На сей раз — про ошибки при разработке форм. И их, согласно статье, 11. Давайте кратко пробежимся.

1. Ставь autocomplete в нужное положение (username, current-password etc.).

Мы тут, кстати, обсуждали возможные значения и даже проблемы.

2. type="email", тут всё ясно.

3. Интерактивные элементы должны быть кнопками или ссылками, а не дивами (и ведь находятся же).

4. Не забывай про существование тега form (и такие тоже существуют).

5. Не используй placeholder как label.

6. Помни о label, особенно для галочек.

7. Состояние :focus должно быть визуально подтверждено.

8. Маркируй невидимые поля для скринридеров.

9. Не прерывай ввод внезапной валидацией (бесит).

10. Исключи случайную повторную отправку формы.

11. Помни о сетевых задержках.

В статье полно примеров и объяснений. Проверяйте свои формы, котаны.

#form #ux #бородач
20