Создание полосы вкладок на основе HTML с помощью jQuery UI

Дино Эспозито (Dino Esposito) | 13 ноября 2009 года

Создание сложных и мощных пользовательских веб-интерфейсов никогда не было простой задачей и, вероятно, никогда не будет. Сложный пользовательский веб-интерфейс требует достаточно много работы над основным HTML-кодом для компоновки необходимой графики и достаточно связующего кода JavaScript, чтобы заставить его работать так, как нужно. Как разработчик, вы отвечаете и за макет, и за код, связывающий пользовательский интерфейс в одно целое. Что еще более важно, вы вынуждены использовать инструменты, которые были в ходу за несколько лет до революции ASP.NET.

Технология ASP.NET была революционной, так как она перенесла разработчиков в мир серверного программирования, в котором клиентские функции генерируются особыми компонентами со скрытой структурой — серверными элементами управления. Наступление эпохи AJAX в корне изменило положение вещей, так как разработчики снова поняли важность выполнения работы на стороне клиента через непосредственный контакт с HTML и JavaScript.

В этой статье я рассмотрю некоторые возможности специальной библиотеки, которые выводят программирование на стороне клиента на новый уровень. Это библиотека jQuery UI, которая является коллекцией мини-приложений и эффектов, составляющих популярный набор возможностей, предлагаемых классической библиотекой jQuery, с особым уклоном в область создания пользовательских интерфейсов. В этой статье я особо рассмотрю создание и оформление вкладок.

Значение организации доступа к DOM

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

Иногда клиентский код в решениях AJAX ограничен загрузкой некоего предварительно форматированного HTML-контента, который присоединен к элементу DOM посредством свойства innerHTML. Это имеет место, например, в частичном отображении, а также для некоторых сторонних элементов управления, которые используют инфраструктуру частичного отображения Microsoft. Этот подход эффективен в сравнении со сценариями без AJAX, но все же он создает множество трудных для браузера задач. Если свойство innerHTML задано, браузер фактически вынужден повторно создавать и отображать целое поддерево. Обычно браузеры довольно быстро обрабатывают свойство DOM innerHTML, хотя все браузеры делают это по-разному. Если бы можно было сделать необходимые изменения непосредственно в DOM, это было бы идеально. Библиотека jQuery очень помогает, предоставляя возможности для работы с элементами DOM, особенно когда дело доходит до создания насыщенных и сложных компонентов пользовательского интерфейса.

Лучшее основание для использования jQuery UI — это возможность определить шаблон в соответствии с известными правилами, а затем вызвать функцию jQuery UI для данного поддерева DOM, когда страница узла загружена или когда она действительно нужна. Откуда ни возьмись, каким-то мистическим образом оживает другой статический фрагмент HTML и начинает реагировать на события мыши. Более того, тот же фрагмент HTML оформлен в новом стиле, что является результатом прекрасной поддержки CSS, предлагаемой библиотекой jQuery UI.

Библиотеку jQuery UI можно бесплатно загрузить с сайта jqueryui.com. Библиотека обладает модульной структурой. При обращении к сайту вы можете собрать собственный пакет, комбинируя модули, которые вам понадобятся. Также можно включать на свои страницы ссылки на несколько предварительно собранных версий, размещенных в сети доставки контента (CDN) Google. Для элементов управления в библиотеке jQuery UI необходима библиотека стилей. При загрузке пакета выбирается тема по умолчанию, которую можно легко изменить на любую другую на ваш вкус.

В контексте приложения ASP.NET MVC для хранения файлов CSS необходимо создать папку jq-ui-Themes в папке Content. Файлы скриптов хранятся в папке Scripts\jQueryUI, как показано на рис. 1. Для библиотеки jQuery UI требуется классическая библиотека jQuery. Последняя версия библиотеки jQuery UI — 1.7.2; эта версия связана с версией jQuery 1.3.2.


Рис. 1: Библиотека jQuery UI в действии в проекте ASP.NET MVC.

Шаблон для вкладок

Давайте посмотрим, что нужно для создания нескольких вкладок для веб-страницы с помощью jQuery UI. Для начала создайте структуру DOM, соответствующую стандартной панели вкладок jQuery UI. По умолчанию список вкладок принадлежит структуре, созданной элементом DIV, который содержит неупорядоченный список (UL). Вот минимальная необходимая разметка:

<div id="MyFirstTabPanel">
  <ul>
  </ul>
</div>

Чтобы преобразовать эту структуру в динамический объект контейнера вкладок JavaScript, необходима ссылка на корневой элемент через jQuery, для которой вызывается функция tabs. Это показано в следующем фрагменте кода:

<script type="text/javascript">

    function fnCreateTabs() {

        $("#MyFirstTabPanel").tabs();

    }

</script>

На рис. 2 показан результат. Не очень много, но достаточно, чтобы поверить, что что-то происходит.


Рис. 2: Пустая вкладка, созданная вверху структуры DOM.

Функцию tabs можно вызвать из обработчика события JavaScript (например, из обработчика нажатия кнопки) или из функции ready, которая запускается при загрузке страницы. Чаще всего, все что нужно — чтобы вкладки были готовы и запускались при загрузке страницы. Однако в целях тестирования возможно имеет смысл использовать нажатия кнопок, как показано ниже:

<input type="button" value="Create" onclick="fnCreateTabs()" />

Можно добавить одну или несколько вкладок динамически или расширить начальный HTML-шаблон и задать группу готовых вкладок. Вот код, необходимый для динамического создания вкладки:

function fnAddTab() {

   $("#MyFirstTabPanel").tabs('add', "/index/about", "Added on the fly", 0);

}

URL-адрес может указывать на ресурс, локальный для приложения или для предварительно скрытого элемента DIV на странице. Однако вкладку нельзя использовать как IFRAME, т. е. нельзя загрузить с внешнего сайта. Более того, при нажатии любой абсолютной ссылки на вкладке обновится не вкладка, а браузер. Указатель на элемент DIV на странице задается с помощью знака #, в виде #divID. При указании на URL-адрес вне страницы, в заголовке вкладки временно отображается сообщение “loading …”, так как AJAX загружает разметку для вставки. С помощью приведенных в таблице 1 параметров AjaxOptions можно настроить механику загрузки.

На рис. 3 показаны текущие результаты.


Рис. 3: Контейнер вкладок с динамически добавленной вкладкой.

Подобным образом можно удалить существующую вкладку и уничтожить контейнер целиком. Вот необходимый код; удаляемая вкладка определяется по индексу:

function fnDestroyTabs() {

    $("#MyFirstTabPanel").tabs('destroy');

}

function fnRemoveTab() {

    $("#MyFirstTabPanel").tabs('remove', 0);

}

Построение реалистичного контейнера вкладок

Слишком много кода для нескольких базовых задач. Теперь давайте рассмотрим более эффективные способы использования функции вкладки. Первое, что нужно продумать — как определить контент вкладок. Вкладка может указывать на общедоступный URL-адрес или на раздел текущего документа. Если вкладка указывает на общедоступный URL-адрес, контент загружается в фоновом режиме, а затем вставляется на страницу. Для загрузки динамического контента из Интернета используется инфраструктура AJAX или классическая библиотека jQuery.

Часто в качестве альтернативы для контейнера вкладок задается относительно статичная структура, в которой каждая вкладка указывает на дочерний блок корневого элемента DIV, который является центром контейнера вкладок. В следующей разметке показана статическая схема для контейнера вкладок:

<div id="AppConsole">
    <ul>
        <li><a href="#tabCustomers"><span><b>Customers</b></span></a></li>
        <li><a href="#tabOrders"><span><b>Orders</b></span></a></li>
        <li><a href="#tabOther"><span><b>Other</b></span></a></li>
    </ul>
    <div id="tabCustomers">
        <span style="font-size:150%"><b>Customers</b></span>
        <hr style="height: 2px;border-top:solid 1px black;" /> 
        :
    </div>
    <div id="tabOrders">
        <span style="font-size:150%"><b>Orders</b></span>
        <hr style="height: 2px;border-top:solid 1px black;" /> 
         :
    </div>
    :
</div>

Каждая отображаемая вкладка представлена элементом LI, который содержит дочерний элемент привязки. Вот универсальный шаблон, определенный в исходном коде библиотеки:

<li><a href="#{href}"><span>#{label}</span></a></li>

При нажатии на вкладку срабатывает URL-адрес в привязке и выполняется переход внутри страницы, если используется URL-адрес со знаком #.

Весь контент, который должен отображаться при выборе соответствующей вкладки (если он не загружается динамически с URL-адреса), должен быть размещен как встроенная разметка. Это как минимум приводит к проблемам с удобочитаемостью и поддержкой кода. Однако в ASP.NET MVC эту проблему можно обойти с помощью вспомогательного метода HTML RenderPartial. Вот как это можно сделать:

<div id="AppConsole">
    <ul>
        <li><a href="#tabCustomers"><span><b>Customers</b></span></a></li>
        <li><a href="#tabOrders"><span><b>Orders</b></span></a></li>
        <li><a href="#tabOther"><span><b>Other</b></span></a></li>
    </ul>
    <div id="tabCustomers">
        <span style="font-size:150%"><b>Customers</b></span>
        <hr style="height: 2px;border-top:solid 1px black;" /> 
        <%  
            Html.RenderPartial("uc_ListCustomers", someData);
        %>
    </div>
    :
</div>

Вспомогательный метод Html.RenderPartial просто обрабатывает контент указанного представления (а именно, пользовательского элемента управления ASP.NET) и выдает готовую разметку. Таким образом, структура контейнера все же содержит статический контент, но, как минимум, его проще поддерживать и HTML-страница остается гораздо более удобочитаемой.

Если планируется использовать контейнер вкладок как меню для всей страницы, то обычно нужно, чтобы полоса вкладок инициализировалась при загрузке страницы. Идеальным способом для выполнения этого действия в контексте jQuery является функция ready, как показано ниже:

<script type="text/javascript">

    $(document).ready(function() {

        $("#AppConsole").tabs(

            {

                disabled: [2], 

                event: 'mouseover',

                fx: { opacity: 'toggle' }

            }

        );

    });

</script>

Как видите, для инициализации функции tabs требуется совсем немного действий. В частности, можно задать объект параметров для управления представлением и поведением полосы вкладок. В следующем разделе мы рассмотрим подробности. На рис. 4 показан гораздо более профессиональный контейнер вкладок, построенный с помощью jQuery UI и темы Sunny.


Рис. 4: Более профессиональная полоса вкладок, настроенная с помощью jQuery UI.

Параметры для контейнера вкладок

Как вы видели, функция tabs настраивает контейнер. Эта функция имеет довольно гибкую сигнатуру. Например, ей можно передать объект параметров, как было показано ранее. Параметры, передаваемые при запуске, заменяют основные и стандартные параметры, неявно определенные библиотекой. В таблице 1 показаны некоторые значения по умолчанию и параметры, которые можно настроить при запуске.


Таблица 1: Свойства объекта вкладки в jQuery UI

Свойство Описание
disabled Ссылается на массив индексов с нулевым основанием, которые соответствуют вкладкам, изначально отображаемым как отключенные. По умолчанию это пустой массив [].
event Ссылается на событие, используемое для переключения между вкладками. По умолчанию это событие click.
fx Ссылается на набор эффектов анимации, таких как прозрачность или продолжительность отображения/скрытия. Продолжительность выражается как slow (медленно), fast (быстро) или normal (нормально).
ajaxOptions Ссылается на параметры, используемые для действий AJAX, направленных на асинхронную загрузку контента вкладки.
selectedClass Ссылается на класс CSS, выбираемый из текущей темы для стиля выбранной вкладки.
unselectClass Ссылается на класс CSS, выбираемый из текущей темы для стиля невыбранных вкладок.
disabledClass Ссылается на класс CSS, выбираемый из текущей темы для стиля отключенных вкладок.
panelClass Ссылается на класс CSS, выбираемый из текущей темы для стиля области панели.
loadingClass Ссылается на класс CSS, выбираемый из текущей темы для стиля вкладки во время загрузки ее контента из Интернета.


Внутренний код компонентов jQuery UI ссылается на предопределенные имена классов CSS, которые эти компоненты рассчитывают найти в текущей теме. У каждой существенной области элемента управления есть собственное имя класса CSS. Однако показанные в таблице 1 свойства xxxClass дают возможность изменить эти имена.

Отправка команд в контейнер вкладок

Кроме объекта параметров, метод tabs принимает несколько команд для взаимодействия со страницами. Синтаксис показан ниже:

$(tabElement).tabs(команда, параметры)

Команда — это предопределенное имя, распознаваемое компонентом. Некоторые команды не имеют параметров, а некоторые принимают несколько параметров. В таблице 2 показано несколько поддерживаемых команд.


Таблица 2: Некоторые команды, поддерживаемые контейнером вкладок

Свойство Описание
destroy Команда без параметров; используется для уничтожения вкладок. После вызова этой команды представление дерева DOM возвращается в исходное состояние, существовавшее до применения метода tabs.
add(url, метка, индекс) Добавляет в контейнер новую вкладку. Вкладка добавляется в указанную позицию с заданной меткой и URL-адресом.
select( индекс) Выбирает вкладку с указанным индексом. Индекс с нулевым основанием.
select( идентификатор) Выбирает вкладку с соответствующим идентификатором. Идентификатор — это значение атрибута href в элементе LI, который представляет вкладку.
remove(индекс) Удаляет указанную вкладку. Если индекс не соответствует ни одной существующей вкладке, ничего не происходит.
disable(индекс) Отключает указанную вкладку. Если индекс не соответствует ни одной существующей вкладке, ничего не происходит.
enable(индекс) Включает указанную вкладку. Если индекс не соответствует ни одной существующей вкладке, ничего не происходит.
option(имя, [значение]) Получает (или задает) значение параметра в объекте вкладки. Параметр — это по существу любой атрибут в структуре HTML, которым можно управлять посредством синтаксиса jQuery. Например, эту функцию можно использовать для выбора вкладки или получения индекса выбранной вкладки. Более того, можно выполнить специальный запрос, чтобы выбрать только те элементы, которые соответствуют заданному условию.
load(индекс) Загружает контент указанной вкладки.

События вкладки

Компонент, который запускает несколько событий для основных ситуаций, которые могут встретиться при работе. Например, событие происходит при выборе, добавлении или удалении вкладки. Более того, событие запускается при отображении, включении, отключении или загрузке вкладки. В таблице 3 перечислены поддерживаемые события:


Таблица 3: События, поддерживаемые контейнером вкладок

Событие Описание
tabsadd Запускается после добавления вкладки.
tabsdisable Запускается после отключения заданной вкладки.
tabsenable Запускается после включения заданной вкладки.
tabsload Запускается после загрузки контента вкладки из удаленного расположения (через AJAX).
tabsremove Запускается после удаления заданной вкладки.
tabsselect Запускается после выбора заданной вкладки.
tabsshow Запускается после отображения заданной вкладки.


Все обработчики событий принимают объект, который представляет контейнер для данных, связанных с вкладкой. Вот сигнатура обработчика события:

function(event, ui) 

{

    :

}

Из параметра ui можно извлечь различные фрагменты более конкретных данных, например таких, которые перечислены в таблице 4.

Таблица 4: Данные, доступные для обработчиков событий.

Свойство Описание
ui.index Возвращает индекс с нулевым основанием для вкладки, связанной с событием. Например, для события tabsadd это будет индекс новой добавленной вкладки.
ui.panel Возвращает элемент, который содержит контент вкладки, связанной с событием. Например, для события tabsselect это будет элемент DIV, содержащий контент вкладки.
ui.tab Возвращает элемент привязки вкладки, связанной с событием.

Работа с выбором вкладки

Как правило, пользователь выбирает вкладку в контейнере щелчком или, скажем, наводя курсор мыши на выбираемую вкладку. Тем не менее, полезно иметь возможность выбрать вкладку программно. Команда select предназначена как раз для этой цели:

$("#MyTabContainer").tabs('select', 2);

Предыдущий код выбирает третью вкладку (если она существует). Индекс, естественно, с нулевым основанием.

Иногда даже более полезно иметь возможность извлечь индекс текущей выбранной вкладки, как показано ниже:

var index = $("#MyTabContainer").tabs('option', 'selected');

В этом случае также вызывается команда option, но считывается индекс, в котором задан атрибут selected.

Еще одной интересной возможностью, имеющей отношение к выбору, является запрет переключения на другую вкладку, если условия на текущей вкладке не позволяют это сделать. Все, что нужно сделать для запрета изменения выбора — обработать событие tabsselect, оценить состояние приложения и вернуть значение false из обработчика.

Загрузка контента через AJAX

При планировании полосы вкладок обычно необходимо разбить некий контент пользовательского интерфейса на несколько более удобных частей. Вся разметка загружается со страницей и собирается в модель DOM. Это означает, что чаще всего контент вкладок уже известен при их отображении. В этой связи можно улучшить взаимодействие с пользователем, скрыв несколько вкладок и отображая их при необходимости; однако это вряд ли может улучшить производительность. С другой стороны, использование загрузчика AJAX гарантирует, что разметка будет загружаться и увеличивать размер DOM только при определенных требованиях.

Контент может загружаться и отображаться автоматически, если в качестве URL-адреса новой вкладки задать URL-адрес из того же домена. Метод load принимает индекс вкладки и загружает для нее контент с веб-сервера. Независимо от кэша браузера, всегда загружается новый контент. Например:

$("#MyTabContainer").tabs('load', 2);

В качестве альтернативы вы можете управлять этим процессом самостоятельно и использовать API jQuery AJAX для установки подключения и захвата данных, а затем использовать API DOM для их присоединения к ранее пустой вкладке.

Выводы

Сегодня вкладки стали довольно обычным компонентом веб-интерфейса. Чаще всего вкладки кодируются при загрузке сегментов страницы и их присоединении к событиям мыши. При нажатии на определенную ссылку отображается связанный с ней контент. С точки зрения браузера вся работа выполняется при первой загрузке страницы. После этого происходит только изменение нескольких атрибутов видимости. Библиотека jQuery UI позволяет собрать полосу вкладок из нескольких тегов DIV и элементов LI, которые уже могли присутствовать на странице. Это похоже на процесс определения для элементов такого стиля, чтобы они выглядели как полоса вкладок, за исключением того, что стиль также включает некое специальное поведение.  Для экономии памяти вкладками также можно управлять динамически. Однако для меня простота и эффективность настройки остается наиболее привлекательной характеристикой.