Русский
Русский
English
Статистика
Реклама

Tips and tricks

Топ-10 Angular-приемов, выбранных сообществом

12.08.2020 20:21:34 | Автор: admin
В течение июня Саша Инкин и я, Рома Седов, запилили в Твиттере челлендж. Каждый день мы публиковали по совету, хитрости или просто какой-нибудь полезной штуке из нашего опыта работы с Angular. Это движение было тепло встречено и поддержано комьюнити разработчиков.
Я решил написать статью о десяти приемах, которые набрали больше всего лайков, а также объяснить показанные в них концепции более подробно.

Давайте начнем!



Токенизируй глобальные объекты!


Самый популярный твит рассказывает о DI-токенах глобальных объектов.
Во фронтенде мы привыкли к тому, что в глобальном окружении есть множество объектов. Мы используем объекты вроде window, document, fetch-метод, location и никогда не ожидаем, что их может и не быть.

Но, к примеру, в Angular Universal или тестовом окружении в Jest нет ни браузера, ни window-объекта, ни DOM. А передавая эти сущности по приложению через токены, у вас никогда не будет проблем с их использованием, заменой или тестированием вашего кода.


Control value как ReplaySubject


Могут быть ситуации, когда необходимо подписаться на valueChanges реактивного контрола, начав стрим с его текущего значения. Теперь можно не изобретать велосипед, а просто добавить к себе такую обертку:


Не забывайте о пайпах


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

А вот пример универсального пайпа для преобразования данных:


RxJS неизведанный мир


Я всегда стараюсь проверять все параметры и перегрузки у RxJS-операторов, которые использую. Причина: во многих из них есть скрытые опции, узнав которые можно писать некоторые стримы быстрее и проще.

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


Или оператор defaultIfEmpty, который позволяет заложить полезную логику в стрим в рамках одной строчки кода:


Заключение


Angular большой, и в нем еще множество фишек. У меня есть ряд идей для новых приемов и хитростей из наших лучших практик, которыми я планирую поделиться с сообществом. Если вам интересны подобные новые твиты, подписывайтесь на меня в Твиттере. И хорошего вам дня!
Подробнее..

Перевод Три редко используемых возможности Python 3, о которых каждый должен знать

09.08.2020 22:07:12 | Автор: admin


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

Данная статья является переводом 3 Neglected Features in Python 3 That Everyone Should Be Using.

Перечисления


Перечисления я много использовал в Java и Swift. Продолжаю их использовать теперь и в Python.

Объявление перечисления в Python очень просто сделать и это было возможно и до третьей версии (хотя и с ограничениями):

from enum import Enumclass State(Enum):  AIR = 0  LAND = 1  SEA = 2  myState = State.AIR# Выводит 0print(myState.value)# Выводит AIRprint(myState.name)

В коде выше перечисление вводится путем объявления класса, наследованного от Enum. А далее просто описываются все нужные состояния. В моем случае: AIR, LAND и SEA.

Функциональность, которая была добавлена в Python 3 возможность использовать .value и .name. Они позволяют получить число и строку соответствующие перечислению.

Например, вывод значения State.LAND.name будет LAND.

Перечисления полезны в коде, когда вы хотите иметь некоторые текстовые идентификаторы для констант. Например, вместо сравнения состояния с 0 или 1 гораздо показательнее сравнивать с State.MOVING или State.STATIONARY. Константы могут меняться и если кто-то посмотрит код позже, то слово MOVING даст гораздо больше понимания, чем 0. В результате сильно повышается читабельность кода.

Больше информации можно найти в официальной документации Python 3 по Enum.

Форматирование


Добавленные в версии 3.6, fstrings это мощное средство форматирования текста. Они позволяют создавать гораздо более читабельный и безошибочный код (чем я наслаждаюсь после перехода из Java). Это лучше, чем format, который использовался ранее в Python. Вот пример использования format:

name = 'Михаил'blog_title = 'codeatcpp.com'# Привет, меня зовут Михаил и я пишу в своем блоге codeatcpp.com.a = "Привет, меня зовут {} и я пишу в своем блоге {}.".format(name, blog_title)

Легко заметить пустые фигурные скобки внутри строки и после список с названиями переменных в определенном порядке.

Теперь посмотрим на такой же код, но с использованием fstring более читабельный и очень похожий на способ форматирования в Swift.

name = 'Михаил'blog_title = 'codeatcpp.com'# Привет, меня зовут Михаил и я пишу в своем блоге codeatcpp.com.a = f"Привет, меня зовут {name} и я пишу в своем блоге {blog_title}."

Чтобы получить такую аккуратную строку, нужно всего лишь поместить букву f перед кавычками, и затем вместо пустых скобок можно сразу писать названия переменных или данные прямо в строке. Поскольку переменные пишутся прямо в строке, нет необходимости считать количество элементов и следить в каком порядке располагать переменные в конце. Они просто находятся сразу там, где нужны их значения.

Использование fstring дает более читабельный и более простой в поддержке код, чем использование классических подходов.

Классы данных


Классы данных может быть более непонятная тема, чем предыдущие, поэтому потребует чуть больше пояснений. Классы данных это что-то, что мне очень понравилось в языке Kotlin, поэтому я люблю их использовать также и в Python.

Класс данных это класс, единственная цель которого хранить данные. Класс содержит переменные, которые можно читать и писать, но не имеет никакой дополнительной логики.

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

Использование класса данных лучше показывает что вы пытаетесь сделать и также упрощает создание юнит-тестов.

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

from dataclasses import dataclass# Определяем класс данных@dataclassclass Vector3D:    x: int    y: int    z: int      # Создаем векторu = Vector3D(1,1,-1)# Выводит: Vector3D(x=1, y=1, z=-1)print(u)

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

Хотя функциональность созданного Vector3D сильно ограничена, суть использования класса данных в том, чтобы повысить эффективность и уменьшить количество ошибок в коде. Ведь гораздо лучше передавать в качестве параметра Vector3D, чем набор переменных типа int.

Больше информации про декоратор @dataclass можно найти в официальной документации Python 3.

Заключение


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

Фильтры действий, или Как просто улучшить читаемость кода

25.01.2021 16:17:27 | Автор: admin

Введение


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

Роль фильтров в процессе обработки запроса


Сначала обсудим сами фильтры: для чего же они нужны? Фильтры позволяют выполнять определённые действия на различных стадиях обработки запроса в ASP.NET Core. Существуют следующие встроенные фильтры:

  • Фильтры авторизации (Authorization filters) выполняются самыми первыми и определяют, может ли пользователь выполнить текущий запрос.
  • Фильтры ресурсов (Resource filters) вызываются после фильтров авторизации и необходимы, как следует из названия, для обработки ресурсов. В частности, данный тип фильтров применяют в качестве механизма кэширования.
  • Фильтры действий (Action Filters) выполняют указанные в них операции до и после выполнения метода контроллера, обрабатывающего запрос.
  • Фильтры исключений (Exception Filters) используются для перехвата необработанных исключений, произошедших при создании контроллера, привязке модели и выполнении кода фильтров действий и методов контроллера.
  • И наконец, самыми последними вызываются фильтры результатов (Result Filters), если метод контроллера был выполнен успешно. Данный тип фильтров чаще всего используется, чтобы модифицировать конечные результаты, например, мы можем создать свой заголовок ответа, в котором добавим нужную нам информацию.


Ниже представлена схема, которая показывает, в каком порядке вызываются фильтры в процессе обработки запроса:



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

Внутреннее устройство фильтров действий


Фильтры действий в ASP.NET


Интерфейс IActionFilter, который нужно реализовать, чтобы создать фильтр действий, существовал ещё в ASP.NET MVC. Он определяет методы OnActionExecuting, который вызывается перед выполнением метода контроллера, и OnActionExecuted, который вызывается сразу после. Ниже представлен пример простейшего фильтра действий, который выводит информацию во время отладки приложения до и после выполнения метода контроллера:

public class CustomActionFilter:IActionFilter {         public void OnActionExecuting(ActionExecutingContext filterContext)         {             Debug.WriteLine("Before Action Execution");         }         public void OnActionExecuted(ActionExecutedContext filterContext)         {             Debug.WriteLine("After Action Execution");         } }


Чтобы использовать вышеуказанный фильтр, его нужно зарегистрировать. Для этого в файле FilterConfig.cs, который находится в папке App_Start, следует добавить следующую строку:

public static void RegisterGlobalFilters(GlobalFilterCollection filters) {         filters.Add(new HandleErrorAttribute());         filters.Add(new CustomActionFilter()); }


Но гораздо удобнее использовать фильтры как атрибуты. Для этих целей существует абстрактный класс ActionFilterAttribute, который унаследован от класса FilterAttribute, а также реализует интерфейсы IActionFilter и IResultFilter. Таким образом, наш класс можно переписать следующим образом:

public class CustomActionFilterAttribute:ActionFilterAttribute {         public override void OnActionExecuting(ActionExecutingContext filterContext)         {             Debug.WriteLine("Before Action Execution");         }         public override void OnActionExecuted(ActionExecutedContext filterContext)         {             Debug.WriteLine("After Action Execution");         } } 


Теперь, чтобы применить наш фильтр, мы добавляем его к методу контроллера следующим образом:

public class HomeController : Controller {         [CustomActionFilter]         public ActionResult Index()         {             return View();         } }


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

Фильтры действий в ASP.NET Core


С появлением ASP.NET Core в фильтрах действий произошёл ряд изменений. Кроме интерфейса IActionFilter, теперь имеется ещё и IAsyncActionFilter, который определяет единственный метод OnActionExecutionAsync. Ниже приведён пример класса, реализующего интерфейс IAsyncActionFilter:

public class AsyncCustomActionFilterAttribute:Attribute, IAsyncActionFilter {         public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)         {             Debug.WriteLine("Before Action Execution");             await next();             Debug.WriteLine("After Action Execution");         } } 


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

Применяют такой фильтр так же, как и синхронный:

public class HomeController : Controller {         [AsyncCustomActionFilter]         public ActionResult Index()         {             return View();         } }


Также изменения затронули абстрактный класс ActionFilterAttribute: теперь он наследуется от класса Attribute и реализует синхронные и асинхронные интерфейсы для фильтров действий (IActionFilter и IAsyncActionFilter) и для фильтров результатов (IResultFilter и IAsyncResultFilter), а также интерфейс IOrderedFilter.

Фильтры действий в действии


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

public class Employee {         [Required(ErrorMessage = "First name is required")]         public string FirstName { get; set; }         [Required(ErrorMessage = "Last name is required")]         public string LastName { get; set; }         [AgeRestriction(MinAge = 18, ErrorMessage = "Date of birth is incorrect")]         public DateTime DateOfBirth { get; set; }         [StringLength(50, MinimumLength = 2)]         public string Position { get; set; }         [Range(45000, 200000)]         public int Salary { get; set; } } 


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

После того как были реализованы методы POST и PUT, мы видим, что оба метода содержат повторяющиеся части кода:

[HttpPost] public IActionResult Post([FromBody] Employee value) {             if (value == null)             {                 return BadRequest("Employee value cannot be null");             }             if (!ModelState.IsValid)             {                 return BadRequest(ModelState);             }             // Perform save actions             return Ok(); } [HttpPut] public IActionResult Put([FromBody] Employee value) {             if (value == null)             {                 return BadRequest("Employee value cannot be null");             }             if (!ModelState.IsValid)             {                 return BadRequest(ModelState);             }             // Perform update actions             return Ok(); } 


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

public class EmployeeValidationFilterAttribute : ActionFilterAttribute {         public override void OnActionExecuting(ActionExecutingContext context)         {             var employeeObject = context.ActionArguments.SingleOrDefault(p => p.Value is Employee);             if (employeeObject.Value == null)             {                 context.Result = new BadRequestObjectResult("Employee value cannot be null");                 return;             }             if (!context.ModelState.IsValid)             {                 context.Result = new BadRequestObjectResult(context.ModelState);             }         } } 


Теперь удаляем ставшие ненужными части кода и применяем созданный нами атрибут-фильтр:

public class EmployeeController : ControllerBase {         [EmployeeValidationFilter]         [HttpPost]         public IActionResult Post([FromBody] Employee value)         {             // Perform save actions             return Ok();         }         [EmployeeValidationFilter]         [HttpPut]         public IActionResult Put([FromBody] Employee value)         {             // Perform update actions             return Ok();         } } 


Теперь код выглядит гораздо компактнее и красивее, но в нашем случае его ещё можно упростить: т.к. в контроллере всего 2 метода и оба используют один и тот же фильтр, то можно применить атрибут непосредственно к контроллеру:

[EmployeeValidationFilter] public class EmployeeController : ControllerBase {             // Perform update actions } 


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

Разработчики часто сталкиваются с задачей, когда требуется добавить логирование для определённых методов. Поэтому попробуем добавить в фильтры действий средство логирования, которое будет записывать информацию перед выполнением методов POST или PUT контроллера и сразу после. Наш фильтр будет выглядеть следующим образом:

public class LoggingFilter: IActionFilter {         private readonly ILogger _logger;         public LoggingFilter(ILoggerFactory loggerFactory)         {             _logger = loggerFactory.CreateLogger<LoggingFilter>();         }         public void OnActionExecuted(ActionExecutedContext context)         {             _logger.LogInformation($"{context.ActionDescriptor.DisplayName} executed");         }         public void OnActionExecuting(ActionExecutingContext context)         {             _logger.LogInformation($"{context.ActionDescriptor.DisplayName} is executing");         } } 


Теперь мы можем применить этот фильтр либо глобально, либо к конкретной области. Сначала попробуем зарегистрировать его глобально. Для этого нам в Startup.cs следует добавить следующие строки:

services.AddControllers(options => {                 options.Filters.Add<LoggingFilter>(); }); 


Если же нам нужно применить фильтр, например, к определённому методу контроллера, то следует его использовать вместе с ServiceFilterAttribute:

[HttpPost] [ServiceFilter(typeof(LoggingFilter))] public IActionResult Post([FromBody] Employee value) 


ServiceFilterAttribute является фабрикой для других фильтров, реализующей интерфейс IFilterFactory и использующей IServiceProvider для получения нужного фильтра. Поэтому в Startup.cs нам необходимо зарегистрировать наш фильтр следующим образом:

services.AddSingleton<LoggingFilter>(); 


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

Внедрение зависимостей в фильтры действий позволяет создавать удобные атрибуты-утилиты, которые можно легко переиспользовать. Ниже представлен пример фильтра, который проверяет наличие объекта в хранилище по Id и возвращает его, если он есть:

public class ProviderFilter : IActionFilter {         private readonly IDataProvider _dataProvider;         public ProviderFilter(IDataProvider dataProvider)         {             _dataProvider = dataProvider;         }         public void OnActionExecuted(ActionExecutedContext context)         {         }         public void OnActionExecuting(ActionExecutingContext context)         {             object idValue;             if (!context.ActionArguments.TryGetValue("id", out idValue))             {                 throw new ArgumentException("id");             }             var id = (int)idValue;             var result = _dataProvider.GetElement(id);             if (result == null)             {                 context.Result = new NotFoundResult();             }             else             {                 context.HttpContext.Items.Add("result", result);             }         } } 


Применить этот фильтр можно так же, как и фильтр из предыдущего примера, с помощью ServiceFilterAttribute.

Фильтры действий раньше очень часто применяли, чтобы заблокировать контент для определённых браузеров на основе информации о User-Agent. На ранних этапах становления веб-разработки многие сайты создавались исключительно для наиболее популярных браузеров, остальные же считались запрещёнными. Сейчас данный подход является нежелательным, т.к. рекомендуется создавать такую HTML-разметку, которую смогло бы поддерживать большинство браузеров. Тем не менее, в некоторых случаях разработчику важно знать источник запроса. Ниже представлен пример получения User-Agent-информации в фильтре действий:

public class BrowserCheckFilter : IActionFilter {         public void OnActionExecuting(ActionExecutingContext context)         {             var userAgent = context.HttpContext.Request.Headers[HeaderNames.UserAgent].ToString().ToLower();             // Detect if a user uses IE             if (userAgent.Contains("msie") || userAgent.Contains("trident"))             {                 // Do some actions              }         }         public void OnActionExecuted(ActionExecutedContext context)         {         } } 


Стоит, однако, заметить, что вышеуказанный метод имеет ещё один недостаток. Многие браузеры умеют прятать или подделывать значения, указанные в User-Agent, поэтому данный способ не является однозначно достоверным в определении типа пользовательского браузера.

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

public class LocalizationActionFilterAttribute: ActionFilterAttribute {         public override void OnActionExecuting(ActionExecutingContext filterContext)         {             var language = (string)filterContext.RouteData.Values["language"] ?? "en";             var culture = (string)filterContext.RouteData.Values["culture"] ?? "GB";             Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo($"{language}-{culture}");             Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo($"{language}-{culture}");         } } 


Следующим шагом следует добавить маршрутизацию, которая будет перенаправлять URL с данными культуры на наш контроллер:

                endpoints.MapControllerRoute(name:"localizedRoute",                     pattern: "{language}-{culture}/{controller}/{action}/{id}",                     defaults: new                     {                         language = "en",                         culture = "GB",                         controller = "Date",                         action = "Index",                         id = "",                     });


Код выше создаёт маршрут с именем localizedRoute, у которого в шаблоне имеется параметр, отвечающий за локализацию. Значение по умолчанию для этого параметра en-GB.

Теперь создадим контроллер с именем DateController, который будет обрабатывать наш запрос, и представление, которое будет отображать локализованную дату. Код контроллера просто возвращает представлению текущую дату:

[LocalizationActionFilter] public class DateController : Controller {         public IActionResult Index()         {             ViewData["Date"] = DateTime.Now.ToShortDateString();             return View();         } }  


После того как пользователь перешёл по ссылке localhost:44338/Date, он увидит в браузере следующее:

image
На скриншоте выше текущая дата представлена с учётом локализации, заданной по умолчанию, т.е. с en-GB. Теперь, если пользователь перейдёт по ссылке, в которой будет явно указана культура, например, en-US, то он увидит следующее:


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

Заключение


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

5 плохих CSS практик

05.04.2021 02:07:14 | Автор: admin

Бытует мнение, что CSS сложный. Существует много костылей, магии. Легко выстрелить себе в колено. Меня это печалит, потому что я так не считаю. Немного подумав, что можно сделать, я собрал 5 привычек разработчиков, которые мне не нравятся, и показал, как их избежать.


Устанавливать отступы, а потом сбрасывать их


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


Используйте один из следующих вариантов: nth-child/nth-of-type селекторы, псевдо-класс :not(), или комбинатор следующего соседнего элемента, более известный как +, и ваш CSS будет более простым и кратким.


Не делайте так


.item {  margin-right: 1rem;}.item:last-child {  margin-right: 0;}

Вы можете использовать


.item:not(:last-child) {  margin-right: 1rem;}/*или*/.item:nth-child(n+2) {  margin-left: 1rem;}/*или*/.item + .item {  margin-left: 1rem;}

Добавлять display: block для элементов с position: absolute или position: fixed


А знаете ли вы, что вам не нужно добавлять display: block для элементов c position: absolute или position: fixed, потому что это происходит по умолчанию?


Кроме того, если вы будете использовать inline-* значения, то они будут изменены следующим образом: inline или inline-block изменятся на block, inline-flex -> flex, inline-grid -> grid, и inline-table -> table.


Поэтому просто пишите position: absolute или position: fixed, и добавляйте display, только тогда, когда вам нужны значения flex или grid.


Не делайте так


.button::before {  content: "";  display: block;  position: absolute;}/*или*/.button::before {  content: "";  display: block;  position: fixed;}

Вы можете использовать


.button::before {  content: "";  position: absolute;}/*или*/.button::before {  content: "";  position: fixed;}

Использовать transform: translate(-50%, -50%) для центрирования


Cуществует популярная задача, которая попила не мало крови. Особенно до 2015 года, когда все ее решения приводили к каким-то проблемам. Я говорю о центрировании элемента с произвольной высотой по двум осям.


В частности одним из решений было использовать комбинацию абсолютного позиционирования и свойства transform. Данный прием приводил к размытию текста в Chromium браузерах.


Но после появления flexbox этот прием, по моему мнению, не актуален. Мало того, что проблема размытия текста еще не решена, так еще нужно использовать 5 свойств. Поэтому я хочу поделиться приемом, который сократит код до 2 свойств.


Мы можем использовать margin: auto внутри flex-контейнера, и браузер расположит элемент по центру. Просто 2 свойства и все.


Не делайте так


.parent {  position: relative;}.child {  position: absolute;  top: 50%;  left: 50%;  transform: translate(-50%, -50%);}

Вы можете использовать


.parent {  display: flex;}.child {  margin: auto;}

Можно использовать justify-content и align-items для этой задачи, но я не люблю этот прием, потому что существуют проблемы, к которым он приводит. О них я расскажу в отдельной статье.


Использовать width: 100% для блочных элементов


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


И чтобы преобразовать сетку в одну колонку разработчики использую width: 100%. Я не понимаю, зачем они это делают. Ведь элементы сетки являются блочными элементами, которые делают это по умолчанию без дополнительных свойств.


Соответственно нам не нужно использовать width: 100%, а следует написать медиа-запрос так, чтобы flexbox использовался только для создания многоколоночной сетки.


Не делайте так


<div class="parent">  <div class="child">1</div>  <div class="child">2</div>  <div class="child">3</div>  <div class="child">4</div></div>

.parent {  display: flex;  flex-wrap: wrap;}.child {  width: 100%;}@media (min-width: 1024px) {  .child {    width: 25%;  }}

Вы можете использовать


<div class="parent">  <div class="child">1</div>  <div class="child">2</div>  <div class="child">3</div>  <div class="child">4</div></div>

@media (min-width: 1024px) {  .parent {    display: flex;    flex-wrap: wrap;  }  .child {    width: 25%;  }}

Если вы знаете, почему нужно добавлять width:100% и мой способ не решает проблему, то напишите об этом в комментариях. Возможно, я что-то не учел. Спасибо


Задавать display: block для flex-элементов


При использовании flexbox важно помнить, что при создании flex-контейнера (добавляем display: flex), все дочерние элементы (flex-элементы) становятся blockified.


Это означает, что у элементов устанавливается свойство display, и у него могут быть только блочные значения. Соответственно, если установить inline или inline-block, то оно изменится на block, inline-flex -> flex, inline-grid -> grid и inline-table -> table.


Поэтому не добавляйте display: block к flex-элементам. Браузер сделает это сам.


Не делайте так


.parent {  display: flex;}.child {  display: block;}

Вы можете использовать


.parent {  display: flex;}

Заключение


Надеюсь мне удалось, показать, как можно избежать простых ошибок, и вы воспользуйтесь моими советами. Если нет, то это будет на вашей совести!


P.S: Если у вас есть вопросы по CSS/HTML, то, не стесняйтесь, пишите мне на мою почту. Она указана в моем профиле на Хабре.

Подробнее..
Категории: Css , Frontend , Tips and tricks , Beginners

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru