Почему раскладка основного контента меняется при открытии модального окна?

Вопрос или проблема

Посмотрите на этот код React для создания модального окна:

import * as React from "react";
import "./styles.css";

function Modal({ handleClose }) {
  return (
    <div onClick={handleClose}>
      <dialog>
        <header>
          <button onClick={handleClose}>X</button>
          <h2>Модальное окно</h2>
        </header>
        <section>
          <p>
            Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
            tempor incididunt ut labore et dolore magna aliqua. Ullamcorper morbi
            tincidunt ornare massa. Morbi enim nunc faucibus a pellentesque sit
            amet. Duis tristique sollicitudin nibh sit amet commodo nulla facilisi
            nullam. Eget dolor morbi non arcu risus. Integer eget aliquet nibh
            praesent tristique magna sit amet purus. Nullam vehicula ipsum a arcu.
            Vitae proin sagittis nisl rhoncus mattis rhoncus. Risus feugiat in ante
            metus dictum at tempor.
          </p>
        </section>
      </dialog>
    </div>
  );
}

export default function App() {
  const [isOpen, setIsOpen] = React.useState(false);
  return (
    <div className="container">
      {/* Модальное окно */}
      {isOpen && <Modal handleClose={() => setIsOpen(false)} />}

      {/* Основное содержимое */}
      <main>
        <header>
          <h1>Тестовое модальное окно</h1>
        </header>
        {["red", "blue", "green", "pink", "purple", "yellow"].map(
          (color, index) => {
            return (
              <section
                key={index}
                style={{
                  backgroundColor: color,
                  width: "100%",
                  height: "50vh",
                }}
              />
            );
          }
        )}
        <button className="primary" onClick={() => setIsOpen(true)}>
          открыть модальное окно
        </button>
      </main>
    </div>
  );
}

styles.css

html {
  background-color: black;
  color: white;
}

.container {
  margin: 0 auto;
  max-width: 1100px;
  padding: 50px;
  position: relative;
}

main {
  margin: auto;
  padding: 24px;
}

main > header {
  position: sticky;
  top: 0;
  background: black;
  text-align: center;
  padding: 24px;
}

dialog {
  position: fixed;
  display: grid;
  grid-template-rows: auto 1fr;
  top: 40px;
  background: transparent;
  width: 75vw;
  margin: auto;
  animation: expand 0.3s cubic-bezier(0.075, 0.82, 0.165, 1);
  border-radius: 8px;
  padding: 32px;
  z-index: 10;
  color: white;
}

dialog section {
  max-height: calc(90vh - 120px);
  overflow: auto;
}

dialog button {
  position: absolute;
  top: 16px;
  right: 16px;
  width: 32px;
  height: 32px;
  border-radius: 50%;
}

div:has(dialog) {
  background-color: rgba(0, 0, 0, 0.5);
  position: fixed;
  /* width: 100%; */
  /* height: 100%; */
  z-index: 9;

  display: flex;
  align-items: center;
  justify-content: center;
  inset: 0;
}

.primary {
  position: fixed;
  bottom: 40px;
  right: 40px;
}

Вы можете запустить полный код здесь.

У меня есть несколько вопросов:

  1. Почему <main> сжимается, и содержимое возвращается вверх, когда я открываю модальное окно?
  2. Почему я не могу прокручивать содержимое в main, когда модальное окно открыто?

Я работал над этим всю ночь, чтобы найти решения.

Если я сделаю модальное окно проще, например так:

<div
  style={{
    backgroundColor: "rgba(0,0,0,0.5)",
    zIndex: 9999,
    display: "flex",
    justifyContent: "center",
    alignItems: "center",
    inset: 0,
    position: "fixed",
  }}
  onClick={handleClose}
>
  <div style={{ backgroundColor: "white", padding: "30px" }}>
    <h2 style={{ color: "black" }}>некоторый заголовок...</h2>
    <p style={{ color: "black" }}>некоторое содержимое</p>
  </div>
</div>;

Тогда всё в порядке.

Ошибка, похоже, связана с CSS.

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

Ответ или решение

При открытии модального окна в вашем React-коде происходит ряд изменений, которые влияют на макет основного контента. Давайте подробно разберем ваши вопросы.

1. Почему элемент <main> сжимается и его содержимое возвращается к верхней части при открытии модального окна?

Это поведение связано с тем, как CSS обрабатывает элементы с фиксированными позициями и модальные окна. В вашем коде модальное окно реализовано с помощью тега <dialog>, который по умолчанию имеет фиксированное позиционирование и отображается в виде слоя над остальным контентом. Однако в вашем CSS для родителя <div> вы видите следующие стили:

div:has(dialog) {
  background-color: rgba(0, 0, 0, 0.5);
  position: fixed;
  z-index: 9;

  display: flex;
  align-items: center;
  justify-content: center;
  inset: 0;
}

Эти стили заставляют родительский элемент становиться фиксированным, занимая все пространство на экране и перекрывая <main>. Это приводит к тому, что основной контент перезаписывается и, таким образом, кажется, что он сжимается. Вы также устанавливаете для <main> фиксированные стили, которые могут вызывать конфликты при отображении с модальным окном.

2. Почему я не могу прокручивать содержимое в <main> при открытии модального окна?

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

Решение

Чтобы исправить эти проблемы и позволить вашему модальному окну работать корректно без сжатия основного контента и бесполезной блокировки прокрутки, вы можете:

  • Убедиться, что ваше модальное окно правильно перекрывает основной контент, но не влияет на его расположение.
  • Добавить обработчик события для блокировки прокрутки основного контента при открытии модального окна.

Пример исправленного кода

  1. Для открытия модального окна добавьте следующий код, чтобы предотвратить прокрутку:
const App = () => {
  const [isOpen, setIsOpen] = React.useState(false);

  const handleOpen = () => {
    setIsOpen(true);
    document.body.style.overflow = 'hidden';  // Отключение прокрутки
  };

  const handleClose = () => {
    setIsOpen(false);
    document.body.style.overflow = 'auto';  // Включение прокрутки
  };

  return (
    <div className="container">
      {isOpen && <Modal handleClose={handleClose} />}
      <main>
        <header>
          <h1>Test-Modal</h1>
        </header>
        {/* Ваш контент */}
        <button className="primary" onClick={handleOpen}>openModal</button>
      </main>
    </div>
  );
};
  1. Убедитесь, что ваши стили не влияют на остальные элементы при открытии окна. Стили для модального окна должны иметь достаточно высокий z-index, чтобы покрыть все остальные элементы:
dialog {
  z-index: 100;
}

Заключение

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

Оцените материал
Добавить комментарий

Капча загружается...