Модальные окна на CSS

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



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

HTML-код попапа обычно имеет такую структуру:

<div class="popup__overlay"></div> <!-- Оверлей --><br/><div class="popup">...</div> <!-- Попап вместе с содержимым --> <br/>

И CSS (здесь и ниже я умышленно буду опускать написание некоторых свойств, необходимых лишь для некоторых браузеров и их версий, оставив лишь самое основное):

.popup__overlay {<br/>    position: fixed;<br/>    left:  0;<br/>    top:  0;<br/>    background: #000;<br/>    opacity: .5;<br/>    filter: alpha(opacity=50);<br/>    z-index: 999<br/>    }<br/>.popup {<br/>    position: absolute;<br/>    width: 20%;<br/>    z-index: 1000;<br/>    border: 1px solid #ccc;<br/>    background: #fff<br/>    } <br/>

JS определяет браузер и версию браузера, и на основании этого высчитывает размеры рабочей области и размеры самого попапа (если они не заданы), а затем производятся нехитрые вычисления положения его левого верхнего угла (css-свойства left и top для .popup). Многие плагины также реагируют на изменение размеров страницы, пересчитывая всё это дело каждый раз, с тем, чтобы попап располагался точно в центре рабочей области.

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

Этим и займёмся.

Ниже я приведу пример попапа, работающего во всех мажорных версиях основных браузеров. Для корректной его работы в IE<9 необходима некоторая экстра-разметка и хаки, потому детальное описание написания такого метода я опущу, а для интересующихся выложу полную версию.

Итак, у нас есть страница с кнопкой, при нажатии на которую должно всплывать модальное окно с некоторой информацией, а весь остальной контент должен затеняться оверлеем.

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

<div class="popup__overlay"><br/>    <div class="popup"></div><br/></div> <br/>

И немного CSS:

.popup__overlay {<br/>    position: fixed;<br/>    left:  0;<br/>    top:  0;<br/>    width: 100%;<br/>    height: 100%;<br/>    z-index: 999<br/>    }<br/>.popup {<br/>    } <br/>

Фиксированные размеры

Самый простой вариант. Ничего нового изобретать не нужно:

.popup {<br/>    left: 50%;<br/>    top: 50%;<br/>    width: 400px;<br/>    height: 200px;<br/>    margin-left: -200px;<br/>    margin-top: -100px<br/>    } <br/>

Отрицательные margin'ы в половину ширины и высоты — банально и скучно, ничего оригинального в этом нет. Идём дальше.

Размеры попапа зависят от содержимого

Сперва — выравнивание по горизонтали — это вроде бы проще. Если попап фиксированной ширины — то достаточно будет следующего:

.popup {<br/>    margin: auto<br/>    } <br/>

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

Вертикальное выравнивание. Здесь уже становится интересно. С такой задачей, конечно, без проблем справилась бы таблица или эмуляция таблицы с помощью display: table & display: table-cell, но заставить такое работать в старых IE — себе дороже. Таблица также отпадает — по понятным причинам.

Итак, margin уже отпадает — размеров мы не знаем. Вспоминаем, что же есть из свойств с подобными эффектами. Ага, text-align. Но только для инлайновых элементов. ОК. Кажется, сам Бог велел использовать display: inline-block — блочный элемент, к которому можно было бы применить свойства для инлайновых элементов. С поддержкой этого свойства у всех браузеров тоже всё, можно сказать, в порядке. Код становится примерно таким:

.popup__overlay {<br/>    position: fixed;<br/>    left:  0;<br/>    top:  0;<br/>    width: 100%;<br/>    height: 100%;<br/>    z-index: 999;<br/>    text-align: center<br/>    }<br/>.popup {<br/>    display: inline-block;<br/>    vertical-align: middle<br/>    } <br/>

Остаётся вертикальное выравнивание — нам подойдёт vertical-align. В любой другой ситуации было бы также уместно использовать line-height, но поскольку у нас нет фиксированной высоты страницы (line-height в данном контексте), здесь использовать её нельзя. На помощь приходит один трюк с вертикальным выравниванием элементов неизвестных размеров. Я точно помню, что нашел этот способ на Хабре, но, к сожалению, не смог найти ссылку на тот топик. Заключается этот способ в следующем: добавляется inline-block элемент нулевой ширины и 100%-ой высоты родителя, который «расхлопывает» высоту строки до 100% высоты родителя, то есть до высоты рабочей области страницы. Сделаем это изящнее — вместо лишней разметки воспользуемся псевдоэлементами:

.popup__overlay:after {<br/>    display: inline-block;<br/>    width:  0;<br/>    height: 100%;<br/>    vertical-align: middle;<br/>    content: ''<br/>    } <br/>

Осталось добавить полупрозрачное затемнение оверлея — с этим справится rgba. Всё! Теперь положение попапа регулируется только средствами браузера на уровне CSS.

Итоговый пример

Из замеченных минусов метода — порой возникают глюки с отображением в IE 6-7, в частности, при использовании флоатов.

Повторюсь — мой метод не избавляет от использования JS вообще, он лишь частично оптимизирует скорость работы всплывающих окон за счет переноса части нагрузки с JS на CSS.

Уверен, что метод можно улучшить — как визуально, так и изнутри, и буду рад любым идеям и предложениям по этому поводу, а также замечаниям по работе и отображению в различных браузерах.

UPD: По просьбам, добавил пример, работающий в IE7+ — http://jsfiddle.net/vend3tta/SE4tS/
Поделиться публикацией
Похожие публикации
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама
Комментарии 40
  • +6
    Чего только стоит избавиться от «гемороя» расчета координат для центровки. Здорово!
    • +1
      Самое интересное не разъяснили:
      С такой задачей, конечно, без проблем справилась бы таблица или эмуляция таблицы с помощью display: table & display: table-cell, но заставить такое работать в старых IE — себе дороже. Таблица также отпадает — по понятным причинам.

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

      Далее. В каком доктайпе это работает? На странице JSFiddle стоит xhtml1-strict, хотя по настройкам слева — HTML5, а документ в фрейме. Означает ли это, что у документа в фрейме — тот же самый доктайп xhtml1-strict (он виден в Firebug, например)? А как вообще по-настоящему в HTML5 и кроссбраузерно — это везде работает? В котором проверяли?
      • +3
        Таблица — это не кошерно, что ли… Да и потом, завернуть всё в таблицу можно всегда, и это гарантированно будет работать — но ведь это скучно, гораздо интереснее заставить этот код работать во всех браузерах без использования таблиц, насколько это возможно.

        В примере на jsFiddle доктайп html5. А о вопросе проверки разных доктайпов, честно говоря, не задумывался — для меня этот вопрос отпал больше года назад, с тех пор использую только doctype html. Можно вовсе отказаться от использования псевдоэлементов, добавив вместо этого один лишний элемент в разметку.
        • –3
          Это сайт так пишет, что HTML5, а Firebug определяет xhtml1-strict (v.1.90, Fx8.0 Lin) и исходный код страницы подтверждает. Посмотрите по Ctrl-U — «xhtml1-strict.dtd». В Опере то же самое. (Это к тому, что надо же знать, что мы верстаем. Я делал такое окно на таблице и тоже в xhtml1-strict.dtd. Особенно большие затраты занимает поддержка IE6(с деградацией)-7 с тенями и бордюрами. За другие способы не брался, потому что IE7 критичен, а что он там выкинет без таблиц — трудно предполагать, к тому же, с эффектами теней.)

          Если будем брать HTML5 то будет иметь значение, какой фреймворк будем использовать для поддержки старых браузеров, а тут (у Вас) вроде нет никакого (и даже нового доктайпа нет). Попробуйте приложить фреймворк — где-то что-то поедет, и — работу начинать сначала. Результат, конечно, интересный, но по этой причине он ничего не доказывает — можно или нельзя делать попапы без таблиц во всех (хотя бы от IE7) браузерах.
          • НЛО прилетело и опубликовало эту надпись здесь
            • 0
              Спасибо, всегда удивляюсь Вашим глубоким знаниям :). (Без шуток.) Суть-то тут как раз в старых браузерах. Для IE9+Fx4+Chrome от где-то 10-12-го?, Оперы 11, Safari5 — всё это делается вообще очень легко на одном дыхании :). А вот необходимость второй вёрстки для старых браузеров вместо одной общей заставит отказаться от новаторства и делать по традиции :). (Правда, для меня это на настоящий момент осталось на уровне хобби, ну и хорошо, пусть они отмирают пока.)
              • НЛО прилетело и опубликовало эту надпись здесь
                • 0
                  DOCTYPE html везде рендерится одинаково и предсказуемо. Потому, что по-большому счёту браузерам плевать на доктайп. Есть два режиме: quirks и standards. Только IE9 при старых доктайпах отрубает всякие фишки в CSS/JS, всем остальным — плевать. Вы можете юзать XHTML Strict и понавставлять фреймов тонну — и всё будет как и задумано.

                  Ставьте всегда DOCTYPE html и не мучайтесь.
                  • НЛО прилетело и опубликовало эту надпись здесь
                    • 0
                      Almost же только в Firefox, не?
                      • НЛО прилетело и опубликовало эту надпись здесь
          • НЛО прилетело и опубликовало эту надпись здесь
            • +1
              Да, посмотрел, во фрейме, действительно <!DOCTYPE html>, спасибо. Тогда остаются остальные замечания выше, по поводу отсутствия СSS-фреймворка под HTML5. Т.е. интересно, но не очень практично; нельзя сделать однозначные выводы, будет ли работать при полноценном подходе где-то на сайте.
              • НЛО прилетело и опубликовало эту надпись здесь
            • НЛО прилетело и опубликовало эту надпись здесь
            • –3
              position: fixed?
              display: inline-block?
              content: ''?
              :after?

              Я уже боюсь открыть это в IE6 :)
              • +7
                Say goodbie IE6 :) www.ie6countdown.com
              • 0
                Под IE 6 данным метод, пожалуй, не рассчитан. Точнее, заставить его работать в IE6 можно, только овчинка выделки не стоит.

                В остальном — inline-block в IE 6-7 заставить работать можно, а использование псевдоэлемента :after — лишь в угоду лаконичности кода. Conditional comments и еще один лишний элемент — вот и вся хитрость.

                Пожалуй, мне стоит добавить ссылку на пример, работающий в IE 7+. Сейчас сделаю.
                • 0
                  Добавил пример для IE7+ в конце статьи.
              • –1
                Просто inline-block будет работать не во всех браузерах.
                Могу предложить так:

                display:-moz-inline-stack;
                display:inline-block;
                _overflow:hidden;
                *zoom:1;
                *display:inline;

                Не помню где вычитал эту кучу хаков, но работает даже в ИЕ-6 ))
                • +3
                  -moz-inline-stack это уже прошлый век, попробуй найди такого динозавра, который сидел бы на FF2. В остальном — да, можно только без overflow обойтись. Да, и zoom после *display: inline — сначала сделать инлайн, затем включить hasLayout.
                  • 0
                    Старый способ, не спорю.
                    overflow нужен для ИЕ6, без него помнится не работало )) Хотя уже давно под него ничего не верстал, не помню если честно.
                    • 0
                      Не, для ие6 overflow не нужен. Хватит вот этого:
                      .cls {
                          display: inline-block;
                          /display: inline;
                          /zoom: 1;
                      }
                • +1
                  Во-первых этот способ не абсолютно универсален. Добавляем очень много текста и все поплыло.
                  Для того, чтобы не поплыло, надо указать max-width попапа < width ширины окна или родителя.
                  • 0
                    Спасибо за замечание, действительно есть такое, как-то упустил этот момент из виду. Обновлю примеры, с учётом вашего замечания.
                  • +3
                    Кстате, если добавить к открытию модального окна:
                    $('body').css('overflow','hidden');
                    а в закрытие соответственно overflow:auto, то избавимся от возможности скролить страницу, что очень важно для модальных окон. Удивлялся, почему про это забывают многие. Или здесь есть подводные камни?
                    • +1
                      Только если окно гарантированно маленькое. Не слишком приятно обнаружить, что кнопка отправки формы под сгибом.
                      • НЛО прилетело и опубликовало эту надпись здесь
                    • 0
                      Для закрывающей кнопки лучше использовать & times;
                      • 0
                        Высоту и ширину в данном случае .popup мы знаем.
                        Не проще сделать так:

                        .popup {
                        position:absolute;
                        width:200px;
                        height:160px;
                        margin-left:-80px;
                        margin-top:-100px;
                        left:50%;
                        top:50%;
                        }
                        • 0
                          margin-left:-100px;*
                          margin-top:-80px;*
                          Фиксированно по центру получается.
                          • 0
                            Да то же самое получается :)
                        • +5
                          А теперь задача следующая:
                          Убираем лишний элемент для оверлея и делаем оверлей псевдоэлементом самого попапа (:before). Центрировать попап можно и через:
                          .popup {
                          position: fixed;
                          top: 50%;
                          left: 50%;
                          -webkit-transform: translate(-50%, -50%);
                          -moz-transform: translate(-50%, -50%);
                          -ms-transform: translate(-50%, -50%);
                          -o-transform: translate(-50%, -50%);
                          transform: translate(-50%, -50%);
                          }


                          Для IE < 9 можно высоту считать либо через expression либо джаваскрипт (на выбор). Также для IE < 8 нужен простенький фолбек скрипт для заворачивания в <div class="_ie_popup_overlay"></div> и стили соответствующие.
                          • НЛО прилетело и опубликовало эту надпись здесь
                          • НЛО прилетело и опубликовало эту надпись здесь
                            • +2
                              Вот интересно, каких только фич не добавили в CSS3, когда же сделают нормальное выравнивание. А то для такой простой задачи как вывод блока по центру экрана, приходится кучу лишнего css писать…
                              Почему нельзя сделать, что-то типа
                              align: center middle;
                              • +2
                                Кстати, советую посмотреть на мой вариант попапов только на CSS: kizu.ru/fun/popups/#SomePopup2 — правда, я там акцентировал внимание на том, чтобы показывать/скрывать попап используя только CSS, и на анимации появления/скрытия через транзишны, т.е. над центрированием по вертикали не заморачивался.

                                Но, может, заморочусь как-нибудь — иногда это бывает надо, да.
                                • 0
                                  Для центрированием по вертикали использую похожий метод: dabblet.com/gist/1629941 (нашел на CSS-Tricks)
                                  Только вот он работает, в отличие от того Fiddle, ссылку на который автор дал в конце статьи.
                                  • 0
                                    Прошу прощения, а есть ли CSS-растягивание на весь экран не только по горизонтали, но и по вертикали.
                                    Я имею ввиду вот что. Вот имеется к примеру 9 блоков:

                                    1 2 3
                                    4 5 6
                                    7 8 9

                                    Их может быть и 4 на 4 и хоть 10 на 5, тут не важно. Тут важно, чтобы например блоки 1 3 7 9 растекались по углам. Блок 2 сверху, блок 8 снизу, соответственно 4 слева и 6 справа, а 5 в центре.

                                    Т.е. через CSS оно бы раскрывалось на весь экран и не важно какого он разрешения.

                                    Ну или хотя бы просто 3 блока (123) (456) (789), чтобы 123 всегда был прижат к верху (ну это и так понятно), а вот чтобы 789 всегда был прижат к низу, а в центре был 456.

                                    Хотя с девятью блоками тоже интересно.

                                    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.