Wise.js | Frontend tips
1.89K subscribers
72 photos
4 videos
66 links
Авторський канал БЕЗ РЕКЛАМИ з Frontend розробки: статті, завдання з співбесід, поради та трюки.

Youtube-канал: https://www.youtube.com/channel/UCOxqQhtg574p8kE4Te41PGg/featuredB

Пиши свої ідеї для постів сюди: @front_idea_bot
Download Telegram
☑️ КАК УСКОРИТЬ ЗАГРУЗКУ GOOGLE FONTS В 4 РАЗА

В разработке очень часто приходится использовать шрифты с Google Fonts (большая библиотека свободно распространяемых шрифтов). Ибо стандартные шрифты никого не устраивают, а хранить шрифты у себя не всегда хочется. Да и сервис очень популярный и вероятность что у пользователя уже есть нужные сайту шрифты очень высока. Сам сервис предлагает вам несколько способов подключения шрифтов:

1. с помощью тега <link> в <head> документа
2. @import в css файле

На примере разберем почему оба этих способа являются медленными:

Допустим нам нужно загрузить 2 шрифта Lato и Racing-Sans-One (способ работает со всеми шрифтами). Ссылка на подключение будет выглядеть таким образом

<link href="https://fonts.googleapis.com/css?family=Lato:300,300i,400,400i,700,700i|Racing+Sans+One&display=swap" rel="stylesheet">

Здесь мы грузим не шрифты, а таблицу стилей (css файл). И уже внутри этого файла @font-face загружает ресурсы шрифтов. То есть мы делаем 3 запроса:
Таблица стилей, и по запросу за каждым шрифтом.

Во втором пункте все еще хуже, мы делаем запрос за нашей таблицей стилей, а после всё те же 3 запроса. В итоге получаем 4. При хорошем интернет соединении на MacbookPro в последней версии Google Chrome затраченное время ~200-250 ms (ms - миллисекунд) в первом случае и ~230-280 ms во втором.

Кажется, не так и много, но при первом входе на сайт (пока ресурсы еще не закешировались) пользователь получает один из негативных эффектов:

FOIT (вспышка невидимого текста) - шрифтов вовсе нет - и через 200-250 ms они появляются.

FOUT (вспышка неустановленного текста) - отображается fallback шрифта (стандартный браузерный) и после загрузки он изменяется на шрифт из Google Fonts.

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

🚀 Начинаем ускорять:

Мы знаем что наш сервис ипользует домен fonts.gstatic.com для загрузки шрифтов, потому мы можем использовать preconnect.

<link rel="preconnect" href="https://fonts.gstatic.com/" crossorigin>

preconnect позволяет браузеру установить соединение прежде, чем HTTP-запрос будет отправлен на сервер. Эта процедура включает поиски DNS, «переговоры» TLS и «рукопожатие» TCP. Что в свою очередь устраняет круговые задержки и экономит время пользователя. (не работает в IE). Для IE можно использовать rel=“dns-prefetch”. Тоже сэкономит немножко времени.

Следующий шаг - минимизировать количество запросов. Так как именно на них тратится наибольшая часть времени. Избавимся от запроса за таблицей стилей со шрифтами и пойдём возьмем их напрямую.

Для этого пойже пройти в файл с таблицей стилей который мы грузили (в нашем случае это https://fonts.googleapis.com/css?family=Lato:300,300i,400,400i,700,700i|Racing+Sans+One&display=swap) и берем прямые сслыки на шрифты.
Смотрим на атрибут src: url().
Например для Lato - это url(https://fonts.gstatic.com/s/lato/v15/S6uyw4BMUTPHjx4wXiWtFCc.woff2)

Подключаем его с помощью тега <link> с особым набором атрибутов:

<link rel="preload" as="font" type="font/woff2" href=“тут ссылка на шрифт” crossorigin>

что делают эти атрибуты:
🔻 href - ссылка на ресурс (CDN шрифа)

🔻 rel=“preload” указывает браузеру декларативно извлекать ресурс, но не «исполнять» его (наш CSS будет поставлен в очередь использования).

🔻 as=“font” указывает браузеру, что он должен загружать, чтобы он мог установить соответствующий приоритет. Без этого браузер установил бы низкий приоритет по умолчанию.

🔻 type=“font/woff2” указывает браузеру тип файла, чтобы он загружал ресурс, только если он поддерживает этот тип файла.

🔻 crossorigin необходимо, потому что шрифты выбираются с использованием анонимного режима CORS.
Дальше подключаем загруженные шрифты в теге <style> если у вас SPA (сэкономите еще пару десятков секунд) или в вашей таблице стилей.

@font-face {
font-family: 'Lato';
font-style: normal;
font-weight: 400;
font-display: swap;
src: local('Lato Regular'), local('Lato-Regular'), url(“сслыка на шрифт”) format('woff2');
unicode-range: /* какой диапазон символов будет загружен из шрифта */
}


Если в правиле была объявлена функция local(), с названием шрифта, то будет производиться поиск на компьютере пользователя, и в случае обнаружения будет использован этот шрифт. Иначе будет скачан и использован шрифт, указанный в функции url().

Полное время загрузки сократилось до 40 - 50 ms. Код примера можно посмотрель здесь: https://codepen.io/anon/pen/MNwxzg
☑️ ЭФФЕКТИВНЫЙ КОД #1 HTML

Каждый день я читаю много кода. Просматривая домашки студентов, pull requestы на работе, тестовые задания кандидатов, или просто код людей которые его шарят в linkedin, twitter и github. Расскажу про некоторые вещи, которых лучше избегать при верстке сайта, ведь даже валидный и кроссбраузерный сайт может быть сверстан отвратительно. В свою очередь написание хорошего кода делает простым поддержку, доработку и масштабирование сайта. Некоторые моменты покажутся очевидными для опытных разработчиков, но начинающим будет полезно. Далее по пунктам:

🔻 Не используйте теги с целью стилизовать контент, а также “Deprecated tags” (устаревшие теги)
Теги <font>, <u>, <mark>, <s>, <big>, <center>, <strike>, <blink> (и прочие стилистических элементы)
Это задача CSS, не стоит мешать разметку со стилями в HTML

🔻 Не используйте устаревшие атрибуты и атрибуты для стилизации
start у <ol>, width у <hr> <img> и других, align у текстовых, и прочие выполняющие роль CSS.

🔻 Не использейте <br> для разметки
Не используйте <br> для форматирования документа или для добавления отступов между элементами.
Это нужно делать с помощью margin или padding в CSS.

🔻 Не засоряйте код <div>ами и <span>ами
Семантические элементы HTML5 доступно описывают свой смысл или назначение как для браузеров, так и для веб-разработчиков.
Код

<div class=“main”>
<div class=“menu”>
<div class=“menu-item”>
<div class=“image-wrapper”>
<img src=“” alt=“”/>
<div class=“image-description”></div>
</div>
</div>
</div>
</div>


может быть заменен на более семантичный и читабельный

<main>
<ul>
<li>
<figure>
<img src=“” alt=“”/>
<figcaption></figcaption>
</figure>
</li>
</ul>
</main>


🔻 Избегайте непоследовательности
Если вы решили использовать 4 пробела или два таба для отступа, сделайте это на каждой строчке вашего HTML кода. Правило также касается регистра, кавычек и прочих элементов разметки кода. Делайте все в одном стиле.

🔻 Не используйте таблицы для блочной верстки
Таблици - только для таблиц. Исключение может быть только верстка emailов.

🔻 Не используйте спецсимволы (типа <, >, «, § и т д.)
Это ведет к невалидному коду.
Замените их на HTML мнемоники. Это это конструкция, которая ссылается на символ из набора символов текстового файла.
Например:
знак меньше < это &lt;
знак больше > это &gt;

🔻 Избегайте тегов <iframe> и <frame>
Их нужно использовать всего в нескольких случаях:
встраивание медиаконтента — как правило стороннего (видео с YouTube, пост с Facebook);
встраивание апплетов — приложений, работающих в контексте веб-сайта (формы оплаты или интерактивные карты)

🔻 Не используйте теги <script> без атрибута src и <style> без причины.
Есть кейсы в которых необходимо использовать эти теги, но в большинстве случев их можно вынести в оддельный файл, что поможет вам избавится от копипаста и поможет в дальнейшем переиспользовании.

🔻 Не вкладывайте блочные элементы в строчные
Строчные элементы могут содержать только данные и другие строчные элементы. Исключение составляет элемент <a>, который согласно спецификации HTML5 может оборачивать целые абзацы, списки, таблицы, заголовки и целые разделы при условии, что они не содержат другие интерактивные элементы - другие ссылки и кнопки.
☑️ ВОПРОСЫ НА СОБЕСЕДОВАНИИ #2

🔻 1. Каким будет результат выполнения следующего кода?

const items = ["xyz", "xxxx", "test", “hello”, “qwe”];

delete items[3];
console.log(items.length);
// ?

Ответ:
console.log(items.length) // 5

💬 Пояснение:
Длина массива не изменится и будет равна 5. Если вывести в консоль массив items мы увидим следующее
["xyz", "xxxx", "test", undefined × 1,“qwe”]
Да, delete удаляет элемент из массива, но не так, как нам этого хочется. Образовалась «дырка».
Индексы элементов не изменились, и длина тоже.

🔻 2. Каким будет результат выполнения следующего кода?

function Foo(){}
Foo.prototype.bar = 42;

const foo = new Foo();

delete foo.bar;
console.log(foo.bar); // ?


Ответ:
console.log(foo.bar); // 42

💬 Пояснение:
Если объект наследует свойство от прототипа и не имеет собственного свойства с таким же именем, свойство не может быть удалено при обращении через этот объект. Однако можно удалить это свойство напрямую в прототипе.

delete Foo.prototype.bar; // удаляет свойство из прототипа
console.log(foo.bar); // undefined


🔻 3. Каким будет результат выполнения следующего кода?

a = 2;
delete a;
console.log(a); // ?



Ответ:
console.log(a); // undefined

💬 Пояснение:
Присвоение значения свойству без операторов var, const, let иногда неверно характеризуется, как глобальная переменная, (a = 2).
На самом деле происходит присвоение значения свойству глобального объекта. Оператор delete удалил поле a из обьекта window.

🔻 4. Каким будет результат выполнения следующего кода?

const a = 2;
delete a;
console.log(a); // ?


Ответ:
console.log(a); // 2

💬 Пояснение:
delete эффективен только применительно к свойствам объектов.
Он не оказывает никакого влияния на имена переменных и функций.

🔻 5. Каким будет результат выполнения следующего кода?

delete Math.PI;
console.log(Math.PI); // ?


Ответ:
console.log(Math.PI); // 3.1415…

💬 Пояснение:
delete не может удалить определенные свойства встроенных объектов (таких как Object, Array, Math и так далее)
☑️ МАЛОИЗВЕСТНЫЕ CSS СЕЛЕКТОРЫ

Селектор определяет, к какому элементу применять то или иное CSS-правило.
Основные селекторы всем хорошо знакомы. Но есть такие, которыми по каким-то причинам мало пользуются разработчики:

🔻 Псевдокласс :focus-within
Определяет стиль элемента, когда он сам или элементы внутри него получают фокус. В отличие от :focus, который применяется непосредственно к самому элементу, :focus-within работает для родителя. Это позволяет выделять цветом или другими методами всю форму или отдельные её части, когда пользователь работает с полями формы.

🔻 Селектор :nth-child используется для добавления стиля к элементам на основе нумерации в дереве элементов. Но допустим нам нужно выбрать диапазон элементов с 5 по 12 включительно. Это можно сделать вот так:

ul li:nth-child(n+5):nth-child(-n+12) {
background: lightpink;
}


В Safari такой приём работать не будет. Однако, решение есть нужно всего лишь перечислить элементы выборки в другом порядке:

ul li:nth-child(-n+12):nth-child(n+5) {
background: lightpink;
}


Как это работает. Первая часть выражения выбрает 5й элемент, а потом все после него. А вторая часть — 12й элемент, а потом все до него. На пересечении запросов получается требуемый диапазон.

🔻 Псевдоклассы пользовательского интерфейса (формы)

:disabled — используется для отбора и стилизации заблокированных для выбора и изменения элементов форм;

:enabled — отбирает не заблокированные для выбора и изменения элементы форм;

:checked — применяется для выбора и стилизации элементов <input type="radio">, <input type="checkbox">, а также элементов <option></option>, находящихся внутри элемента <select></select>;

:indeterminate — элементы радио и чекбокс могут быть включены или выключены пользователем. Некоторые из них могут находиться в неопределенном состоянии, к которым и применяется данный псевдокласс.

И многие другие: valid, invalid, required и т. д.

🔻 Псевдокласс :only-child
Находит любой элемент, являющийся единственным потомком родителя. Это тоже, что и :first-child:last-child или :nth-child(1):nth-last-child(1), но с меньшей специфичностью.


🔻 Псевдокласс :only-of-type
Элемент, который является единственным элементом данного типа в родительском элементе.

🔻 Псевдокласс :empty
Применяется к элементам, которые ничего в себе не содержат (да, это работает не только с формами). И вместо создания условной логики для <h1>{name}</h1> в шаблоне вы можете просто спрятать тег с помощью CSS, если он оказался пустым:
h1:empty { display: none; }

🔻 Псевдокласс :root
Находит корневой элемент(элемент <html>) и идентичен селектору по тегу html, но его специфичность выше. Полезно для объявления CSS переменных, и других глобальных значений.

🔻 Псевдокласс :target
Выбирает текущий целевой элемент на странице, то есть тот, к которому был осуществлён переход по ссылке внутри страницы. Так можно скрывать или показывать целые разделы страници. Пример:

div {
display: none;
}
div:target {
display: block;
}


<a href="#link1">Ссылка 1</a>
<a href="#link2">Ссылка 2</a>

<div id="link1">
<h3>Содержимое по ссылке 1</h3>
</div>

<div id="link2">
<h3>Содержимое по ссылке 2</h3>
</div>
☑️ КООРДИНАТЫ В БРАУЗЕРЕ

Чтобы передвигать элементы по экрану, или перемещатся между ними следует знать систему координат в браузере.
Так как этих свойст достаточно много (pageX, pageY, screenX, screenY, clientX, clientY, x, y, layerX, layerY, offsetX, offsetY) можно запутаться и выбрать неверный для конкретной задачи.

Все они работают в одной из нескольких указанных ниже систем координат:

✔️ Относительно окна браузера (как position:fixed) – привязывается к координатам окна, а не документа.
✔️ Относительно документа (как position:absolute) – на уровне документа, отсчёт идёт от верхнего левого угла документа.
✔️ Относительно родителя (для объектов событий мыши).
✔️ Физические координаты (для объектов событий мыши).

Относительно окна браузера:

🔻 clientX/clientY - относительно верхнего левого края полностью отображаемой области содержимого в браузере. Эта контрольная точка находится под строкой URL и кнопкой возврата в верхнем левом углу. Эта точка может находиться в любом месте окна браузера и перемещается при прокрутки страници. Значение в левом верхнем углу всегда будет (0,0) независимо от позиции скрола.

🔻 x/y - alias для MouseEvent.clientX/MouseEvent.clientY (то же самое что и clientX/clientY)

Относительно документа:

🔻 pageX/pageY - возвращает значение равное горизонтальной/вертикальной координате, относительно всего документа. Учитывает любую горизонтальную/вертикальную прокрутку страницы.

Относительно родителя:

🔻 offsetX/offsetY - то же что и pageX и pageY только относятся к родительскому контейнеру, а не документу.

🔻 layerX/layerY - являются позицией мыши относительно "ближайшего расположенного элемента", то есть элемента, свойство стиля position не является static.

И последний - физические координаты

🔻 screenX/screenY - по отношению к верхнему левому углу физического экрана/монитора эта контрольная точка перемещается только при увеличении или уменьшении количества мониторов или разрешения монитора.
☑️ МНОЖЕСТВЕННЫЕ ФОНЫ И ТЕНИ В CSS

Мы часто пользуемся свойствами background и box-shadow. Но не все знают про то, что эти свойства могут быть множественными и не используют их возможности по максимуму.

🔻Например чтобы сделать 2 фона у одного элемента нам не нужно использовать псевдоэлементы (или лишние вложенные элементы).

В CSS3 отлично работают множественные фоны. Мы можем применить несколько фонов к элементу. Они будут располагаться поверх друг друга: фон, заданный первым - в самом верху, последний фон - в самом низу. Задать множественные фоны легко:

background: url(a.png) no-repeat,
url(b.png) repeat-x,
url(c.png) 100% 100% repeat-y;


или в развернутой форме

background-image: url(a.png), url(b.png), url(c.png);
background-repeat: no-repeat, repeat-x, repeat-y;
background-position: 0 0, 0 0, 100% 100%;


🔻 Такая же ситуация с тенями у элементов. Свойство box-shadow дает возможность добавлять к блочным элементам множественные тени (внутренние и внешние). Для этого тебе нужно указать значения цвета, размера, размытости и смещения ( color, size, blur и offset). Перечислив их через запятую можно сделать несколько теней у одного элемента.

box-shadow: /* устанавливаем 4 внешних тени для элемента */
20px 15px 30px yellow,
-20px 15px 30px green,
-20px -15px 30px blue,
20px -15px 30px red;


Работает и для text-shadow.

text-shadow: /* устанавливаем 4 тени для текста */
0 0 4px white, 0 -5px 4px violet,
2px -10px 6px indigo,
2px -60px 33px orange,
0px -85px 40px red;


Множественные тени могут служить не только для создания теней, а и для других декоративных частей элементов. Вот например «Мoна Лиза», или «Джоконда» сделанная с использованием одного только свойства box-shadow. (код https://codepen.io/jaysalvat/pen/HaqBf).
Превью: https://www.dropbox.com/s/zhjfdc6oqjlfcv7/mona_lisa_box_shadow.png
☑️ ПРОГРАММИСТ - ПРОФЕССИЯ ИЗБРАННЫХ?

Давно не публиковал ничего, но после перерыва возобновляю активность на канале.
Сегодня пост не про технологии, а про программирование в целом. Поделюсь мыслями почему я, считаю это обычным ремеслом, а не профессией избранных умов и гениев (не без исключений конечно).

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

А вот вам биография эталонного программиста (ну HR порадуются такому в любом случае).
В детстве рано получил комп, играл в игрушки, стало интересно их делать, начал учить Pascal | Delphi | любой другой ЯП (нужное подчеркнуть), потом универ, после работа и на фоне всего этого тонны прочитанных книг по разработке софта и решение олимпиадных задач.

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

Да, конечно, это все вам нужно если вы собираетесь устраиваться в Google/Facebook, писать игровые движки с нуля, заниматься машинным интеллектом или запускать ракеты в космос.
Но если вы собираетесь делать сайты для продажи очередного крутого продукта / новую соцсеть / приложение, которое изменит мир (нет) и т д. то для вас хорошие новости — не нужно быть гением и знать алгоритм Косарайю наизусть.

Я не собираюсь занижать ценность выше перечисленных качеств и знаний. Но они ни в коем случае не являются ключевыми при вхождении в ИТ и даже в работе большого количества разработчиков. На изучение теории может быть затрачено слишком много времени, которое стоило бы потратить на практику в какой-то конкретной области программирования. Огромное количество паттернов уже имеют реализацию в самом языке и его библиотеках. Написание кода на фреймворках и движках не позволит вам отойти от заложенных в него канонов. Многое уже сделано до вас и писать каждый раз велосипед не придется. Из математики важен только один раздел — логика. Умение гуглить спасает больше, чем умение запоминать тонны редко используемой информации (нужно только запоминать что гуглить).

И пару слов про возраст. Многих это смущает и я понимаю почему. Одному моему студенту сейчас за 60 лет. Он пришел на курсы, чтобы выучить HTML/CSS. На первом занятии я был на 100% уверен что на следующее он не придет. Он не пропустил ни одного занятия и сделал дипломный проект лучше всех. Верстка была ему нужна чтобы красиво рисовать графики для ошибок измерений (как оказалось, он был профессором МГУ института прикладной математики на пенсии, но все же).

В каком то смысле программирование действительно ремесло для избранных.
Только избранные в этом случае те, кому хватает упорства и терпения чтоб бесконечно учится и не останавливают неудачи и сложности.
☑️ НОВЫЕ ВОЗМОЖНОСТИ, КОТОРЫЕ JavaScript ПОЛУЧИЛ В 2019 ГОДУ

❗️Изменения в классах JavaScript

Разработчики сделали # частью синтаксиса. Теперь с помощью этого символа можно сделать настоящие приватные свойства. До этого приватные свойства были лишь договоренностью, записаны через видимый пробел(нижнее подчёркивание). Ничто не мешало использовать префикс для других целей, использовать другой префикс, или не использовать вовсе. Сейчас же, такая возможность появилась в самом языке.

class User {
#name;
#surname;

constructor(name, surname) {
this.#name = name;
this.#surname = surname;
}

get fullName() {
return ${this.#name} ${this.#surname}
}
}

const john = new User('John', 'Doe')

john.fullName // "John Doe"
john.name // undefined
john.#name // Uncaught SyntaxError: Private field '#name' must be declared in an enclosing class


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

❗️Стандартизированный объект globalThis

Исторически, для доступа к глобальному объекту использовался разный синтаксис в разных средах JavaScript. В вебе вы могли использовать window, self или frames, но в Worker можно использовать только self. В Node.js ничего из этого не работает, поэтому вы должны использовать global. Свойство globalThis даёт возможность стандарного доступа к глобльному значению this (и как следствие, к глобальному объекту) вне зависимости от окружения. Полезно для изоморфных приложений.

❗️Строковые методы trimStart() и trimEnd()

В добавок к методу trim(), с помощью которого можно удалить пробелы с обоих концов строки. Вводятся методы trimStart() и trimEnd(), которые дадут больше контроля над удалением пробелов.

❗️flat() и flatMap() для массивов

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

const arr1 = [1, 2, [3, 4]];
arr1.flat(); // [1, 2, 3, 4]

const arr2 = [1, 2, [3, 4, [5, 6]]];
arr2.flat(); // [1, 2, 3, 4, [5, 6]]

const arr3 = [1, 2, [3, 4, [5, 6]]];
arr3.flat(2); // [1, 2, 3, 4, 5, 6]


Также он удаляет не определенные элементы массива:

const arr4 = [1, 2, , 4, 5];
arr4.flat(); // [1, 2, 4, 5]


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

Всё выше перечисленное отлично работает в последней версии Google Chrome без передварительной Babel транспиляции.
СТРОКОВЫЕ МЕТОДЫ slice() vs substr() vs substring()

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

Все эти методы:
➡️ предназначены для извлечения части строки
➡️ не модифицируют исходную строку (создают копию)
➡️ первый аргумент везде начальный индекс (с какого символа начать копирование/извлечение)

❗️slice()
Принимает два параметра slice(start, end) (оба параметра опциональны)
start - индекс, с которого начинается извлечение. Если опущен - равен нулю Если отрицателен, то трактуется как “полная длина строки длина строки” + start.
end - индекс, которым заканчивать извлечение (не включая элемент под данным индексом).
Если опущен, slice извлечёт всё до конца строки. Если отрицателен, то трактуется как “полная длина строки длина строки” + end.

💭 Примеры:
"frontend tips".slice( 0, 5 ) // "front”
"frontend tips".slice( -8, -5 ) // “end”
"frontend tips".slice( -4 ) // “tips”


❗️substring()
Почти полностью идентичен slice (такие же параметры start и end, работает аналогично) с некоторыми НО:

- не поддерживает отрицательные значения (они интерпретируются как 0)
- можно задавать start больше end (в этом случае они поменяются местами)

💭 Примеры:
"frontend tips".substring( 9 ) // “tips"
"frontend tips".substring( -5, 8 ) // “frontend"
"frontend tips".substring( 8, 5 ) // “end"
"frontend tips".substring( 5, 8 ) // “end"


❗️substr()
Главное отличие substr() от двух предыдущих состоит в том, что указывать нужно длину, а не конечную позицию (substr(start, length) - start начало, length - длина/количество символов).
Значение первого аргумента может быть отрицательным, тогда позиция определяется с конца.

💭 Примеры:
"frontend tips".substr( 5, 8 ) // "end tips"
"frontend tips".substr( -4, 4 ) // “tips"


Итого:
Используйте slice() или substring() если вы знаете индекс (позицию), на которой вы остановитесь (но НЕ включите). Я на практике всегда использую slice().
Используйте substr() если вы знаете количество символов для извлечения.
☑️ ОПРЕДЕЛЯЕМ ТЕМУ ОПЕРАЦИОННОЙ СИСТЕМЫ И РЕАЛИЗУЕМ ПОДДЕРЖКУ ТЕМ НА САЙТЕ

В последнее время набрали популярность темные и светлые темы интерфейсов. В новом обновлении на IOS интегрировали тёмный режим. Расскажу про простой и красивый сбособ сделать светлую/темную темы интерфейса сайта в зависимости от темы ОС пользователя.

С помощью CSS переменных определяем базовые свойства тем (фон, цвет, тени и т. д.)

:root {
/* light */
--theme-light-background: #fff;
--theme-light-color: #000;
/* dark */
--theme-dark-background: #000;
--theme-dark-color: #fff;
}


Определяем тему операционной системы пользователя с помощью нового @media запроса prefers-color-scheme (определяет light и dark).

@media (prefers-color-scheme: light) {
.themed {
--background: var(--theme-light-background);
--color: var(--theme-dark-color);
}
}


Аналогичто определяем и для dark. А в самом классе используем определенные значения.

.themed {
background-color: var(—background);
color: var(—color);
}


Если нам не нужен переключатель тем то на этом все.
Тестировал на MacBook и iPhone (протестить можно здесь: https://jsfiddle.net/sam4hp9o/1/)

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

// определяем тему
const isDarkMode = window.matchMedia("(prefers-color-scheme: dark)").matches
const isLightMode = window.matchMedia("(prefers-color-scheme: light)").matches
const isNotSpecified = window.matchMedia("(prefers-color-scheme: no-preference)").matches
const hasNoSupport = !isDarkMode && !isLightMode && !isNotSpecified;

// дефолтная тема может быть и не определена, тогда используем на собственное усмотрение
// можно в зависимости от времени суток ночью - темную, днем - светлую

// вешаем обработчики событий на изменение
window.matchMedia("(prefers-color-scheme: dark)").addListener(e => e.matches && activateDarkMode())
window.matchMedia("(prefers-color-scheme: light)").addListener(e => e.matches && activateLightMode())


// определяем поведение при инициализации
if(isDarkMode) activateDarkMode()
if(isLightMode) activateLightMode()


Реализуем activateDarkMode и activateLightMode (Значения переменных CSS можно менять через JS)

const html = document.querySelector(':root')
html.style.setProperty("--background", “var(--theme-light-background)”);


Или просто добавляем нужный класс к root елементу.

Добавляем чекбокс или кнопку управления темой (вызовами activateDarkMode и activateLightMode соответственно).
Пример здесь: https://jsfiddle.net/sam4hp9o/2/
☑️ УЛУЧШАЕМ ТИПОГРАФИКУ КАВЫЧЕК НА САЙТЕ

В русском языке традиционно применяются французские «ёлочки», а для кавычек внутри кавычек и при письме от руки — немецкие „лапки“.

Правильно:
«„Цыганы“ мои не продаются вовсе», — сетовал Пушкин.

Неправильно:
««Цыганы» мои не продаются вовсе», — сетовал Пушкин.

В некоторых европейских языках (Хорватии, Дании, Швеции) открывающая кавычка выглядит как французская закрывающая и наоборот.
»citirati«

В иероглифической письменности существуют свои кавычки.
『 引 號 』

У нас есть возможность использовать разные кавычки на странице: “ ‘ « < и т д (в utf-8 их очень много разных).
Контролировать их не самая интересная задача. Для этого нужно использовать CSS свойство quotes. Его использование упростит жизнь контент менеджерам и вам самим.

💭 Пример:
CSS
.quote {
/* порядок кавычек следующий */
/* "левая кавычка" "правая кавычка" "левая кавычка 2-го уровня“ “правая кавычка 2-го уровня“ и т. д. */
quotes: "«" "»" "„" "“";
/* можно использовать сколько угодно пар кавычек */
/* можно использовать любые другие символы */
}
.quote:before {
content: open-quote;
}
.quote:after {
content: close-quote;
}


HTML
<span class=“quote”>
Первая открывающая
<span class=“quote”>
Вторая открывающая и закрывающая
</span>
Первая закрывающая
</span>


Результат (доступен код):
«Первая открывающая „Вторая открывающая и закрывающая“ Первая закрывающая»

Особенно полезно будет на мультиязычных сайтах.
В зависимости от языка можно управлять кавычками.
.quote:lang(de) {
quotes: "„" "“";
}


Можно глобально настроить на все элементы, к конкретному классу(как в примере) или тегам <q>/<blockquote>.

Использование свойства quotes значительно улучшит типографику вашего сайта, а этот момент многие упускают из виду.
☑️ ОПТИМИЗИРУЕМ ЗАГРУЗКУ ИЗОБРАЖЕНИЙ

Наличия изображений на сайте делает их привлекательными, информативными и качественными.
Но так же, картинки зачастую являются самой «тяжелой» для браузера частью страницы. Большие картинки долго загружаются, особенно при нестабильном или медленном интернет соединении.
Оптимизация изображений позволяет значительно ускорить загрузку сайта.
Разберем несколько способов оптимизации картинок:

Cамым простым действием может быть сжатие.
Это даст быстрый результат в плане скорости загрузки.
В большинстве случаев сжатие на 30-40% можно сделать без потери качества.
Ресурсов для сжатия изображений существует множество.
Но что делать если картинки уже и так сжаты, а загрузка все равно медленная?

Распространенной проблемой является то, что большинство сайтов рассчитано на множество девайсов (т. е. адаптивные).
Картинки же используются одни и те же на всех устройствах.
Хотя на телефоне шириной в 320 пикселей нам абсолютно незачем использовать картинку размером 1200 пикселей.

С помощью тега <picture> мы можем загружать картинку в зависимости от размера дисплея.
Он содержит два разных тега: один или несколько <source> и один <img>.
💭 Пример:
<picture>
<source media="(min-width: 650px)" srcset="img_pink_flowers.jpg">
<source media="(min-width: 465px)" srcset="img_white_flower.jpg">
<img src="img_orange_flowers.jpg" alt="Flowers">
</picture>


<picture> требует <img> в качестве своего последнего потомка. Без этого ничего не выведется. Это хорошо для совместимости, так как есть только одно место для альтернативного текста (alt) и хорошо для поддержки содержимого в старых браузерах (если тег <source> не поддерживается). Eсли ни один из атрибутов media или type тегов <source> не соответствует, будет показан тег <img>.

Атрибуты тега source который вы могли не встречать:
srcset (обязательный) - Определяет URL-адрес изображения для отображения.
media - является таким же, как и при использовании в CSS media queries.
Можно использовать те же самые проверки, т.е. проверять max-width, min-width, max-height, min-height, orientation и т.д.
sizes - определяет один дескриптор ширины, один запрос носителя с дескриптором ширины или список с разделителями-запятыми запросов мультимедиа с дескриптором ширины. (простыми словами размер картинки на определенном устройстве).
type - определяет тип MIME.

Тег <picture> работает во всех современных браузерах. Не работает в IE 11, но для него есть полифилл (picturefill).

Для еще большей производительности данную технику можно совместить с “ленивой” загрузкой изображений.
Суть ленивой загрузки в том, чтобы откладывать загрузку содержимого элементов <img> находящихся за пределами видимой области страницы, до тех пор, пока пользователь, прокручивая страницу, не окажется достаточно близко к этим элементам.
В последних версиях Google Chrome нативно поддерживает эту технологию.
💭 Пример:
<picture>
<source media="(min-width: 40em)" srcset="big.jpg 1x, big-hd.jpg 2x">
<source srcset="small.jpg 1x, small-hd.jpg 2x">
<img align="center" src="fallback.jpg" loading="lazy">
</picture>


Атрибут loading имеет несколько значений:
lazy - указывает на материалы, которые являются хорошими кандидатами на ленивую загрузку.
eager - материалы в элементах с таким значением атрибута нужно загрузить без промедления.
auto - браузер самостоятельно примет решение о том, применять ли к материалам с этим значением атрибута ленивую загрузку.

Атрибут loading можно использовать и без тега <picture>, просто на картинках.

Для остальных браузеров можно использовать библиотеку полифилов (lazysizes).
Привет 👋
Нас уже почти пол тысячи 🔥
Хочу лучше узнать свою аудиторию, так что, будь добр, проголосуй внизу 👇
Anonymous Poll
45%
Я - опытный разработчик
29%
Я - закончил курсы по Frontend
14%
Я - обучаюсь на курсах по Frontend
12%
Я - хочу освоить Frontend
☑️ ИНТЕРАКТИВНОЕ СРАВНЕНИЕ ИЗОБРАЖЕНИЙ ИЛИ «БЫЛО — СТАЛО» НА ЧИСТОМ CSS

Временами появляется надобность показать зрительные различия меж 2-мя изображениями, как правило, до и после сопоставления. К примеру, презентация эффектов фото манипуляции в портфолио, итогов косметических процедур, видимых результатов катастрофического события или как меняются объекты с течением времени.

Первое что приходит в голову это изменять размер контейнера через JS на onmousedown & onmousemove. Но можно ли решить такую задачу без JavaScript? Оказалось что можно, и решение оказывается в css свойстве resize.
Вы скорее всего использовали это свойство с элементами textarea (указывает, можно ли пользователю изменять размеры текстового поля). На Mozilla MDN сказано:
Applies to elements with overflow other than visible, and optionally replaced elements representing images or videos, and iframes. То есть применить можно к любому элементу, у которого свойство overflow отличается от visible.
Принимает он такие значения: none | both | horizontal | vertical. Мы будем использовать свойство horizontal.

💭 Далее реализуем разметку:
<div class="slider">
<div class="resize-image">
<img src="" />
</div>
<img src="" />
</div>

💭 Через CSS накладываем одно изображение на другое при помощи абсолютного позиционирования.
.slider {
position: relative;
}

.resize-image {
position: absolute;
top: 0;
bottom: 0;
left: 0;
width: 50%;
max-width: 100%;
overflow: hidden;
resize: horizontal;
}

.resize-image::before {
// стилизируем ресайзер
}

Готово, получаем отличное решение без скриптов.

Однако есть и недостатки у этого способа:
— пользователь может изменить размер верхнего изображения только из его правого нижнего угла
— управление слайдером недоступно с клавиатуры.

PS: Это решение нашлось в книге, которой я с вами делался еще в самом первом посте (Секреты CSS. Идеальные решения ежедневных задач).
☑️ АРГУМЕНТЫ ФЛАГИ

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

Рассмотрим пример кода (очень близко к тому, который я встретил в реальном проекте), который делает запрос за данными, получает список пользователей, и выводит их на страницу:

const users = await userService.fetch();

userComponent.render(users);

Этот код используется во многих частях проекта, чтобы его не дублировать, мы вынесем его в отдельную функцию. Обычная практика DRY.

const UserFacade = {
update: async () => {
const users = await userService.fetch();

userComponent.render(users);
}
}


Все хорошо, но клиент требует показывать лоадер при обновлении пользователей (т. е. при вызове данной функции). Но лоадер нужно показывать не всегда, а только при вызове из определенных мест в коде. Простым решением может показаться добавление флага (или даже нескольких):

const UserFacade = {
update: async (showLoader = false) => {
if (showLoader) { // показываем лоадер если нужно
userComponent.renderLoader(true);
}

const users = await userService.fetch();

userComponent.render(users);
userComponent.renderLoader(false); // скрываем лоадер в любом случае
}
}


UserFacade.update и userComponent.renderLoader теперь принимают флаги true и false.
В зависимости от переданного аргумента выполняется конкретная логика: либо мы отображаем список пользователей без лоадера либо с ним.
Получившийся код оказывается очень сложным для чтения, для понимания и нарушает множество принципов проектирования. Чтобы понять, что делают эти методы нам нужно заглядывать в названия параметров функции. Потому что вызов UserFacade.update(true) и UserFacade.update(false) не дают абсолютно никакого понимания о том, что происходит внутри.

Мне встречались подобные методы принимающие 4(!) аргумента, 3 из которых были флагами. Выглядело это примерно так:

SomeFacace.updateSomeData(uuid, true, false, true)

Мало того, что аргументов слишком много, так еще и совершенно не понятно что они делают.
Этот метод использовался во многих местах, а время на рефакторинг не было, я переписал его так, чтобы он принимал объект.

SomeFacace.updateSomeData(uuid, {
showLoader: true,
updateCases: false,
scrollToCase: true
});


Теперь можно понять что мы меняем без перехода к исходникам, но стало не намного лучше.

Улучшить код в примере с UserFacade можно следующим образом. Переписать UserFacade и userComponent так, чтобы они не принимали флаги.

const UserFacade = {
updateWithLoader async () => {
userComponent.showLoader();
await this.update();
userComponent.hideLoader();
},
update: async () => {
const users = await userService.fetch();

userComponent.render(users);
}
}


Код стал чище, понятнее, а функции теперь вообще не принимают никаких аргументов.

И в подтверждение моих слов цитата из книги Роберта Мартина «Чистый код»:
«Аргументы-флаги уродливы. Передача логического значения функции — воистину ужасная привычка. Она немедленно усложняет сигнатуру метода, громко провозглашая, что функция выполняет более одной операции…»