Продолжение предыдущего поста: http://blog.e0ne.info/post/Modal-popup-with-HTML.aspx

Часть 2. Практика

Если есть проблема, то, обязательно, должно быть решение. Так как готового я не нашел (возможно, плохо искал), пришлось изобретать свое.  Решений, на самом деле, нашлось аж целых два, но так как я остановился лишь на одном, то это решение я рассмотрю более подробно и с примером.

Для начала рассмотрю тот вариант, который я не стал реализовывать.

Решение проблемы #1: манипуляции с свойством tabIndex.

Те, кто сталкивался с необходимостью навигацией по сайту с помощью клавиатуры непременно сталкивались с таким свойством элементов, как tabIndex. По умолчанию, для всех элементов <input /> (кроме <input type=”hidden” />) и <option /> tabIndex равен 0. Для других элементов свойство или не определено, или задано значение “-1” (минус еденица) В таком случае переход по элементам осуществляется в порядке их расположения в DOM-моделе. Если необходимо сделать так, чтоб при навигации по странице с помощью кнопки tab фокус на элемент не попадал, то значение tabIndex необходимо сделать -1.

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

  • реализовать диалоговое окно, как описано в предыдущем посте;
  • при вызове функции show() для всех элементов запоминаем текущее состояние tabIndex и ставим новое значение, равное “-1”;
  • в вызове функции hide() восстанавливаем прежние значения tabIndex.

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

Решение проблемы #2: манипуляции с DOM.

Мой диалог работает следующим образом: базовая часть отображения остается без изменений, все изменения касаются только функций show и hide и некоторыми манипуляциями в DOM-модели.

Работает это следующим образом:

  • все содержимое тега body обрамляем контейнером:
    $(document.body).wrapInner("<div id='body-container' />");
  • делаем метку, где находится наш диалог:
    $("<div id='dlg-marker'/>").insertBefore($("#dlg-content"));
  • перемещаем содержимое диалога в начало тега body:
    $(document.body).prepend($("#dlg-content"));
  • ставим атрибут disabled=”true” для всех элементов, которые не находятся в диалоге:
    $("#body-container").attr('disabled', true);
    // for webkit based browsers
    $("#body-container > input, option").each(function(){
        $(this).attr('disabled', true);
    });
  • показываем диалог.

Соответственно, в методе hide() необходимо все вернуть на своё место:

  • убираем атрибут disabled:
    $('#body-container').removeAttr('disabled');
  • ставим диалог на то место, где он находился изначально; для этого нам и нужна была метка:
    $("#dlg-content").insertAfter($("#dlg-marker"));
  • удаляем ранее созданную метку:
    $("#dlg-marker").detach();
  • удаляем элемент, в который мы поместили содерживое body:
    var html = $('#body-container').html();
    $('#body-container').detach();
    $(document.body).html(html);
  • прячем диалог.

Этот код является слишком неоптимизированным - от него можно избавится, если элемент <div id='body-container' /> будет находится на странице всегда.

Манипуляции с возвращением диалога на прежнее место, прежде всего, необходимы для корректной работы приложений, написанных с помощью ASP.NET. Если этого не сделать, то будут проблемы c UpdatePanel и кнопками, которые находятся вне формы.

В файле advanced-popup-with-input.html я привожу базовую реализацию такого способа. Для использования в production его необходимо немного доработать напильником. А именно:
оформить это всё в виде плагина для jQuery или виджета jQueryUi;
решить проблему с производительностью и обработчиками событий при возвращении элементов в начальное положение относительно DOM-модели.

Сейчас похожая реализация успешно работает в production коде, единственное отличие состоит в том, что элемент <div id='body-container' /> всегда находится на странице и нет необходимости его добавлять и удалять.

Все примеры доступны на GitHub: https://github.com/e0ne/BlogSamples/tree/master/ModalDialog


Comments are closed