Сложные интерфейсы на javascript вместе Yahoo UI. Часть 1

Сегодняшняя статья будет посвящена разработке с помощью javascript сложных и визуально богатых веб-приложений. Нет, даже не так: очень сложных и очень визуально богатых веб-приложений. Современный посетитель сайта уже привык к тому, что на практически каждом посещаемом им сайте активно используются идеи ajax, когда часть содержимого страницы меняется без перезагрузки всей страницы. Привык к тому, что активно используются всплывающие подсказки, из-за угла страницы плавно выезжают диалоговые окна, и под курсором мыши что-то "блестит, подсвечивается, перемещается". Удобно, приятно и, главное, легко реализуемо даже не слишком опытным веб-программистом, благо существует множество javascript-библиотек, которые автоматизируют создание подобных визуальных эффектов. Я и сам ранее написал две серии статей, посвященных популяризации подобных библиотек: jquery и mootools. Теперь, спустя полгода, я снова хотел бы вернуться к теме javascript-библиотек и созданию с их помощью эффективных веб- интерфейсов, но только немного с другой стороны.

Идея с переносом ряда классических desktop-приложений в internet крайне популярна в последние годы (и, что особенно приятно, коммерчески выгодна). Вот только инструментарий и методики, которые используются для разработки сайта "с рюшечками" и серьезного приложения (например, равного по функциональности gmail) различны. И если в первом случае нам хватает javascript-библиотеки, ориентированной на визуальные эффекты, то во втором нам нужны javascript-библиотеки, более похожие на библиотеки визуальных компонентов, доступные для программистов на Delphi/.net. Наличие в такой библиотеке большого количества сложных визуальных элементов управления вроде таблицы, дерева, набора закладок недостаточно. Нам нужно, чтобы код этих компонентов был написан крайне — я подчеркиваю: крайне эффективно. Я сам несколько раз слышал и видел, во что превращается веб-страница, на которой расположена табличка с сотней-другой записей. Загрузка процессора под 100% и полгигабайта оперативной памяти, которые "скушал" браузер, — это еще цветочки. И никакой пользователь не будет терпеть сайт (пусть даже с очень хорошим информационным наполнением), который ведет себя подобным образом. В этом плане я крайне приветствую приход на наш рынок технологий flex, silverlight 2, javafx. Сейчас Flex (спустя два с половиной года после появления первых версий flex sdk 2) уже начинает появляться на рынке (и даже в списке белорусских вакансий). А вот с silverlight2 (на момент написания статьи эта технология все еще в бетах) и javafx все гораздо хуже. Не решены вопросы с наличием и качественной работой этих технологий под различные версии О.С., нет критической массы установленных плагинов, нет достаточной массы ни специалистов, ни заказов. Так что создавать веб-приложения с "богатым" (сложным и удобным) интерфейсом можно только либо с помощью flex, либо с помощью javascript.

И эта статья будет посвящена именно наличным (значит, качественным и рабочим) средствам, ориентированным на javascript. Необходимо понимать, что существует огромная разница между приложениями, предназначенными для работы в internet и в intranet. В первом случае мы должны уделять внимание скорости загрузки страниц (следовательно, чем меньше по размеру библиотека, тем лучше) — следует помнить, что "парк" компьютеров у пользователей достаточно различен, и, кроме "новых и производительных", есть и "старые и медленные" компьютеры. Еще раз напомню, что основная масса пользователей рассматривает сайт как средство развлечения, а не "тот самый" источник информации, ради доступа к которой можно терпеть все. Совсем другое дело — если мы говорим о приложениях, предназначенных для использования в intranet. Веб-приложения размещены на внутреннем сервере компании, доступ к ним осуществляется по высокоскоростным сетям плюс унифицированный (хочется верить) как аппаратно, так и программно компьютерный "парк" и наличие системного администратора под боком. Когда пару лет назад я впервые столкнулся с javascript-библиотекой YUI (темой нашего разговора), то использовал средства этой библиотеки для создания именно "внутреннего проекта" и ни разу не пожалел. Недавно мне пришлось использовать YUI для обычного сайта, и я с радостью обнаружил, что прошедшее время оказало крайне положительное воздействие на yui. Так, появились новые элементы управления, javascript-код стал работать корректнее в разных версиях браузера, а грамотное разделение css-кода и javascript дает гибкие возможности по созданию не "сухих" офисных приложений, а "ярких и цветастых" сайтов для массового посещения. Подросшая пропускная способность сетей вкупе с алгоритмами сжатия javascript-файлов и грамотно настроенной системой кэширования могут снять последний аргумент, "почему сайт так медленно грузится".

Перед тем, как я перейду к практическому рассмотрению yui, необходимо разобраться с вопросами лицензирования. Библиотека распространяется бесплатно и с открытыми исходными кодам (лицензия BSD). Фактически суть лицензии BSD можно свести к высказыванию: "вот исходный код библиотеки, вы можете делать с ней все, что угодно, вы можете модифицировать этот код, вы можете создать на базе этого кода коммерческий продукт и никому не обязаны показывать его исходники". Единственное, что нельзя делать, — так это говорить, что именно вы автор данной библиотеки — других ограничений нет. Еще YUI часто сравнивают с другой известной библиотекой extJS. Так что стоит сказать пару слов об истории взаимоотношения этих продуктов. В недалеком 2006 году некто Jack Slocum работал над созданием для yui дополнительных расширений, и называлась эта библиотека yui-ext (распространялась также под лицензией BSD). Вскоре yui-ext приобрела такую популярность среди разработчиков, что Jack Slocum решил отделить свое детище от yahoo ui и создал коммерческую компанию, которая посвятила себя развитию extjs. Extjs распространяется под двойной лицензией и в общем случае для коммерческого использования является платным. В настоящее время extjs приобрел достаточную популярность, и уже появились библиотеки, интегрирующие extjs с популярными серверными framework'ами (java, php).

Если вы заинтересуетесь yui, то настоятельно советую добавить в закладки своего браузера сайт сайт , на котором публикуют свои блоги разработчики yahoo, работающие над yui. На домашнем сайте YUI (расшифровывается как Yahoo User Interface library) сайт вы можете скачать архив с библиотекой. Распаковав архив, вы увидите 7 папок: build, docs, examples, as-docs, as- src, assets (не обращайте на нее внимания), tests. В папке build содержатся javascript-файлы собственно библиотеки. Обратите внимание на то, что каждый файл существует в трех версиях — например: animation.js, animation-debug.js, animation-min.js. Если назначение третьего файла очевидно, то с первыми двумя стоит разобраться подробнее. В состав YUI входит модуль журналирования. Условно говоря, с его помощью где-нибудь на html- странице можно создать плавающее окошко, в котором будут выводиться сообщения от самого yui либо вы можете при написании собственного кода вместо "надоедливых и неудобных" окошек alert использовать вывод в стандартизированной форме. Я обязательно расскажу об использовании модуля журналирования, но позже. Возвращаясь к рассмотрению устройства архива библиотеки yui: второй каталог — docs — содержит документацию по библиотеке (документация очень хорошая и написана подробно). Я настоятельно рекомендую в дополнение к библиотеке загрузить с сайта yahoo еще и так называемые Cheat Sheets. Cheat Sheets — это набор pdf-файлов-шпаргалок. Каждая "шпаргалка" описывает один из модулей yui и содержит основные сведения для "быстрого старта", так что начать использовать yui можно и без долгого штудирования мануала. Третий каталог, который вы найдете в архиве yui, — это examples. Там вы можете найти для каждого из модулей пачку примеров. YUI в последних своих версиях (я описываю в этой серии статей версию 2.5.2) состоит не только из "чистого" javascript. Так, для функционирования некоторых модулей (построение графиков и загрузка файлов на сервер) нужен flash-код. Исходники этих flash-роликов находятся в папке as-src, а документация — в подкаталоге as-docs. Подкаталог tests содержит набор js-файлов с тестами модулей yui (не слишком полезен для нас). Теперь перейдем к рассмотрению каждого из модулей, образующих yui, подробно и на практике.

Условно модули, образующие yui, делятся на четыре группы. Во-первых, поддержка css. Эта часть, гордо называемая css-framework, включает наборы css-стилей для создания веб-интерфейсов, построенных на сетках (что это за такой вид верстки, можно почитать на сайте сайт ). Затем идут модули, образующие ядро yui (global, dom, event). Третья часть yui — это "всякое разное". В эту категорию входят модули для создания анимации веб-страниц, поддержки D&D, управления загрузкой ресурсов. Четвертая часть yui — основная часть материала будет посвящена именно ей — образована различными графическими компонентами (деревья, сетки, менюшки, кнопки, падающие списки…). Рассказ о css-grid'ах я пропускаю, а сейчас начну с рассмотрения возможностей "ядра yui". Если вы вспомните мою серию статей про jquery, то увидите, что больше всего я там "восхищался" средствами для поиска заданных узлов (html-элементов) на странице. Вместо того, чтобы организовывать "страшные" циклы и много-много if'ов, я мог записать компактное выражение, перечислив цепочку css-характеристик узлов, через которые нужно пройтись в поисках нужного dom-узла. Рассмотрение YUI я начну тоже с этой темы. Прежде всего, нужно создать html-файл, в котором подключаются следующие js-модули yui:
<script type="text/javascript" src="js/yahoo/yahoo.js"></script>
<script type="text/javascript" src="js/event/event.js"></script>
<script type="text/javascript" src="js/dom/dom.js"></script>
<script type="text/javascript" src="js/selector/selector-beta.js"></script>

Для последующих тестов я создал html-файл с множеством html-элементов различных типов, вложенных друг в друга. Завершающим штрихом будет создание кнопки с обработчиком события, вызывающим функцию doAction:
<button onclick="doAction()">do something</button>

Внутри функции doAction я обращаюсь к объекту YAHOO.util.Selector и вызываю метод query в качестве параметра, которому передается строка условия поиска:
function doAction (){
alert (YAHOO.util.Selector.query ('#block span')); }

Результатом вызова метода query будет список всех тегов span, находящихся внутри тега, с идентификатором "block" на всех уровнях вложенности. Для того, чтобы найти только те теги span, которые непосредственно вложены внутрь block, нужно использовать такой синтаксис: "#block > span". Чтобы отобрать только те теги span, которые помечены классом "warning", используем запись "#block > span.warning". А такая форма записи "b + i" означает, что нужно найти элемент "i", непосредственно (именно непосредственно) следующий за тегом "b" (т.е. "b" является sibling'ом тега "i"). В случае, если нужно найти тег "i", следующий после "b", но ограничения на непосредственность нет, можно использовать такую запись: "b ~ i". В этом случае мы найдем тег "i" даже если между ним и "b" есть какие-то еще теги. YUI Selector поддерживает некоторое подмножество CSS3-селекторов — например, "div > span:first-child" означает, что непосредственно внутри тега "div" нужно найти первый (на это указывает псевдокласс ":first- child") дочерний тег "span". Аналогично псевдокласс ":last-child" поможет вам выбрать последний тег заданного типа. Запись "span em:only-child" позволяет найти тег span, содержащий один-единственный элемент "em". Если же нужно найти тот тег "div", содержимое которого (напрямую самого "div"'а или одного из вложенных элементов) содержит слово "help", то используйте такую запись: "div:contains(help)" (слово "help" должно идти без кавычек).

Что же, это, конечно, не все возможности селекторов yahoo, но пора идти дальше. Функция query получает не только единственный параметр со строкой "что искать", но можно указать dom-элемент страницы, относительно которого нужно начать поиск (второй параметр функции query). Третий параметр (логического типа) управляет тем, будет ли yui искать все элементы по заданному вами шаблону или будет возвращен только первый из подошедших. В состав yui-модуля Selector входит еще функция filter. В качестве первого параметра ей передается список узлов, а второй параметр играет роль фильтра условия (увы, но использовать все вышеописанные возможности фильтрации нельзя). Таким образом, можно из списка узлов отбросить те, которые не подходят дополнительному условию. Похожа на filter и функция test, только ей в качестве первого параметра должен быть передан одиночный узел, и в случае, если этот узел соответствует условию, заданному вторым параметром, функция test вернет "true".

После того, как научишься пользоваться модулем Selector и функцией query, неизбежно возникает вопрос: и что с того? Действительно, обычно мы получаем ссылку на некоторые узлы в html-документе для того, чтобы в последующем как-то их обработать — например, назначить определенное стилевое оформление. В составе модуля YAHOO.util.Dom есть несколько методов, позволяющих назначить элементу некоторый css-класс или избавиться от оного. Для демонстрации этих возможностей я решил развить пример с поиском внутри dom-дерева документа некоторых узлов и уже не выводить их на экран функцией alert, а выделять визуально — например, красным цветом. Для этого я создал css-класс red:
<style>.red {color: red;}</style>

Html-код страницы был немного модифицирован: я создал текстовое поле, расположенное непосредственно перед кнопкой, и дал ему название "reg". Теперь пользователь может ввести в это поле выражение поиска, нажать кнопку и увидеть, как на html-странице некоторые элементы меняют цвет на красный. Однако перед тем, как применять новое оформление, необходимо удалить старое (те элементы, которые были помечены красным в прошлой итерации, должны снова принять свой обычный внешний вид):

//создаем переменную, хранящую список узлов, выделенных в прошлый раз
var old = null;
function doAction (){
// в том случае, если какие-то элементы были выделены, необходимо удалить от них класс red
if (old != null)
YAHOO.util.Dom.removeClass (old, 'red');
// а теперь ищем узлы по заданному условию
old = YAHOO.util.Selector.query ( YAHOO.util.Selector.query('#reg')[0].value );
// и применяем к ним выделение
YAHOO.util.Dom.addClass (old, 'red'); }

Наверняка вместе с функциями addClass и removeClass вам пригодится и функция replaceClass (для кого, старое, новое). Ее назначение — заменить для элемента старое значение css-класса на новое (внимание: если у элемента нет старого класса, то новый класс ему все равно будет назначен). Еще одна полезная функция для работы со стилями — hasClass (для кого, класс). Она позволяет узнать, назначен ли некоторый css-класс для заданного элемента. Помимо изменения стилевого оформления элементов, второе наиболее частое действие с узлами DOM-документа — это их динамическое добавление, удаление, перемещение. И тут, скажем прямо, yui не слишком хорош. Дело в том, что, хотя в состав yui-объекта Dom входят методы insertAfter и insertBefore (первый параметр этих функций — то, что нужно вставить, второй — элемент с позицией вставки). Так вот, фокус в том, что мы не можем задать наподобие jquery что-то вроде (внимание, этот код не работает):
YAHOO.util.Dom.insertAfter ('<span>new text</span>', targetBox)

В jquery функции вставки узлов в случае, если "то, что нужно вставить" не представлял собой конкретный html-элемент, выполнялось автоматическое создание на основании строки набора html-элементов, которые затем и вставлялись в искомую позицию. В yahoo мы должны явно создать вставляемые элементы — собственно говоря, это нетрудно, но неприятно. В состав модуля Dom входит еще несколько функций, позволяющих получить информацию об элементах страницы. Например, getX, getY, setX, setY — функции, возвращающие координаты элементов и, соответственно, устанавливающие их. Функция getStyle возвращает сведения о стилях, примененных к элементу с помощью атрибута "style", а функция setStyle позволяет присвоить стилю новое значение:
YAHOO.util.Dom.getStyle(elts, 'color');
YAHOO.util.Dom.setStyle(elts, 'color', 'red');

Теперь перейдем к рассмотрению еще одного модуля yui, образующего его ядро — Event. Этот модуль предназначен для управления событиями. В качестве первого примера я перепишу показанный ранее фрагмент кода с кнопкой и явно заданной через атрибут "onclick" функцией обработки события "на кнопку нажали". Первое, о чем нужно задуматься, — так это о методике подписки на событие "дерево DOM готово к использованию". Эта стандартная функциональность для любой уважающей себя библиотеки javascript заключается в наличии способа сказать, что некоторая функция должна быть вызвана не на событие "страница загрузилась", а на событие "DOM готов к использованию", которое выбрасывается тогда, когда загружена структура документа, и мы можем назначить обработчики для кнопок меню, не дожидаясь завершения загрузки всей страницы (а этого, может быть, придется ждать очень долго, особенно если на страничке много больших картинок, баннеров, загружаемых с других сайтов…).
function initYUI (){
YAHOO.util.Event.addListener ( YAHOO.util.Selector.query('button'),'click', doAction );}
YAHOO.util.Event.onDOMReady (initYUI);

Здесь я с помощью вызова onDOMReady назначаю функцию инициализации initUI, которая, в свою очередь, вызвав метод addListener (приятно, что мы можем назначить таким образом обработчик события сразу для нескольких элементов), привязывает к тегу "button" и событию "click" обработчик события doAction. Кроме привязки событий, мы можем их и отсоединять (вызвав метод removeListener с такими же самыми параметрами). Особый интерес представляет идея с созданием собственных типов событий: это дает возможность построения модульных приложений, в которых модули извещают друг друга об изменениях именно с помощью рассылки подобных "custom"-событий.

В следующий раз я продолжу рассказ об yui и перейду к рассмотрению модуля поддержки анимации, также нас ждут различные widget'ы (меню, набор закладок, таблицы).

black-zorro@tut.by, black-zorro.com


Компьютерная газета. Статья была опубликована в номере 28 за 2008 год в рубрике программирование

©1997-2024 Компьютерная газета