The Modal — правильные модальные окна

    Очень часто модальные окна и диалоги делаются при помощи плагинов jQuery. Например, SimpleModal или jqModal. К сожалению, все они, в варианте по умолчанию, работают неправильно.

    Что же такое «правильно»?

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

    1. Пользователю нельзя позволять прокручивать страницу под ним.
    2. При этом, если содержимого в модальном окне очень много, нужно позволить прокручивать содержимое.


    По этому принципу работает просмотр фото в Facebook и Вконтакте и, я считаю, что для модальных окон это правильный вариант.

    Чтобы не мучать вас заранее деталями реализации, покажу сначала демо плагина jQuery: http://rmcreative.ru/playground/modals_plugin/demo.html.

    Ну а теперь немного про реализацию.

    Идея достаточно проста. В открытом состоянии разметка получается примерно вот такая:

    <html>
      <head>
        ...    
      </head>
      <body class="lock">
        <div class="shim">
          <div class="modal">
            ...
            <h1>Hi, I'm the modal demo.</h1>
            ...
          </div>
        </div>
        ...
      </body>
    </html>
    


    К ней применяется CSS:

    /* Данный класс навешивается на контейнер при открытии модального окна. Для нормальных браузеров это body, для стырах IE — html */
    .lock {
        /* убираем скроллбары с основнового содержимого страницы */
        overflow: hidden;
    }
    
    /* ширма (полупрозрачное затенение под модальным окном) */
    .shim {
        /* фиксируем, растягиваем на весь доступный экран */
        position: fixed;
        bottom: 0; left: 0; top: 0; right: 0;
    
        /* если в модальном окне много содержимого, показываем скроллбар */
        overflow: auto;
    
        /* однопальцевый скролл для iPad*/
        -webkit-overflow-scrolling: touch;
    }
    
    /* фикс для странностей в iPad */
    .shim > * {
        -webkit-transform: translateZ(0px);
    }
    
    /* декоративная часть ширмы: делаем полупрозрачной и чёрной */
    .shim {
        background: rgba(0,0,0,0.5);
    
        filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#7F000000,endColorstr=#7F000000); /* IE6–IE8 */
        zoom: 1;
    }
    


    Вот и всё. Остаётся завернуть в удобный для использования плагин jQuery и добавить плюшки вроде закрытия по ESC. У меня получился плагин вот с таким API:

    $.modal().open({
        onOpen: function(el, options){
            $.get('http://example.com/', function(data){
                el.html(data);
            });
        }
    });
    


    Забрать его можно на github.

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

    • Скролл клавиатурой в FF скроллит страницу вместо модального окна.
    • На iPad скролл работает не очень стабильно (иногда скроллит страницу).
    • Скролл нажатием средней кнопки мыши работает неверно.
    • Открытие модального окна сдвигает страницу в том случае, если появляется или убирается скролл.


    Конструктивная критика и советы очень приветствуются.
    Support the author
    Share post
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 81

      0
      На android основная страница прокручивается всегда… когда малое модальное окно и когда большое.
      К тому же глюки с большим окошком.
        0
        К сожалению, пока не на чем погонять.
          0
          Android 4.1 — Chrome:
          + Модальное окно открывается так же как в Chrome декстопе
          + Прокрутка окна основной страницы останавливается
          + Прокрутка Модального окна работает

          — Закрыть модальное окно нажатием на крестик невозможно :( Пришлось перезагружать страницу
          0
          iOS 5 — аналогично
            0
            Вот это уже странно — я тестировал на iPad2 и там прокручивалось почти как надо.
              +1
              iPad 2 16Gb Wifi+3G, iOS 5. Прокручивается, чесслово.
                0
                «почти как надо » это все равно как «чуть-чуть беременная»
                  0
                  Знаем знаем. Отладить мод мобильные девайсы нормально ещё предстоит.
            +3
            «Пользователю нельзя позволять прокручивать страницу под ним.»
            как пользователь я против, часто нужно.
              0
              Для чего?
                +4
                Юзкейс: у меня таблица с списками которые разворачиваются. Редактирование «общей» информации происходит в модальном окне. Я хочу отредактировать первый элемент, и забить его значениями из 10 элемента, а без прокрутки туда не доберешься, потому что данных много. Модальное не прокручивается. Печаль.
                  +4
                  Значит, вам нужно не модальное окно.
                    +4
                    Вы приводите пример не модального окна.
                      –3
                      Это пример реальной реализации с которой я сталкивался. Вопрос правильности — дело другое. Скорее это просто «поп-ап» или «поп-овер» реализуют, на основе модального окна. Конечно, само по себе модальное окно уже говорит о том, что контект блокируется до окончании работы с модальным окном.

                      Так что не совсем понятно, зачем обсуждать то, что и так по умолчанию должно быть.
                    +1
                    скопировать/подсмотреть нечто для заполнения формы в модальном окне, или посмотреть про что там вообще было, не закрывая его.
                      0
                      Это не модальное окно.
                  –14
                  Если уж вы пишите так:
                  /* ширма (полупрозрачное затенение под модальным окном) */
                  .shim

                  то логичнее писать вместо
                  .lock {

                  так:
                  .zaperet {
                  0
                  Клик в серую область вокруг окна должен его закрывать.
                  Появление и сокрытие скролл-бара браузера приводит к подёргиванию контента страницы влево-вправо.
                    0
                    Про подёргивание я указал (последний пункт). С закрытием по клику согласен. Хорошо-бы прикрутить.
                      0
                      Я считаю, что нужно сделать этот параметр настраиваемым.
                      И ещё вопрос: есть функуция onOpen, а есть ли аналогичная onClose? было бы неплохо иметь такую, и чтобы если она возвращала false, окно не закрывалось
                        +1
                        onClose есть. Посмотрите документацию на github. Про возврат false неплохая идея.
                      0
                      на счет скроллбара: можно изначально присвоить body, .shim { overflow-y: scroll; } тогда скроллбар будет всегда виден и скачка не будет
                        0
                        Скроллбара тогда будет два.
                          0
                          а как же

                          .lock {
                              /* убираем скроллбары с основнового содержимого страницы */
                              overflow: hidden;
                          }
                            0
                            Ну так если что-то убрать или показать, содержимое прыгнет.
                      0
                      Было бы неплохо вместе с overflow для основной страницы устанавливать правый паддинг равный ширине скролла если он есть — иначе страница «прыгает».
                        0
                        Сделаем.
                        0
                        Есть ещё куда допиливать. Например, располагать модальное окно по центру окна, закрывать его не только по крестику и Esc, но и при потере фокуса на окне.

                        А ту же возможность/невозможность прокрутки содержания страницы при открытом модальном окне можно просто вынести в настроечный параметр. Надо прокручивать — прописал «lockScroll: false» и радуешься своим величием.
                          0
                          Располагать и оформлять можно как угодно, см. github.com/samdark/the-modal/blob/master/demo-modals.css

                          Про потерю форкуса уже рассказали. Сделаем.

                          Про опциональность прокрутки не согласен. Если прокрутка возможно — это неправильное модальное окно или не модальное окно вообще.
                            0
                            Опциональность — это хорошо. Тогда можно будет одну и ту же библиотеку использовать как для создания модальных, так и диалоговых окон.
                            Но можно тогда назвать опцию не lockScroll, а modal = true | false, правда придется еще и плагин переименовать ;)
                          +1
                          Правильное модальное окно — это отсутствующее модальное окно. Почему я должен прекращать пользоваться сайтом, если совершаю какую-то операцию? Весь сайт при этом вдруг переходит в состояние, когда им нельзя пользоваться? Нет же.

                          Модальные окна — зло. Их в десктопе должно быть куда меньше, чем есть сейчас, а уж в вебе — и подавно.
                            0
                            Взять тот же Вконтакт или Facebook. Как бы вы организовали просмотр фотографий без модального окна?
                              0
                              Почему окно должно быть модальным? Зачем закрывать и делать недоступным то, что позади окна?
                                0
                                Вы видели Вконтактовский просмотрщик?

                                Изображение может быть очень длинным по вертикали (те же комиксы и манга). Кроме самого изображения в окне расположена информация об авторе и неограниченное количество комментариев, что делает окно гарантированно длиннее viewport-а.

                                Такое окно закрывает основное содержимое страницы. От того, что какие-то обрезки будут торчать из под него будет только плохо: это отвлекает, можно промахнуться и нажать торчащую из под окна ссылку. К тому же, непонятно, как прокручивать такое немодальное окно.
                                  0
                                  Видел этот просмотрщик. И не понимаю, на черта он нужен, такой навороченный. Лично мне браузерную вкладку открыть-закрыть было бы удобнее. Сначала браузерам не хватило управления окнами на уровне оси, наизобретали 100500 вариантов MDI, в том числе вкладки с навороченными оконнными менеджерами. Теперь ещё каждый сайт норовит изобрести свою внутреннюю систему трижды вложенных вкладок, попапов в четыре слоя и прочей нечисти.

                                  Нет, может, в каких-то случаях это и удобно, но у меня в браузере все клавиатурные сочетания и жесты удобно настроены, мне все эти пятиуровневые оконные менеджеры нафиг не нужны. С менеджерами десктопов вообще весело получается: игла в яйце, яйцо в утке, утка в зайце, заяц в шоке.
                                    0
                                    Это очень субъективный взгляд. Привыкшие к Facebook и Вконтакте юзеры, скорее всего, с вами не согласятся.
                                      0
                                      Справедливости ради, нужно сказать, что Джеф Раскин в своей книжке про интерфейсы писал о том же. Модальные окна — зло, потому что они вынуждают пользователя переключиться от одного способа взаимодействия с системой на другой. А каждое такое переключение требует мысленных усилий. Субъективного тут как раз мало, законы восприятия более-менее одинаковые для всех. Опыт работы с системой, конечно, снижает когнитивную нагрузку, но сам интерфейс от этого лучше не становится.

                                      Другое дело, что совсем без модальных окон тоже нельзя обойтись. Поэтому с ними, как и со всем прочим, нужно соблюдать меру и не плодить сущности сверх необходимости.
                                        0
                                        Злоупотреблять, в общем, плохо. О чём бы не была речь.
                                    0
                                    Видели — там не модальное окно по логике — адрес страницы-то меняется.
                                0
                                Модальные окна для того и нужны, чтобы не прекращать пользоваться сайтом. Т.е. вы не переходите на новую страницу для какого-то незначительного действия, а лишь сфокусировались на нём.
                                Это как сидеть за столом, где всё под рукой: достали, что нужно из ящика стола, а не пошли в соседнюю комнату за тем же самым, теряя кДж энергии.
                                  +1
                                  Ну почитайте вики: ru.wikipedia.org/wiki/Модальное_окно :). Просто не всегда нужно использовать именно модальное окно ;).
                                    +1
                                    просто не всегда нужно использовать вики )
                                0
                                А почему бы просто не убирать весь текущий контент страницы в div с overflow:hidden, выставляя этому диву текущий скроллинг? По идее это должно решить проблему со скроллингом контента под окном на всех браузерах, включая мобильные.
                                  +1
                                  Слишком дорогая операция. Если DOM содержит большое количество элементов или они просто используют много эффектов, то появление модального окна будет сопряжено с нежелательными эффектами да и скорость появления будет страдать
                                    0
                                    Ну большой DOM зачастую уже сам по себе косяк, впрочем, point taken
                                  +2
                                  Я тоже когда то разрабатывал такие модальные окна для своего сайта.

                                  Тогда я понял, что универсальное решение для любого сайта да ещё и с возможностью настроек будет очень громоздким как в js так и в css.
                                    0
                                    Может у вас есть решения для озвученных болячек?
                                      0
                                      К сожалению, нет.

                                      Могу помочь советами:

                                      1) Я отказался от overflow hidden для body по какой-то причине (уже не помню). Посмотрите как сделали Вконтакте — просто двигают отрицательным margin-top внутренний контейнер.

                                      2) Тестируйте с разными DOCTYPE документа. от него зависит высота экрана и что-то может отвалиться

                                      3) Я вам ночью постараюсь прислать ссылку на рабочий пример модальных окон, который мне очень понравился. Как я сделал решение для своего сайта можете посмотреть на orgup.com/e244 (внизу ссылка «Обратная связь» + постер сделан по похожей схеме, но имеет особенность сжиматься на маленьких разрешениях экрана)

                                      4) Помните, что на разных операционных системах в разных браузерах разная ширина скролла (а в мобильном мире она равна нулю)

                                      5) Не забудьте про вызов модальных окон из модальных окон

                                      6) Помните, что вы молодец! Мне есть чему у вас поучиться, и слепо доверять моим советам не следует
                                        0
                                        1) Раньше так делал. Тоже не могу вспомнить, зачем именно.
                                        4) Это решаемо. Есть идеи.
                                        6) :)
                                      0
                                      Но есть один вид задач, который все же предпочтительнее делать модальными окнами — разворачивать большую картинку на весь экран. Другими словами, lightbox, slimbox всякие. Это очень хорошо вписывается в поведенческие паттерны человека.
                                      0
                                      Отличное решение. Только почему окно не закрывается по клику на бэкдроп (.shim)?
                                      И кстати кнопку "×" лучше вынести за край диалогового окна и зафиксировать у его левого края, тогда при прокрутке пользователю не придется возвращаться наверх только ради того чтобы закрыть окошко.
                                        0
                                        Будет закрываться и по клику на shim.

                                        Оформление можно сделать какое угодно. Это не основная задача.
                                          0
                                          Это не оформление, а UI, такие вещи лучше сделать из коробки, а не оставлять на реализацию «специалистов».
                                            0
                                            Можно будет заняться после починки болячек.
                                          0
                                          P.S. Что за дурацкая привычка, определять сторону от лица элемента )) У правого края.
                                          –2
                                          Забавно. В начале автор показывает неправильные примеры решения проблемы, а после предлагает свое решение… И тоже неправильное :).

                                          В общем я, как фанат Firefox'а, открыл вашу демку. Какие же косяки я нашел:

                                          1. При появлении модального окна пропадает скролл.
                                          2. При открытии «модального» окна, клике по серой области и нажатии пробела прокручивается страница.
                                          3. При открытии «модального» окна и нажатии клавиши tab фокус начинает летать по всем ссылкам на странице…
                                          4. При открытии «модального» и нажатии ctrl+a на странице выделяется весь текст, влючая текст под «модальным» окном.

                                          + не совсем косяки, но обычно:
                                          1. Отключают правую кнопку мыши.
                                          2. Выравнивают модальное окно по центру.
                                          3. Не делают фокусировку на самом модальном окне (вообще не понял сего :)).

                                          В общем это только один браузер, а косяков куча… А есть еще и Chrome, IE, Opera… Мобильные…

                                          Конечно же можно сказать, что я предираюсь / эти косяки несущественны / это невозможно испрпавить и т.д., и т.п. Но вполне можно сказать, что это очередной глючный велосипед, как и n-ое количество иных.

                                          А исправить вполне возможно (ибо сам делал модальные окна) посредствм CSS и без всяких там сложных js. Ибо есть -moz-user-* (искать здесь) и pointer-events (искать здесь).

                                          Учитесь писать правильные скрипты ;).
                                            0
                                            Косяки:

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

                                            Не совсем:

                                            1. Зачем?
                                            2. Это пока на совести того, кто задаёт стили модального окна.
                                            3. Забыл убрать из демки. В коде на github этого уже нет.

                                            Chrome, IE и Opera ведут себя куда лучше FF и мобильных. Для того я и выложил всё это дело на github, чтобы все зантересованные могли помочь довести плагин до стабильного состояния. Несколько светлых голов всегда лучше одной.

                                            Что именно из moz-user-* и pointer-events посоветуете использовать и для чего?
                                              0
                                              Не, 1-ое это косяк, ибо при убираении скролла вы изменяете размер элемента (скролл-то тоже место занимаете). В вашем сферическом примере это портит лишь картинку (исчез скролл… появился), но вы не можете знать, к чему может привести изменение размера в реальности (съехать верстка, аль что-то еще, зависящее от размера. В общем нельзя предугадать все возможные сценарии, но конкретно это можно исправить.

                                              И, да, в Firefox'е при появлении alert(1);'а можно использовать поиск по странице на полную мощь, но из-за убираения скролла ваш вариант не прокручивает страницу к искомому элементу.

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

                                              Про то, как использовать приведенные стили можно почитать и в сети. С pointer-events все и так понятно, он отключает события мыши на элементе. Для SVG там больше плюшек, ну а для HTML там всего 2, ловить события мыши и не ловить :).

                                              С остальными немного сложнее. Совсем немного. Мне не нравится IE, но мне нравятся демки его разработчиков. Вот, посмотрите вот эту: http://ie.microsoft.com/testdrive/HTML5/msUserSelect/. Благодаря сему мы уже не сможем выделить текст, аль что-то еще. Здесь имеет место различия в реализации между браузерами, но внимательно исследуйте пример (и MDC) и все станет на свои места. Такс, это было свойство user-select. Далее user-input. Он поможет блокировать любой ввод текста в поля ввода и т.п., расположеные под перекрывающей областью (кстати, быть может обозвать ее «overlay», ибо ширма как-то не звучит...).

                                              По поводу ссылок нужно думать. user-focus работает через одно место, ибо на его нормальную реализацию пока забыили, а user-focus-key и user-focus-pointer не сделали. Есть атрибут HTML «tabindex». Возможно ли расставить его средствами js — не знаю. Точнее можно-то можно, но при большом количетве объектов это будет медленно + нужно запоминать состояние уже имеющегося атрибута и возвращать его при удалении модального окна. Сам подобного не пробовал, ибо делал модальные окна там, где ссылок не было.

                                              Скорее всего я что-то проглядел, но статью-то не я пишу ;).
                                                0
                                                Ах вот вы про что… да, №1 уже запланирован к исправлению.

                                                pointer-events: none действительно может пригодиться, как и всё остальное. Спасибо.
                                              +3
                                              За отключение правой кнопки мыши — расстрел на месте.
                                                0
                                                Решение не «неправильно», а пока ещё технически несовершенно.
                                                  0
                                                  Без разницы как это обозвать. Я использовал вами же употребленное слово :). Доведите до ума и польователи будут вам благодарны (наверно :) ).

                                                  И, да, напишите в начале статьи большими красными буквами о том, что такое модальные окна, ибо в комментариях какой-то базар от непонимания предмета статьи.
                                                0
                                                filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#7F000000,endColorstr=#7F000000); /* IE6–IE8 */
                                                
                                                Не понятно, зачем в комментарии указан ИЕ6, все равно ведь правильно работать не будет, т.к. нет поддержки position: fixed.
                                                  0
                                                  Именно эта строка работать будет :)
                                                    0
                                                    Именно эта строка — да :) но в общем — нет.
                                                  0
                                                  Было бы интересно поработать с формами. Например, в модальном окне выводится фотография, под ней комментарии посетителей. И тут же форма – добавить комментарий. С валидацией на пустое поле, скажем. При добавлении коммент появляется тут же в модальном окне. Не планируете прикрутить такой функционал?
                                                  И еще один момент – добавление в текущее модальное окно какой-либо ajax-инфы по щелчку.
                                                    0
                                                    Открываем вашу демку, нажимаем среднюю кнопку мыши (у меня она же колесико скролла) и спокойно прокручиваем «подложку» вверх-вниз.
                                                      0
                                                      Что и упомянуто в самом конце поста.
                                                        0
                                                        Не заметил — прошу прощения. В целом с количеством багов не очень тянет на звание «Тру» модальных окошек, но если поправить все баги — станет очень даже тру!
                                                      +1
                                                      Может сразу стоит совместить с галереей например с Slimbox 2, вот здорово бы было )
                                                        +1
                                                        Маленькое пожелание к плагину — возможность закрыть модальное окно щелчком по области вокруг него, а не только крестиком.
                                                          0
                                                          Шикарный плагин! Серьёзно, это очень здорово. Когда он был нужен мне несколько месяцев назад, я даже удивился, что его ещё никто не написал.

                                                          Тем не менее, хочу обратить внимание на интересное поведение клавиш Home, End, Page Up и Page Down. На данный момент оно замечательно тем, что эти клавиши работают по-разному в разных браузерах. Например, в Firefox (13.0.1 и 14.0.1) они всегда прокручивают страницу (даже если в модальном окне есть содержимое), в Opera (10.62) они прокручивают модальное окно, если оно открыто, а иначе прокручивают страницу (то есть в Opera всё работает правильно), а в Chrome (19.0.1084.46 beta) они вообще ничего не делают.

                                                          А ещё вот это:

                                                          $('.modal .more-toggle').on('click', function(e){
                                                          	e.stopPropagation();
                                                          	$('.modal .more').toggle();
                                                          });
                                                          

                                                          Тут можно тоже e.preventDefault() добавить, чтобы лишний хэштэг не добавлять.
                                                            0
                                                            версия с центрированием как по горизонтали так и по вертикали, со скроллингом внутри и прибитыми хедером и футером, размытием страницы, а также возможностью открытия нескольких попапов со сборкой их в стопочку jsfiddle.net/fy48x/6/
                                                            выдрано из живого проекта, там только вёрстка. скрипты нужно допилить для себя
                                                              0
                                                              Со скролом все совсем плохо.
                                                                0
                                                                что именно плохо? в отличие от предложенных выше решений тут нет проблем со скроллированием страницы на фоне модального окна, скроллбар страницы затеняется вместе со страницей, а у модального окна свой независимый скроллинг, который тут выполнен внутри окна, чтобы шапка и подвал всегда были навиду.
                                                              0
                                                              В Опере работает автоскролл средней кнопкой мыши.
                                                              (нажать, отпустить и чуть сдвинуть мышь вниз)
                                                                0
                                                                Есть такое дело. В Gmail как-то запретили. Надо будет расковырять.
                                                                0
                                                                Автоскролл средней работает, да :-)
                                                                В fancybox классно модальное работает ещё.
                                                                  0
                                                                  И та же фигня наблюдается при использовании клавиши Tab: после последнего элемента фокус переходит либо на системные компоненты, либо на первый элемент в «фоновой» странице. В свое время боролся с этим в jqModal и поборол, чтобы после Tab на последнем элементе фокус переходил на первый в этом же окне и наоборот — при Ctrl-Tab на первом — на последний элемент. Кстати, понравилась реализация окна у KendoUI, за исключением пары досадных недостатков, включая упомянутый. Плюсы: неплохая кастомизация, перетаскивание, изменение размеров, набор кнопок в хидере, управление анимацией появления и исчезания, поддержка тачскринов, динамическое управление триггерами и т.д. Минусы: табом уходит фокус, не закрывается кнопками и кликом на пустую область, при запрете перетаскивания выделяется текст в хидере, нет автоцентрирования при динамическом изменении размеров. Пришлось допиливать под себя, но пока это самая грамотная реализация модального окна, которую я видел.

                                                                  Only users with full accounts can post comments. Log in, please.