Чистий ефект фокусування галереї CSS із :not

Вихідний вузол: 1723238

Часто в минулому мені потрібно було з’ясувати, як додати стилі до всіх елементів усередині контейнера, але НЕ завислий.

Демонстрація очікуваного ефекту «згасання» для братів і сестер, щоб дозволити користувачам «зосередитися» на певному елементі.

Цей ефект потребує вибору братів і сестер елемента, що наводиться. Для цього я використовував JavaScript, додаючи або видаляючи клас, який визначав належні правила CSS mouseenter та mouseleave події, подібні до цього:

Хоча код справляє свою роботу, моє чуття завжди підказувало мені, що має бути якийсь чистий CSS спосіб досягти того самого результату. Кілька років тому, працюючи над певним слайдером для своєї компанії, я придумав рішення, схоже на як Кріс Гілгоуд відтворив відому анімацію домашньої сторінки Netflix і я зрозумів, що мені для цього більше не потрібен JavaScript.

Пару місяців тому я намагався застосувати той самий підхід до стрічки на основі сітки на веб-сайті моєї компанії, і — бум — це не спрацювало через проміжок між елементами!

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

Розмітка та базовий CSS

Давайте почнемо кодування з підготовки відповідної розмітки:

  • .grid базується на сітці

      Список;

    • та .grid__child елементи є
    • дітей, з якими ми хочемо спілкуватися.

    Розмітка виглядає так:

    Стиль повинен виглядати так:

    .grid {
      display: grid;
      grid-template-columns: repeat(auto-fit, 15rem);
      grid-gap: 1rem;
    }
    
    .grid__child {
      background: rgba(0, 0, 0, .1);
      border-radius: .5rem;
      aspect-ratio: 1/1;
    }

    Цей приклад коду створить три елементи списку, які займатимуть три стовпці в сітці.

    Потужність селекторів CSS

    Тепер давайте додамо трохи інтерактивності. Підхід, який я спочатку застосував, базувався на двох кроках:

    1. наведення курсора на контейнер має змінити стилі всіх елементів усередині…  
    2. …окрім того, на якому курсор знаходиться в даний момент.

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

    .grid:hover .grid__child {
      /* ... */
    }

    По-друге, давайте виключимо поточний наведений елемент і зменшимо opacity будь-якої іншої дитини:

    .grid:hover .grid__child:not(:hover) {
      opacity: 0.3;
    }

    І цього буде цілком достатньо для контейнерів без проміжків між дочірніми елементами:

    Анімований GIF курсора миші, який взаємодіє з елементами, які не розділені проміжками.
    Демо рішення, яке працює без розривів.

    Однак у моєму випадку я не міг усунути ці прогалини:

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

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

    Ігнорування прогалин

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

    Так, ми можемо, використовуючи pointer-events: none на .grid контейнер і повертати їх назад pointer-events: auto на своїх дітей:

    .grid {
      /* ... */
      pointer-events: none;
    }
    
    /* ... */
    
    .grid__child {
      /* ... */
      pointer-events: auto;
    }

    Давайте просто додамо трохи крутого переходу на прозорість, і ми маємо готовий компонент:

    Мабуть, ще крутіше, коли ми додаємо більше плиток і створюємо двовимірний макет:

    Остаточний CSS виглядає так:

    .grid {
      display: grid;
      grid-template-columns: repeat(auto-fit, 15rem);
      grid-gap: 3rem;
      pointer-events: none;
    }
    
    .grid:hover .grid__child:not(:hover) {
      opacity: 0.3;
    }
    
    .grid__child {
      background: rgba(0, 0, 0, .1);
      border-radius: .5rem;
      aspect-ratio: 1/1;
      pointer-events: auto;
      transition: opacity 300ms;
    }

    За допомогою лише 2 додаткових рядків коду ми подолали проблему розриву!

    Можливі проблеми

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

    На жаль, цей трюк не спрацює, якщо ви хочете, щоб контейнер можна було прокручувати, наприклад, у якомусь горизонтальному повзунку. The pointer-events: none style ігноруватиме не лише подію наведення, але й усі інші. У таких ситуаціях можна загорнути .grid в іншому контейнері, ось так:

    Підсумки

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

    Сподіваюся, вам сподобався цей короткий посібник і він був корисним. Дякую!

    Автор вибрав Tech Освіта отримати пожертву в рамках Пишіть для пожертвувань програми.

    Часова мітка:

    Більше від CSS-хитрощі