• ГЛАВА 14. Введение в Web-программирование. Язык JavaScript 
  • Примеры Web-сценариев 
  • Простейший Web-сценарий
  • Как Web-сценарии помещаются в Web-страницу
  • Язык программирования JavaScript 
  • Основные понятия JavaScript
  • Типы данных JavaScript
  • Переменные 
  • Именование переменных
  • Объявление переменных
  • Операторы 
  • Арифметические операторы
  • Оператор объединения строк
  • Операторы присваивания
  • Операторы сравнения
  • Логические операторы
  • Оператор получения типа typeof
  • Совместимость и преобразование типов данных
  • Приоритет операторов
  • Сложные выражения JavaScript 
  • Блоки
  • Условные выражения
  • Условный оператор?
  • Выражения выбора
  • Циклы 
  • Цикл со счетчиком
  • Цикл с постусловием
  • Цикл с предусловием
  • Прерывание и перезапуск цикла
  • Функции 
  • Объявление функций
  • Функции и переменные. Локальные переменные
  • Вызов функций
  • Присваивание функций. Функциональный тип данных
  • Массивы
  • Ссылки
  • Объекты 
  • Понятия объекта и экземпляра объекта
  • Получение экземпляра объекта
  • Работа с экземпляром объекта
  • Встроенные объекты языка JavaScript
  • Объект Object и использование его экземпляров
  • Объекты Web-обозревателя. Объектная модель документа DOM
  • Свойства и методы экземпляра объекта
  • Правила написания выражений
  • Комментарии JavaScript
  • ГЛАВА 15. Библиотека Ext Core и объекты Web-обозревателя 
  • Библиотека Ext Core 
  • Зачем нужна библиотека Ext Core
  • Использование библиотеки Ext Core
  • Ключевые объекты библиотеки Ext Core
  • Доступ к нужному элементу Web-страницы
  • Доступ сразу к нескольким элементам Web-страницы
  • Доступ к родительскому, дочерним и соседним элементам Web-страницы
  • Получение и задание размеров и местоположения элемента Web-страницы
  • Получение размеров Web-страницы и клиентской области окна Web-обозревателя
  • Получение и задание значений атрибутов тега
  • Управление привязкой стилевых классов
  • Получение и задание значений атрибутов стиля
  • Управление видимостью элементов Web-страницы
  • Добавление и удаление элементов Web-страницы
  • Обработка событий 
  • Понятие события и его обработки
  • События объекта Element
  • Привязка и удаление обработчиков событий
  • Всплытие и действие по умолчанию
  • Получение сведений о событии. Объект EventObject
  • Объект CompositeElementLite
  • Объекты Web-обозревателя
  • ГЛАВА 16. Создание интерактивных Web-страниц 
  • Управление размерами блочных контейнеров
  • Выделение пункта полосы навигации при наведении на него курсора мыши
  • Переход на целевую Web-страницу при щелчке на пункте полосы навигации
  • Скрытие и открытие вложенных списков
  • Выделение пункта полосы навигации, соответствующего открытой в данный момент Web-странице
  • Скрытие и открытие текста примеров
  • ЧАСТЬ 3. Поведение Web-страниц. Web-сценарии

    ГЛАВА 14. Введение в Web-программирование. Язык JavaScript 

    Web-дизайн состоит из трех частей: содержимого, представления и поведения. Это мы узнали еще в главе 1. Содержимому была посвящена часть I, представлению — часть II. Теперь настала очередь обсудить поведение.

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

    Поведение создается с помощью так называемых Web-сценариев — программ, которые записывают прямо в HTML-коде Web-страниц или, что предпочтительнее, в отдельных файлах. Эти программы пишут на языке JavaScript. Web-обозреватель считывает JavaScript-код и последовательно выполняет записанные в нем выражения, проводя вычисления и выполняя на основе полученного результата заданные манипуляции над Web-страницей.

    Эта глава будет целиком посвящена принципам Web-программирования, языку JavaScript и средствам, предоставляемым Web-обозревателем для написания Web- сценариев. Она будет очень большой, так что приготовимся к долгому чтению и обилию новых терминов.

    Примеры Web-сценариев 

    Язык JavaScript лучше всего изучать, имея перед глазами пару хороших примеров. Поэтому давайте сразу создадим их.

    Простейший Web-сценарий

    Первый Web-сценарий, который мы напишем, будет совсем простым. Он выведет на Web-страницу текущую дату.

    В самом начале этой книги, приступив к изучению HTML, мы создали небольшую Web-страничку 1.1.htm. Найдем ее и откроем в Блокноте. В самом конце ее HTML- кода, перед закрывающим тегом </BODY>, вставим код листинга 14.1.

    Листинг 14.1

    <SCRIPT>

    var dNow = new Date();

    var sNow = dNow.toString();

    document.write(sNow);

    </SCRIPT>

    Это Web-сценарий. Мы поместили его прямо в HTML-код Web-страницы.

    Сохраним исправленную Web-страницу и откроем ее в Web-обозревателе. В самом низу мы увидим новый абзац, содержащий сегодняшнюю дату в "международном" формате.

    Наш первый Web-сценарий — поведение Web-страницы — работает!

    Теперь немного исправим его так, чтобы он выводил дату в привычном для нас формате <число>.<месяц>.<год> (листинг 14.2).

    Листинг 14.2

    <SCRIPT>

    var dNow = new Date();

    var sNow = dNow.getDate() +"." + dNow.getMonth() +"." + dNow.getFullYear();

    document.write(sNow);

    </SCRIPT>

    Обновим открытую в Web-обозревателе Web-страницу, нажав клавишу <F5>. Вот теперь дата отображается в привычном формате.

    Более сложный Web-сценарий

    Теперь сделаем что-нибудь посложнее — заставим пункты списков, формирующих полосу навигации на Web-странице index.htm, менять цвет рамки при наведении на них курсора мыши. Так мы дадим посетителю понять, что данные элементы Web-страницы могут реагировать на его действия.

    Сначала откроем таблицу стилей и внесем в ее CSS-код некоторые изменения. Прежде всего, исправим комбинированные стили #navbar LI и #navbar LI UL LI так, как показано в листинге 14.3.

    Листинг 14.3

    #navbar LI { padding: 5px 10px; margin: 10px 0px; border: thin solid #B1BEC6; cursor: pointer }

    .

    #navbar LI UL LI { font-size: 12pt; padding: 2px; margin: 0px 0px; border: thin solid #F8F8F8; cursor: pointer }

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

    Во-вторых, мы создали у пунктов вложенного списка тонкую сплошную рамку того же цвета, что и фон Web-страницы. Такая рамка будет невидима.

    Далее добавим в таблицу стилей вот такой стиль:

    hovered { border-color: #3B4043!important }

    Мы создали стилевой класс, задающий цвет рамки. Поскольку стилевой класс является менее конкретным, чем комбинированный стиль, мы сделали атрибут стиля, задающий цвет рамки, важным. (О важных атрибутах стиля см. главу 7.)

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

    После этого отправимся по интернет-адресу http://www.extjs.com/products/core/download.php?dl=extcore31 и загрузим оттуда архивный файл с именем вида ext-core-<номер версии>.zip — библиотеку Ext Core, упрощающую написание даже очень сложных Web-сценариев. (Подробнее речь о ней пойдет в главе 15.) Распакуем этот файл, найдем в распакованном содержимом файл ext-core.js и скопируем в корневую папку нашего Web-сайта, где хранится Web-страница index.htm и таблица стилей main.css. Теперь библиотека Ext Core готова к использованию.

    ВНИМАНИЕ!

    Если по указанному интернет-адресу архивный файл библиотеки Ext Core загрузить не удается, следует открыть Web-страницу http://www.extjs.com/products/core/download.php, найти на ней гиперссылку загрузки этого файла (на данный момент она имеет текст "Download") и щелкнуть на ней.

    Теперь откроем в Блокноте Web-страницу index.htm и поместим в ее секцию заголовка (тег <HEAD>) такой тег:

    <SCRIPT SRC="ext-core.js"></SCRIPT>

    А в самом конце HTML-кода этой Web-страницы, перед закрывающим тегом

    </BODY>, вставим такой тег:

    <SCRIPT SRC="main.js"></SCRIPT>

    Напоследок в корневой папке нашего Web-сайта создадим текстовый файл main.js и запишем в него содержимое листинга 14.4.

    Листинг 14.4

    Ext.onReady(function()

    {var ceLinks = Ext.select("UL[id=navbar] LI");

    ceLinks.on("mouseover", function(e, t) { Ext.get(this). addClass("hovered");});

    ceLinks.on("mouseout", function(e, t) { Ext.get(this). removeClass("hovered");});

    });

    Это тоже Web-сценарий. Но поместили его мы уже в отдельный файл main.js.

    Сохраним его, выбрав кодировку UTF-8 (как это сделать, было описано в главе 1).

    Все, поведение создано. Откроем Web-страницу index.htm в Web-обозревателе. Наведем курсор мыши на любой пункт полосы навигации и увидим, как вокруг него появится темная рамка.

    Как Web-сценарии помещаются в Web-страницу

    Как мы только что убедились, Web-сценарии, формирующие поведение Web- страницы, можно поместить как в саму Web-страницу, так и в отдельный файл. Рассмотрим подробнее, как это делается.

    Для вставки Web-сценария в HTML-код в любом случае применяется парный тег <SCRIPT>. Встретив его, Web-обозреватель поймет, что здесь присутствует Web-сценарий, который следует выполнить, а не выводить на экран.

    Мы можем поместить код Web-сценария прямо в тег <SCRIPT>, создав внутренний Web-сценарий (листинг 14.5). Собственно, мы уже сделали это, когда создавали наш первый Web-сценарий.

    Листинг 14.5

    <SCRIPT>

    var dNow = new Date();

    var sNow = dNow.getDate() +"." + dNow.getMonth() +"." +

    dNow.getFullYear();

    document.write(sNow);

    </SCRIPT>

    Внутренние Web-сценарии имеют одно неоспоримое преимущество. Поскольку они записаны прямо в коде Web-страницы, то являются ее неотъемлемой частью, и их невозможно "потерять". Однако внутренние Web-сценарии не соответствуют

    концепции Web 2.0, требующей, чтобы содержимое, представление и поведение Web-страницы были разделены. Поэтому сейчас их применяют довольно редко, практически только при экспериментах (как и внутренние таблицы стилей; подробнее — в главе 7).

    Мы можем поместить Web-сценарий и в отдельный файл — файл Web-сценария, — создав внешний Web-сценарий. (Наш второй Web-сценарий именно таков.) Файлы Web-сценария представляют собой обычные текстовые файлы, содержат только код Web-сценария без всяких тегов HTML и имеют расширение js.

    Для вставки в Web-страницу Web-сценария, хранящегося в отдельном файле, применяется тег <SCRIPT> такого вида:

    <SCRIPT SRC="<интернет-адрес файла Web-сценария>"></SCRIPT>

    Тег <SCRIPT> оставляют пустым, и в него помещают обязательный в данном случае атрибут SRC, в качестве значения которого указывают интернет-адрес нужного файла Web-сценария:

    <SCRIPT SRC="main.js"></SCRIPT>

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

    В нашем втором Web-сценарии мы использовали библиотеку Ext Core, значительно облегчающую труд Web-программиста. Во всех языках программирования библиотекой называется набор готовых языковых конструкций (функций и объектов, о которых речь пойдет потом), написанных сторонними программистами, чтобы облегчить труд их коллег. Так вот, все библиотеки для языка JavaScript, в том числе и Ext Core, реализованы в виде внешних Web-сценариев.

    И еще. Web-сценарий всегда выполняется в том месте HTML-кода Web-страницы, где присутствует. При этом не имеет значения, помещен он прямо в HTML-код или находится в отдельном файле.

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

    Язык программирования JavaScript 

    Настала пора рассмотреть язык программирования JavaScript. Ведь в Web-программировании без него никуда.

    Основные понятия JavaScript

    Давайте рассмотрим пример еще одного Web-сценария, совсем небольшого:

    x = 4;

    y = 5;

    z = x * y;

    Больше похоже на набор каких-то формул. Но это не формулы, а выражения языка JavaScript; каждое выражение представляет собой описание одного законченного действия, выполняемого Web-сценарием.

    Разберем приведенный Web-сценарий по выражениям. Вот первое из них:

    x = 4;

    Здесь мы видим число 4. В JavaScript такие числа, а также строки и прочие величины, значения которых никогда не изменяются, называются константами. В самом деле, значение числа 4 всегда равно четырем!

    Еще мы видим здесь латинскую букву x. А она что означает?

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

    Осталось выяснить, что делает символ равенства (=), поставленный между переменной и константой. А он здесь стоит не просто так! (Вообще, в коде любой программы, в том числе и Web-сценария, каждый символ что-то да значит.) Это оператор — команда, выполняющая определенные действия над данными Web- сценария. А если точнее, то символом = обозначается оператор присваивания. Он помещает значение, расположенное справа (операнд), в переменную, расположенную слева, в нашем случае — значение 4 в переменную x. Если же такой переменной еще нет, она будет создана.

    Каждое выражение JavaScript должно оканчиваться символом точки с запятой (;), обозначающим конец выражения; его отсутствие вызывает ошибку обработки Web- сценария.

    Рассмотрим следующее выражение:

    y = 5;

    Оно аналогично первому и присваивает переменной y константу 5. Подобные выражения часто называют математическими.

    Третье выражение стоит несколько особняком:

    z = x * y;

    Здесь мы видим все тот же оператор присваивания, присваивающий что-то переменной z. Но что? Результат вычисления произведения значений, хранящихся в переменных x и y. Вычисление произведения выполняет оператор умножения, который в JavaScript (и во многих других языках программирования) обозначается символом звездочки (*). Это арифметический оператор.

    В результате выполнения приведенного ранее Web-сценария в переменной z окажется произведение значений 4 и 5 — 20.

    Вот еще один пример математического выражения, на этот раз более сложного:

    y = y1 * y2 + x1 * x2;

    Оно вычисляется в следующем порядке:

    1. Значение переменной y1 умножается на значение переменной y2.

    2. Перемножаются значения переменных x1 и x2.

    3. Полученные на шагах 1 и 2 произведения складываются (оператор сложения обозначается привычным нам знаком +).

    4. Полученная сумма присваивается переменной y.

    Но почему на шаге 2 выполняется умножение x1 на x2, а не сложение произведения

    y1 и y2 с x1. Дело в том, что каждый оператор имеет приоритет — своего рода номер в очереди их выполнения. Так вот, оператор умножения имеет более высокий приоритет, чем оператор сложения, поэтому умножение всегда выполняется перед сложением.

    А вот еще одно выражение:

    x = x + 3;

    Оно абсолютно правильно с точки зрения JavaScript, хоть и выглядит нелепым. В нем сначала выполняется сложение значения переменной x и числа 3, после чего результат сложения снова присваивается переменной x. Такие выражения встречаются в Web-сценариях довольно часто.

    Типы данных JavaScript

    Любая программа при своей работе оперирует некими данными: именем стилевого класса, размерами элемента Web-страницы, цветом шрифта, величиной атмосферного давления и пр. Конечно, не составляют исключения и Web-сценарии — уже первый созданный нами Web-сценарий обрабатывал какие-то данные.

    JavaScript может манипулировать данными, относящимися к разным типам. Тип данных описывает их возможные значения и набор применимых к ним операций. Давайте перечислим все типы данных, с которыми мы можем столкнуться.

    Строковые данные (или строки) — это последовательности букв, цифр, пробелов, знаков препинания и прочих символов, заключенные в одинарные или двойные кавычки.

    Примеры строк:

    "JavaScript" "1234567"

    'Строковые данные — это последовательности символов.'

    Строки могут иметь любую длину (определяемую количеством составляющих их символов), ограниченную лишь объемом свободной памяти компьютера. Теоретически существует предел в 2 Гбайт, но вряд ли в нашей практике встретятся столь длинные строки.

    Кроме букв, цифр и знаков препинания, строки могут содержать специальные символы, служащие для особых целей (табл. 14.1).

    Таблица 14.1. Специальные символы, поддерживаемые JavaScript, и их коды

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

    "\"Этот фрагмент текста\" помещен в кавычки"

    Числовые данные (или числа) — это обычные числа, над которыми можно производить арифметические действия, извлекать из них квадратный корень и вычислять тригонометрические функции. Числа могут быть как целыми, так и дробными; в последнем случае целая и дробная части разделяются точкой (не запятой!).

    Примеры чисел:

    13756

    454.7873

    0.5635

    Дробные числа могут быть записаны в экспоненциальной форме:

    <мантисса>E<порядок>.

    Вот примеры чисел, заданных таким образом (в скобках дано традиционное математическое представление):

    1E-5 (10–5)

    8.546E23 (8,546 1023)

    Также имеется возможность записи целых чисел в восьмеричном и шестнадцатеричном виде. Восьмеричные числа записываются с нулем в начале (например, 047 или -012543624), а шестнадцатеричные — с символами 0x, также помещенными в начало (например, 0x35F). Отметим, что в JavaScript так можно записывать только целые числа.

    Логическая величина может принимать только два значения: true и false — "истина" и "ложь", — обозначаемые соответственно ключевыми словами true и false. (Ключевое слово — это слово, имеющее в языке программирования особое значение.) Логические величины используются, как правило, в условных выражениях (о них речь пойдет позже).

    JavaScript также поддерживает три специальных типа. Тип null может принимать единственное значение null и применяется в особых случаях. Тип NaN также может принимать единственное значение NaN и обозначает числовое значение, не являющееся числом (например, математическую бесконечность). Тип undefined указывает на то, что переменной не было присвоено никакое значение, и, опять же, принимает единственное значение undefined.

    ВНИМАНИЕ!

    undefined — это не то же самое, что null!

    Еще два типа данных, поддерживаемые JavaScript и не описанные здесь, мы рассмотрим позже.

    Переменные 

    В начале этой главы мы кое-что узнали о переменных. Сейчас настало время обсудить их детальнее.

    Именование переменных

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

    Прежде всего, в имени переменной могут присутствовать только латинские буквы, цифры и символы подчеркивания (_), причем первый символ имени должен быть либо буквой, либо символом подчеркивания. Например, pageAddress, _link, userName — правильные имена переменных, а 678vasya и Имя пользователя — неправильные.

    Язык JavaScript чувствителен к регистру символов, которыми набраны имена переменных. Это значит, что pageaddress и pageAddress — разные переменные.

    Совпадение имени переменной с ключевым словом языка JavaScript не допускается.

    Объявление переменных

    Перед использованием переменной в коде Web-сценария рекомендуется выполнить ее объявление. Для этого служит оператор объявления переменной var, после которого указывают имя переменной:

    var x;

    Теперь объявленной переменной можно присвоить какое-либо значение:

    x = 1234;

    и использовать в Web-сценарии:

    y = x * 2 + 10;

    Значение переменной также можно присвоить прямо при ее объявлении:

    var x = 1234;

    Также можно объявить сразу несколько переменных:

    var x, y, textColor = "black";

    Вообще, объявлять переменные с помощью оператора var не обязательно. Мы можем просто присвоить переменной какое-либо значение, и JavaScript сам ее создаст. Просто явное объявление переменных оператором var считается хорошим стилем программирования.

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

    ВНИМАНИЕ!

    Если обратиться к еще не созданной переменной, она вернет значение undefined.

    Пока закончим с переменными. (Впоследствии, при рассмотрении функций, мы к ним еще вернемся.) И займемся операторами JavaScript.

    Операторы 

    Операторов язык JavaScript поддерживает очень много — на все случаи жизни. Их можно разделить на несколько групп.

    Арифметические операторы

    Арифметические операторы служат для выполнения арифметических действий над числами. Все арифметические операторы, поддерживаемые JavaScript, перечислены в табл. 14.2.

    Таблица 14.2. Арифметические операторы

    Арифметические операторы делятся на две группы: унарные и бинарные. Унарные операторы выполняются над одним операндом; к ним относятся операторы смены знака, инкремента и декремента. Унарный оператор извлекает из переменной значение, изменяет его и снова помещает в ту же переменную. Приведем пример выражения с унарным оператором:

    ++r;

    При выполнении этого выражения в переменной r окажется ее значение, увеличенное на единицу. А если записать вот так:

    s = ++r;

    то значение r, увеличенное на единицу, будет помещено еще и в переменную s.

    Операторы инкремента и декремента можно ставить как перед операндом, так и после него. Если оператор инкремента стоит перед операндом, то значение операнда сначала увеличивается на единицу, а уже потом используется в дальнейших вычислениях. Если же оператор инкремента стоит после операнда, то его значение сначала вычисляется, а уже потом увеличивается на единицу. Точно так же ведет себя оператор декремента.

    Бинарные операторы всегда имеют два операнда и помещают результат в третью переменную. Вот примеры выражений с бинарными операторами:

    l = r * 3.14;

    f = e / 2;

    x = x + t / 3;

    Оператор объединения строк

    Оператор объединения строк + позволяет соединить две строки в одну. Например, сценарий:

    s1 = "Java";

    s2 = "Script";

    s = s1 + s2;

    поместит в переменную s строку JavaScript.

    Операторы присваивания

    Оператор присваивания = нам уже знаком. Его еще называют оператором простого присваивания, поскольку он просто присваивает переменной новое значение:

    a = 2;

    b = c = 3;

    Второе выражение в приведенном примере выполняет присвоение значения 3 сразу двум переменным — b и c.

    JavaScript также поддерживает операторы сложного присваивания, позволяющие выполнять присваивание одновременно с другой операцией:

    a = a + b;

    a += b;

    Два этих выражения эквивалентны по результату. Просто во втором указан оператор сложного присваивания +=.

    Все операторы сложного присваивания, поддерживаемые JavaScript, и их эквиваленты приведены в табл. 14.3.

    Таблица 14.3. Операторы сложного присваивания


    Операторы сравнения

    Операторы сравнения сравнивают два операнда согласно определенному условию и выдают (или, как говорят программисты, возвращают) логическое значение. Если условие сравнения выполняется, возвращается значение true, если не выполняется — false.

    Все поддерживаемые JavaScript операторы сравнения приведены в табл. 14.4.

    Пример:

    a1 = 2 < 3;

    a2 = -4 > 0;

    a3 = r < t;


    Переменная a1 получит значение true (2 меньше 3), переменная a2 — значение false (число –4 по определению не может быть больше нуля), а значение переменной a3 будет зависеть от значений переменных r и t.

    Можно сравнивать не только числа, но и строки:

    a = "JavaScript"!= "Java";

    Переменная a получит значение true, т. к. строки "JavaScript" и "Java" не равны.

    На двух последних операторах из табл. 14.4 — "строго равно" и "строго не равно" — нужно остановиться подробнее. Это операторы так называемого строгого сравнения. Обычные операторы "равно" и "не равно", если встречают операнды разных типов, пытаются преобразовать их к одному типу (о преобразованиях типов см. далее в этой главе). Операторы строгого равенства и строгого неравенства такого преобразования не делают, а в случае несовпадения типов операндов всегда возвращают false.

    Пример:

    a1 = 2 == "2";

    a2 = 2 === "2";

    Переменная a1 получит значение true, т. к. в процессе вычисления строка "2" будет преобразована в число 2, и условие сравнение выполнится. Но переменная a2 получит значение false, т. к. сравниваемые операнды принадлежат разным типам.

    Логические операторы

    Логические операторы выполняют действия над логическими значениями. Все они приведены в табл. 14.5. А в табл. 14.6 и 14.7 показаны результаты выполнения этих операторов.

    Основная область применения логических операторов — выражения сравнения (о них см. далее в этой главе). Приведем примеры таких выражений:

    a = (b > 0) && (c + 1!= d);

    flag =!(status = 0);

    Таблица 14.5. Логические операторы

    Таблица 14.6. Результаты выполнения операторов И и ИЛИ

    Таблица 14.7. Результаты выполнения оператора НЕ


    Оператор получения типа typeof

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

    s = typeof("str");

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

    Все значения, которые может вернуть оператор typeof, перечислены в табл. 14.8.

    Таблица 14.8. Значения, возвращаемые оператором typeof

    Совместимость и преобразование типов данных

    Настала пора рассмотреть еще два важных вопроса: совместимость типов данных и преобразование одного типа к другому.

    Что получится, если сложить два числовых значения? Правильно — еще одно числовое значение. А если сложить число и строку? Трудно сказать… Тут JavaScript сталкивается с проблемой несовместимости типов данных и пытается сделать эти типы совместимыми, преобразуя один из них к другому. Сначала он пытается преобразовать строку в число и, если это удается, выполняет сложение. В случае неудачи число будет преобразовано в строку, и две полученные строки будут объединены. Например, в результате выполнения Web-сценария из листинга 14.6 значение переменной b при сложении с переменной a будет преобразовано в числовой тип; таким образом, переменная c будет содержать значение 23.

    Листинг 14.6

    var a, b, c, d, e, f;

    a = 11;

    b = "12";

    c = a + b;

    d = "JavaScript";

    e = 2;

    f = d + e;

    Но поскольку значение переменной d нельзя преобразовать в число, значение e будет преобразовано в строку, и результат — значение f — станет равным "JavaScript2".

    Логические величины преобразуются либо в числовые, либо в строковые, в зависимости от конкретного случая. Значение true будет преобразовано в число 1 или строку "1", а значение false — в 0 или "0". И наоборот, число 1 будет преобразовано в значение true, а число 0 — в значение false. Также в false будут преобразованы значения null и undefined.

    Видно, что JavaScript изо всех сил пытается правильно выполнить даже некорректно написанные выражения. Иногда это получается, но чаще все работает не так, как планировалось, и, в конце концов, выполнение Web-сценария прерывается в связи с обнаружением ошибки совсем в другом его месте, на абсолютно верном операторе. Поэтому лучше не допускать подобных казусов.

    Приоритет операторов

    Последний вопрос, который мы здесь разберем, — приоритет операторов. Как мы помним, приоритет влияет на порядок, в котором выполняются операторы в выражении.

    Пусть имеется следующее выражение:

    a = b + c — 10;

    В этом случае сначала к значению переменной b будет прибавлено значение c, а потом из суммы будет вычтено 10. Операторы этого выражения имеют одинаковый приоритет и поэтому выполняются строго слева направо.

    Теперь рассмотрим такое выражение:

    a = b + c * 10;

    Здесь сначала будет выполнено умножение значения c на 10, а уже потом к полученному произведению будет прибавлено значение b. Оператор умножения имеет больший приоритет, чем оператор сложения, поэтому порядок "строго слева направо" будет нарушен.

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

    В общем, основной принцип выполнения всех операторов таков: сначала выполняются операторы с более высоким приоритетом, а уже потом — операторы с более низким. Операторы с одинаковым приоритетом выполняются в порядке их следования (слева направо).

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

    Таблица 14.9. Приоритет операторов (в порядке убывания)

    ВНИМАНИЕ!

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

    Но что делать, если нам нужно нарушить обычный порядок выполнения операторов? Воспользуемся скобками. При такой записи заключенные в скобки операторы выполняются первыми:

    a = (b + c) * 10;

    Здесь сначала будет выполнено сложение значений переменных b и c, а потом получившаяся сумма будет умножена на 10.

    Операторы, заключенные в скобки, также подчиняются приоритету. Поэтому часто используются многократно вложенные скобки:

    a = ((b + c) * 10 — d) / 2 + 9;

    Здесь операторы будут выполнены в такой последовательности:

    1. Сложение b и c.

    2. Умножение полученной суммы на 10.

    3. Вычитание d из произведения.

    4. Деление разности на 2.

    5. Прибавление 9 к частному.

    Если удалить скобки:

    a = b + c * 10 — d / 2 + 9;

    то порядок выполнения операторов будет таким:

    1. Умножение c и 10.

    2. Деление d на 2.

    3. Сложение b и произведения c и 10.

    4. Вычитание из полученной суммы частного от деления d на 2.

    5. Прибавление 9 к полученной разности.

    Получается совсем другой результат, не так ли?

    Сложные выражения JavaScript 

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

    Блоки

    JavaScript позволяет нам объединить несколько выражений в одно. Такое выражение называется блочным выражением или просто блоком. Составляющие его выражения заключают в фигурные скобки, например:

    {

    b = "12";

    c = a — b;

    }

    Как правило, блоки не существуют сами по себе. Чаще всего они входят в состав других сложных выражений.

    Условные выражения

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

    Листинг 14.7 иллюстрирует формат условного выражения.

    Листинг 14.7

    if (<условие>)

    <блок "то">

    else

    <блок "иначе">

    Существует также другая, "вырожденная" разновидность условного выражения,

    содержащая только одно выражение, которое выполняется при выполнении условия и пропускается, если условие не выполнено:

    if (<условие>)

    <блок "то">

    Для написания условных выражений предусмотрены особые ключевые слова if и

    else. Отметим, что условие всегда записывают в круглых скобках.

    Если условие имеет значение true, то выполняется блок "то". Если же условие имеет значение false, то выполняется блок "иначе" (если он присутствует в условном выражении). А если блок "иначе" отсутствует, выполняется следующее выражение Web-сценария.

    ВНИМАНИЕ!

    Значения null или undefined преобразуются в false. Не забываем об этом.

    Рассмотрим несколько примеров.

    В листинге 14.8 мы сравниваем значение переменной x с единицей и в зависимости от результатов сравнения присваиваем переменным f и h разные значения.

    Листинг 14.8

    if (x == 1) {

    a = "Единица";

    b = 1;

    }

    else {

    a = "Не единица";

    b = 22222;

    }

    Условие может быть довольно сложным (листинг 14.9).

    Листинг 14.9

    if ((x == 1) && (y > 10))

    f = 3;

    else

    f = 33;

    Здесь мы использовали сложное условие, возвращающее значение true в случае,

    если значение переменной x равно 1 и значение переменной y больше 10. Заметим также, что мы подставили одиночные выражения, т. к. фрагменты кода слишком просты, чтобы оформлять их в виде блоков.

    Условный оператор?

    Если условное выражение совсем простое, мы можем записать его немного по-другому. А именно, воспользоваться условным оператором ?:

    <условие>? <выражение "то">: <выражение "иначе">;

    Достоинство этого оператора в том, что он может быть частью выражения. Например:

    f = (x == 1 && y > 10)? 3: 33;

    Фактически мы записали условное выражение из предыдущего примера, но в виде простого выражения. Компактность кода налицо. Недостаток же оператора? в том, что с его помощью можно записывать только самые простые условные выражения.

    Приоритет условного оператора один из самых низких. Приоритет ниже него имеют только операторы присваивания.

    Выражения выбора

    Выражение выбора — это фактически несколько условных выражений, объединенных в одном. Его формат иллюстрирует листинг 14.10.

    Листинг 14.10

    switch (<исходное выражение>) {

    case <значение 1> :

    <блок 1>

    [break;]

    [case <значение 2> :

    <блок 2>

    [break;]]

    <… другие секции case>

    [default:

    <блок, исполняемый для остальных значений>]

    }

    В выражениях выбора присутствуют ключевые слова switch, case и default.

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

    Листинг 14.11 иллюстрирует пример выражения выбора.

    Листинг 14.11

    switch (a) {

    case 1:

    out = "Единица";

    break;

    case 2:

    out = "Двойка";

    break;

    case 3:

    out = "Тройка";

    break;

    default:

    out = "Другое число";

    }

    Здесь, если переменная a содержит значение 1, переменная out получит значение "Единица", если 2 — значение "Двойка", а если 3 — значение "Тройка". Если же переменная a содержит какое-то другое значение, переменная out получит значение "Другое число".

    Циклы 

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

    Цикл со счетчиком

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

    Цикл со счетчиком записывается так:

    for (<выражение инициализации>; <условие>; <приращение>)

    <тело цикла>

    Здесь используется ключевое слово for. Поэтому такие циклы часто называют "циклами for".

    Выражение инициализации выполняется самым первым и всего один раз. Оно присваивает особой переменной, называемой счетчиком цикла, некое начальное значение (обычно 1). Счетчик цикла подсчитывает, сколько раз было выполнено тело цикла — собственно код, который нужно выполнить определенное количество раз.

    Следующий шаг — проверка условия. Оно определяет момент, когда выполнение цикла прервется и начнет выполняться следующий за ним код. Как правило, условие сравнивает значение счетчика цикла с его граничным значением. Если условие возвращает true, выполняется тело цикла, в противном случае цикл завершается и начинается выполнение кода, следующего за циклом.

    После прохода тела цикла выполняется выражение приращения, изменяющее значение счетчика. Это выражение обычно инкрементирует счетчик (увеличивает его значение на единицу). Далее снова проверяется условие, выполняется тело цикла, приращение и т. д., пока условие не станет равно false.

    Пример цикла со счетчиком:

    for (i = 1; i < 11; i++) {

    a += 3;

    b = i * 2 + 1;

    }

    Этот цикл будет выполнен 10 раз. Мы присваиваем счетчику i начальное значение 1 и после каждого выполнения тела цикла увеличиваем его на единицу. Цикл перестанет выполняться, когда значение счетчика увеличится до 11, и условие цикла станет ложным.

    Счетчик цикла можно записать в одном из выражений тела цикла, как это сделали

    мы. В нашем случае счетчик i будет содержать последовательно возрастающие значения от 1 до 10, которые используются в вычислениях.

    Приведем еще два примера цикла со счетчиком:

    for (i = 10; i > 0; i-) {

    a += 3;

    b = i * 2 + 1;

    }

    Здесь значение счетчика декрементируется. Начальное его значение равно 10. Цикл выполнится 10 раз и завершится, когда счетчик i будет содержать 0; при этом значения последнего будут последовательно уменьшаться от 10 до 1.

    for (i = 2; i < 21; i += 2) b = i * 2 + 1;

    А в этом примере начальное значение счетчика равно 2, а конечное — 21, но цикл выполнится, опять же, 10 раз. А все потому, что значение счетчика увеличивается на 2 и последовательно принимает значения 2, 4, 6… 20.

    Цикл с постусловием

    Цикл с постусловием во многом похож на цикл со счетчиком: он выполняется до тех пор, пока остается истинным условие цикла. Причем условие проверяется не до, а после выполнения тела цикла, отчего цикл с постусловием и получил свое название. Такой цикл выполнится хотя бы один раз, даже если его условие с самого начала ложно.

    Формат цикла с постусловием:

    do

    <тело цикла>

    while (<условие>);

    Для задания цикла с постусловием предусмотрены ключевые слова do и while, поэтому такие циклы часто называют "циклами do-while".

    Вот пример цикла с постусловием:

    do {

    a = a * i + 2;

    ++i;

    } while (a < 100);

    А вот еще один пример:

    var a = 0, i = 1;

    do {

    a = a * i + 2;

    ++i;

    } while (i < 20);

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

    Цикл с предусловием

    Цикл с предусловием отличается от цикла с постусловием тем, что условие проверяется перед выполнением тела цикла. Так что, если оно (условие) изначально ложно, цикл не выполнится ни разу:

    while (<условие>)

    <тело цикла>

    Для создания цикла с постусловием предусмотрено ключевое слово while. Поэтому такие циклы называют еще "циклами while" (не путать с "циклами do-while"!).

    Пример цикла с предусловием:

    while (a < 100) {

    a = a * i + 2;

    ++i;

    }

    Прерывание и перезапуск цикла

    Иногда бывает нужно прервать выполнение цикла. Для этого JavaScript предоставляет Web-программистам операторы break и continue.

    Оператор прерывания break позволяет прервать выполнение цикла и перейти к следующему за ним выражению:

    while (a < 100) {

    a = a * i + 2;

    if (a > 50) break;

    ++i;

    }

    В этом примере мы прерываем выполнение цикла, если значение переменной a превысит 50.

    Оператор перезапуска continue позволяет перезапустить цикл, т. е. оставить невыполненными все последующие выражения, входящие в тело цикла, и запустить выполнение цикла с самого его начала: проверка условия, выполнение приращения и тела и т. д.

    Пример:

    while (a < 100) {

    i = ++i;

    if (i > 9 && i < 11) continue;

    a = a * i + 2;

    }

    Здесь мы пропускаем выражение, вычисляющее a, для всех значений i от 10 до 20.

    Функции 

    Функция — это особым образом написанный и оформленный фрагмент кода JavaScript, который можно вызвать из любого Web-сценария на данной Web-странице (повторно используемый код, как его часто называют). Так что, если какой-то фрагмент кода встречается в нескольких местах нашего Web-сценария, лучше всего оформить его в виде функции.

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

    Объявление функций

    Прежде чем функция будет использована где-то в Web-сценарии, ее нужно объявить. Функцию объявляют с помощью ключевого слова function:

    function <имя функции>([<список параметров, разделенных запятыми>])

    <тело функции>

    Имя функции, как уже говорилось, должно быть уникально в пределах Web- страницы. Для имен функций действуют те же правила, что и для имен переменных.

    Список параметров представляет собой набор переменных, в которые при вызове функции будут помещены значения переданных ей параметров. (О вызове функций будет рассказано далее.) Мы можем придумать для этих переменных любые имена — все равно они будут использованы только внутри тела функции. Это так называемые формальные параметры функции.

    Список параметров функции помещают в круглые скобки и ставят после ее имени; сами параметры отделяют друг от друга запятыми. Если функция не требует параметров, следует указать пустые скобки — это обязательно.

    В пределах тела функции над принятыми ею параметрами (если они есть) и другими данными выполняются некоторые действия и, возможно, вырабатывается результат. Оператор возврата return возвращает результат из функции в выражение, из которого она была вызвана:

    return <переменная или выражение>;

    Здесь переменная должна содержать возвращаемое значение, а выражение должно его вычислять.

    Пример объявления функции:

    function divide(a, b) {

    var c;

    c = a / b;

    return c;

    }

    Данная функция принимает два параметра — a и b, — после чего делит a на b и возвращает частное от этого деления.

    Эту функцию можно записать компактнее:

    function divide(a, b) {

    return a / b;

    }

    Или даже так, в одну строку:

    function divide(a, b) { return a / b; }

    JavaScript позволяет нам создавать так называемые необязательные параметры функций — параметры, которые при вызове можно не указывать, после чего они примут некоторое значение по умолчанию. Вот пример функции с необязательным параметром b, имеющим значение по умолчанию 2:

    function divide(a, b) {

    if (typeof(b) == "undefined") b = 2;

    return a / b;

    }

    Понятно, что мы должны как-то выяснить, был ли при вызове функции указан параметр b. Для этого мы используем оператор получения типа typeof (он был описан ранее). Если параметр b не был указан, данный оператор вернет строку "undefined"; тогда мы создадим переменную с именем b, как и у необязательного параметра, и присвоим ей число 2 — значение этого параметра по умолчанию, — которое и будет использовано в теле функции. Если возвращенное оператором значение иное, значит, параметр b был указан при вызове, и мы используем значение, которое было передано с ним.

    Функции и переменные. Локальные переменные

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

    Разумеется, любая функция может обращаться к любой переменной, объявленной вне ее тела (переменной уровня Web-страницы). При этом нужно помнить об одной вещи. Если существуют две переменные с одинаковыми именами, одна — уровня Web-страницы, другая — локальная, то при обращении по этому имени будет получен доступ к локальной переменной. Одноименная переменная уровня страницы будет "замаскирована" своей локальной "тезкой".

    Вызов функций

    После объявления функции ее можно вызвать из любого Web-сценария, присутствующего на этой же Web-странице. Формат вызова функции:

    <имя функции>([<список фактических параметров, разделенных запятыми>])

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

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

    ВНИМАНИЕ!

    При вызове функции подставляйте именно фактические параметры, а не формальные, указанные в объявлении функции.

    Вот пример вызова объявленной нами ранее функции divide:

    d = divide(3, 2);

    Здесь мы подставили в выражение вызова функции фактические параметры — константы 3 и 2.

    А здесь мы выполняем вызов функции с переменными в качестве фактических параметров:

    s = 4 * divide(x, r) + y;

    Если функция имеет необязательные параметры и нас удовлетворяют их значения по умолчанию, мы можем при вызове не указывать эти параметры, все или некоторые из них. Например, функцию divide со вторым необязательным параметром мы можем вызвать так:

    s = divide(4);

    Тогда в переменной s окажется число 2 — результат деления 4 (значение первого параметра) на 2 (значение второго, необязательного, параметра по умолчанию).

    Если функция не возвращает результат, то ее вызывают так:

    initVars(1, 2, 3, 6);

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

    Если функция не принимает параметров, при ее вызове все равно нужно указать пустые скобки, иначе возникнет ошибка выполнения Web-сценария:

    s = computeValue();

    Функции могут вызывать друг друга. Вот пример:

    function cmp(c, d, e) {

    var f;

    f = divide(c, d) + e;

    return f;

    }

    Здесь мы использовали в функции cmp вызов объявленной ранее функции divide.

    Присваивание функций. Функциональный тип данных

    JavaScript позволяет выполнять над функциями один фокус — присваивать функции переменным.

    Пример:

    var someFunc;

    someFunc = cmp;

    Здесь мы присвоили переменной someFunc объявленную ранее функцию cmp. Заметим, что в этом случае справа от оператора присваивания указывают только имя функции без скобок и параметров.

    Впоследствии мы можем вызывать данную функцию, обратившись к переменной, которой она была присвоена:

    c = someFunc(1, 2, 3);

    Здесь мы вызвали функцию cmp через переменную someFunc.

    Переменная, которой была присвоена функция, хранит данные, относящиеся к функциональному типу. Это еще один тип данных, поддерживаемый JavaScript.

    А можно сделать и так:

    var someFunc = function(c, d, e) {

    var f;

    f = divide(c, d) + e;

    return f;

    }

    Здесь мы объявили функцию и сразу же присвоили ее переменной. Как видим, имени объявляемой функции мы не указали — в данном случае оно не нужно.

    А еще JavaScript позволяет нам указать функцию в качестве параметра другой функции. Для примера давайте рассмотрим фрагмент JavaScript-кода нашего второго Web-сценария:

    ceLinks.on("mouseover", function(e, t) { Ext.get(t). addClass("hovered");

    });

    Здесь второй параметр функции on (вообще-то, это не функция, а метод объекта, но об этом потом) — другая функция, объявленная там же. Эта функция принимает два параметра и содержит одно выражение.

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

    Массивы

    Массив — это пронумерованный набор переменных (элементов), фактически хранящийся в одной переменной. Доступ к отдельному элементу массива выполняется по его порядковому номеру, называемому индексом. А общее число элементов массива называется его размером.

    ВНИМАНИЕ!

    Нумерация элементов массива начинается с нуля.

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

    var someArray = [1, 2, 3, 4];

    Здесь мы создали массив, содержащий четыре элемента, и присвоили его переменной someArray. После этого мы можем получить доступ к любому из элементов по его индексу, указав его после имени переменной массива в квадратных скобках:

    a = massive[2];

    В данном примере мы получили доступ к третьему элементу массива. (Нумерация элементов массива начинается с нуля — помните об этом!)

    Определять сразу все элементы массива необязательно:

    someArray2 = [1, 2, 4];

    Здесь мы пропустили третий элемент массива, и он остался неопределенным (т. е. будет содержать значение undefined).

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

    someArray[4] = 9;

    При этом будет создан новый, пятый по счету, элемент массива с индексом 4 и значением 9.

    Можно даже сделать так:

    someArray[7] = 9;

    В этом случае будут созданы четыре новых элемента, и восьмой элемент получит значение 9. Пятый, шестой и седьмой останутся неопределенными (undefined).

    Мы можем присвоить любому элементу массива другой массив (или, как говорят опытные программисты, создать вложенный массив):

    someArray[2] = ["n1", "n2", "n3"];

    После этого можно получить доступ к любому элементу вложенного массива, указав последовательно оба индекса: сначала — индекс во "внешнем" массиве, потом — индекс во вложенном:

    str = someArray[2][1];

    Переменная str получит в качестве значения строку, содержащуюся во втором элементе вложенного массива, — n2.

    Ранее говорилось, что доступ к элементам массива выполняется по числовому индексу. Но JavaScript позволяет создавать и массивы, элементы которых имеют строковые индексы (ассоциативные массивы, или хэши).

    Пример:

    var hash;

    hash["AUDIO"] = "t_audio.htm"; hash["IMG"] = "t_img.htm"; hash["TITLE"] = "t_title.htm";

    JavaScript также позволяет нам создать массив, вообще не содержащий элементов (пустой массив). Для этого достаточно присвоить любой переменной "пустые" квадратные скобки:

    var someArray;

    someArray = [];

    Разумеется, впоследствии мы можем и даже должны наполнить этот массив элементами:

    someArray[0] = 1; someArray[1] = 2; someArray[2] = 3;

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

    Ссылки

    Осталось рассмотреть еще один момент, связанный с организацией доступа к данным. Это так называемые ссылки — своего рода указатели на массивы и экземпляры объектов (о них будет рассказано далее), хранящиеся в соответствующих им переменных.

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

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

    Рассмотрим такой пример:

    var myArray = ["AUDIO", "IMG", "TITLE"];

    var newArray = myArray;

    Здесь создается массив myArray с тремя элементами и далее он присваивается переменной newArray (при этом данная переменная получает ссылку на массив). Если потом мы присвоим новое значение первому элементу массива myArray:

    myArray[0] = "VIDEO";

    и обратимся к нему через переменную newArray:

    s = newArray[0];

    то в переменной s окажется строка "VIDEO" — новое значение первого элемента этого массива. Фактически переменные myArray и newArray указывают на один и тот же массив.

    ВНИМАНИЕ!

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

    Переменная, хранящая ссылку на массив (и экземпляр объекта), содержит данные объектного типа. Это последний тип данных, поддерживаемый JavaScript, который мы здесь рассмотрим.

    НА ЗАМЕТКУ

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

    Объекты 

    Итак, мы познакомились с типами данных, переменными, константами, операторами, простыми и сложными выражениями, функциями и массивами. Но это была, так сказать, присказка, а сказка будет впереди. Настала пора узнать о самых сложных структурах данных JavaScript — объектах.

    Понятия объекта и экземпляра объекта

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

    Это были так называемые простые типы данных. Сущность, относящаяся к такому типу, может хранить только одно значение.

    Однако JavaScript предоставляет нам и сложные типы данных. Сущность такого типа может хранить сразу несколько значений. Один из примеров сложного типа данных — уже знакомые нам массивы.

    Другой пример сложного типа данных — объекты. Объект — это сложная сущность, способная хранить сразу несколько значений разных типов. Для этого объект определяет набор своего рода внутренних переменных, называемых свойствами; такое свойство может хранить одну сущность, относящуюся к простому или сложному типу данных. Так, одно свойство объекта может хранить строки, другое — числа, третье — массивы.

    А еще объект может содержать набор внутренних функций, называемых методами. Методы могут обрабатывать данные, хранящиеся в свойствах или полученные "извне", менять значения свойств и возвращать результат, как обычные функции.

    Объект — это всего лишь тип данных. Сущности же, хранящие реальные данные и созданные на основе этого объекта, называются его экземплярами. Точно так же, как строка "JavaScript" — экземпляр строкового типа данных, хранящий реальную строку.

    Экземпляры объектов имеют такую же природу, как и массивы. Сам экземпляр объекта находится где-то в памяти компьютера, а в переменной хранится ссылка на него. При присваивании ее значения другой переменной выполняется присваивание именно ссылки. Не забываем об этом.

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

    Объекты — невероятно мощное средство объединить данные (свойства) и средства их обработки (методы) воедино. Так, объект, представляющий абзац Web- страницы, объединяет параметры этого абзаца, хранящиеся в различных свойствах, и инструменты для манипуляции абзацем, предоставляемые соответствующими методами. Нам не придется "раскидывать" параметры абзаца по десяткам переменных и пользоваться для работы с ним массой отдельных функций — все это сведено в один объект.

    Экземпляры одного объекта — отдельные сущности, не влияющие друг на друга. Мы можем работать с одним экземпляром объекта, а другие экземпляры того же самого объекта останутся неизменными. Так, мы можем изменить параметры одного абзаца, присвоив новые значения свойствам соответствующего экземпляра объекта, не затрагивая другие абзацы на этой же Web-странице.

    Все объекты, доступные в Web-сценариях, можно разделить на три разновидности:

    — предоставляемые самим языком JavaScript (встроенные объекты);

    — предоставляемые Web-обозревателем (объекты Web-обозревателя);

    — созданные нами или сторонним разработчиком на самом JavaScript (пользовательские объекты). В частности, популярные JavaScript-библиотеки, такие как Ext Core, создают множество пользовательских объектов.

    С точки зрения Web-программиста все эти объекты одинаковы. И работа ними и их экземплярами выполняется сходным образом.

    Получение экземпляра объекта

    Но как нам получить экземпляр нужного объекта?

    — Экземпляры многих объектов создаются самим языком JavaScript, Web-обозревателем или библиотеками сторонних разработчиков.

    — Экземпляры некоторых объектов возвращаются функциями или методами других объектов.

    — Мы можем создать экземпляр объекта сами, написав соответствующее выражение.

    Давайте рассмотрим все эти случаи подробно.

    Прежде всего, сам язык JavaScript предоставляет нам несколько экземпляров различных объектов. Они хранятся в особых переменных, также создаваемых самим языком.

    ВНИМАНИЕ!

    Вообще-то, в переменных хранятся ссылки на экземпляры объектов. Просто раньше мы договорились, что будем считать, будто в переменных хранятся сами экземпляры объектов, — так проще.

    Так, переменная Math хранит экземпляр одноименного объекта, поддерживающего множество методов для выполнения математических и тригонометрических вычислений над числами:

    var a = Math.sqrt(2);

    Это выражение поместит в переменную a квадратный корень из 2. Метод sqrt объекта Math как раз вычисляет квадратный корень из числа, переданного ему в качестве единственного параметра.

    А метод sin объекта Math вычисляет синус угла, заданного в радианах и переданного данному методу единственным параметром:

    var b = Math.sin(0.1);

    Как видим, мы просто используем переменную Math, созданную языком JavaScript, чтобы получить доступ к экземпляру объекта Math и его методам.

    Web-обозреватель также предоставляет множество экземпляров объектов, представляющих текущую Web-страницу, различные ее элементы и сам Web- обозреватель. Так, Web-страницу представляет экземпляр объекта HTMLDocument, который хранится в переменной document, также созданной Web-обозревателем.

    В частности, объект HTMLDocument поддерживает метод write, выводящий переданную ему в качестве единственного параметра строку в то место Web-страницы, где встретился вызов этого метода:

    document.write("Привет, посетитель!");

    И в этом случае мы просто используем переменную document, созданную Web- обозревателем, чтобы получить доступ к экземпляру объекта HTMLDocument и его методу write.

    Сторонние библиотеки также создают множество экземпляров объектов сами. Так, библиотека Ext Core создает экземпляр объекта Ext, хранящийся в одноименной переменной:

    var ceLinks = Ext.select("UL[id=navbar] > LI");

    Здесь мы обратились к методу select объекта Ext. Данный метод возвращает массив экземпляров объектов Element, каждый из которых представляет элемент Web-страницы, удовлетворяющий переданному ему в качестве единственного параметра специальному селектору CSS. (Кстати, объект Element также определен библиотекой Ext Core.)

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

    var elNavbar = Ext.get("navbar");

    Метод get объекта Ext, созданного библиотекой Ext Core, возвращает экземпляр объекта Element, представляющий определенный элемент Web-страницы. В данном случае это будет элемент, значение атрибута тега ID которого равно "navbar" — именно такую строку мы передали методу get в качестве параметра.

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

    И, наконец, с помощью оператора создания экземпляра new мы можем создать экземпляр нужного нам объекта сами, явно:

    new <имя объекта>([<список параметров, разделенных запятыми>])

    Оператор new возвращает созданный экземпляр объекта. Его можно присвоить какой-либо переменной или свойству или передать в качестве параметра функции или методу.

    Список параметров может как присутствовать, так и отсутствовать. Обычно он содержит значения, которые присваиваются свойствам экземпляра объекта при его создании.

    Язык JavaScript предоставляет нам объект Date, хранящий значение даты и времени. Его экземпляры можно создать только явно, оператором new:

    var dNow = new Date();

    После выполнения этого выражения в переменной dNow окажется экземпляр объекта Date, хранящий сегодняшнюю дату и текущее время.

    А вот выражение, которое поместит в переменную dNewYear экземпляр объекта

    Date, хранящий дату 31 декабря 2009 года и текущее время:

    var dNewYear = new Date(2009, 12, 31);

    Но какой способ получения экземпляра объекта в каком случае применять? Выбор зависит от объекта, экземпляр которого мы хотим получить.

    — Экземпляры объектов Web-обозревателя, представляющие Web-страницу и сам Web-обозреватель, доступны через соответствующие переменные.

    — Экземпляры объектов Web-обозревателя, представляющие различные элементы Web-страницы, получаются в результате выполнения особой функции или метода.

    — Экземпляры всех объектов языка JavaScript, кроме Math, получаются явным созданием с помощью оператора new.

    — Экземпляр объекта Math языка JavaScript доступен через одноименную переменную.

    Это самые общие правила. А конкретные приемы получения нужного экземпляра объекта мы рассмотрим потом, когда начнем заниматься Web-программированием вплотную.

    Работа с экземпляром объекта

    Получив тем или иным способом экземпляр объекта, мы можем с ним работать: вызывать его методы и получать доступ к его свойствам.

    Ранее мы уже познакомились с методами различных объектов и выяснили, как они вызываются. Для этого к переменной, хранящей экземпляр объекта, добавляется справа точка, а после нее записывается вызов метода. Метод — суть "собственная" функция объекта, и вызывается она так же (см. раздел, посвященный вызовам функций).

    Пример:

    var elNavbar = Ext.get("navbar");

    Здесь мы вызвали метод get объекта Ext, экземпляр которого хранится в переменной Ext, передав данному методу параметр — строку "navbar". Как мы уже знаем, метод get ищет на Web-странице элемент, имеющий значение атрибута тега ID, которое совпадает с переданной ему в качестве параметра строкой. Возвращенный этим методом результат — экземпляр объекта Element, представляющего список navbar, — будет присвоен переменной elNavbar.

    Пример:

    var elParent = elNavbar.parent();

    А здесь мы вызвали метод parent у экземпляра объекта Element, хранящегося в переменной elNavbar и полученного в предыдущем выражении. Возвращенный этим методом результат — экземпляр объекта Element, представляющего элемент Web- страницы, который является родителем списка navbar, — будет присвоен переменной elParent.

    Если результат, возвращаемый каким-либо методом (назовем его методом 1), представляет собой объект, мы можем сразу вызвать метод у него (это будет метод 2). Для этого мы добавим справа от вызова метода 1 точку, после которой поставим вызов метода 2.

    Пример:

    elNavbar.parent(). addClass("hovered");

    Здесь мы сначала вызвали метод parent у экземпляра объекта Element, хранящегося в переменной elNavbar (метод 1). У полученного в результате вызова метода 1 результата — экземпляра объекта Element, представляющего элемент-родитель, — мы вызвали метод addClass (метод 2).

    Такие цепочки последовательных вызовов методов, когда стоящий справа метод

    вызывается у экземпляра, возвращенного методом, стоящим слева, встречаются в JavaScript-коде очень часто.

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

    Пример:

    var sID = elParent.id;

    В данном примере мы обратились к свойству id объекта Element, экземпляр которого хранится в переменной elParent. Это свойство хранит значение атрибута тега ID у соответствующего элемента Web-страницы.

    Пример:

    Ext.enableFx = false;

    Здесь мы присвоили новое значение свойству enableFx объекта Ext, экземпляр которого хранится в переменной Ext.

    Пример:

    var sID = elNavbar.parent(). id;

    А здесь мы сначала получили родитель элемента Web-страницы, хранящегося в переменной elNavbar, после чего извлекли значение атрибута тега ID уже у полученного родителя.

    Что ж, с объектами и их экземплярами, свойствами и методами мы познакомились. Теперь давайте кратко "пробежимся" по объектам, которые будем использовать при написании Web-сценариев. Рассмотрим только встроенные объекты JavaScript и объекты Web-обозревателя; пользовательским объектам, создаваемым библиотекой Ext Core, будет полностью посвящена глава 15.

    Встроенные объекты языка JavaScript

    Ранее мы познакомились со встроенным объектом Date, который предоставляется самим языком JavaScript и служит для хранения значений даты и времени:

    var dNow = new Date();

    Объект Date поддерживает ряд методов, позволяющих получать отдельные составляющие даты и времени и манипулировать ими. Так, метод getDate возвращает число, getMonth — номер месяца, а getFullYear — год. Все эти методы не принимают параметров, а возвращаемые ими результаты представляют собой числа.

    Пример:

    var sNow = dNow.getDate() +"." + dNow.getMonth() +"." +

    dNow.getFullYear();

    Здесь мы объединяем в одну строку число, номер месяца и год, разделяя их точками. Таким образом мы получим значение даты в формате <число>.<месяц>.<год>.

    При этом JavaScript сам выполняет неявное преобразование числовых величин в строки.

    Объект String служит для хранения строк.

    var s = "JavaScript";

    Мы только что создали экземпляр объекта String, хранящий строку JavaScript.

    Здесь мы столкнулись с одной особенностью JavaScript, отличающей его от других языков программирования. Все значения простых типов данных в нем на самом деле являются экземплярами соответствующих объектов. Так, строка — это фактически экземпляр объекта String.

    Свойство length объекта String хранит длину строки в символах:

    var l = s.length;

    var l = "JavaScript".length;

    Эти выражения помещают в переменную l длину строки JavaScript.

    Метод substr возвращает фрагмент строки заданной длины, начинающийся с указанного символа:

    substr(<номер первого символа>[, <длина фрагмента>]);

    Первым параметром передается номер первого символа, включаемого в возвращаемый фрагмент строки.

    ВНИМАНИЕ!

    В JavaScript символы в строках нумеруются, начиная с нуля.

    Второй, необязательный, параметр задает длину возвращаемого фрагмента в символах. Если он опущен, возвращаемый фрагмент будет содержать все оставшиеся символы строки.

    После выполнения Web-сценария

    var s1 = s.substr(4);

    var s2 = s.substr(4, 2);

    в переменной s1 окажется строка "Script", а в переменной s2 — строка "Sc".

    Объект Number служит для хранения чисел, а объект Boolean — логических величин:

    var n = 123;

    var b = false;

    Числа и логические величины с точки зрения JavaScript также представляют собой экземпляры соответствующих объектов.

    Объект Array служит для хранения массивов:

    var a = [1, 2, 3, 4];

    Он поддерживает единственное свойство length, возвращающее размер массива, т. е. число элементов в нем:

    var l = a.length;

    var l = [1, 2, 3, 4].length;

    Уже знакомый нам объект Math, единственный экземпляр которого создается самим JavaScript и хранится в переменной Math, представляет набор методов для выполнения математических и тригонометрических вычислений.

    Мы не будем рассматривать здесь все встроенные объекты JavaScript и поддерживаемые ими свойства и методы. Это можно найти в любой книге по JavaScript- программированию.

    Объект Object и использование его экземпляров

    Но об одном встроенном объекте следует поговорить особо. Это объект Object, весьма специфический.

    Экземпляры этого объекта обычно используются для хранения сложных структур данных, включающих произвольный набор свойств и методов. Один экземпляр объекта Object может иметь один набор свойств и методов, а другой экземпляр — совсем другой.

    Экземпляры объекта Object создают с помощью особых выражений, называемых инициализаторами. Инициализатор чем-то похож на определение стиля (листинг 14.12).

    Листинг 14.12

    {

    <имя свойства 1>: <значение свойства 1>,

    <имя свойства 2>: <значение свойства 2>,

    .

    <имя свойства n-1>: <значение свойства n-1>;

    <имя свойства n>: <значение свойства n>

    <имя метода 1>: <функция, реализующая метод 1>,

    <имя метода 2>: <функция, реализующая метод 2>,

    .

    <имя метода n-1>: <функция, реализующая метод n-1>,

    <имя метода n>: <функция, реализующая метод n>

    }

    После выполнения инициализатора JavaScript вернет нам готовый экземпляр объекта Object, который мы можем присвоить какой-либо переменной или использовать в качестве параметра функции или метода.

    Пример:

    var oConfig = { tag: "DIV", id: "cother", html: "Это прочие сведения." };

    Здесь мы получили экземпляр объекта Object со свойствами tag, id и html, задали для этих свойств значения и сохранили получившийся экземпляр в переменной oConfig.

    Пример:

    var oConfig2 = { url: "pages/t_img.htm",

    success: function (response, opts){

    var obj = Ext.decode(response.responseText);

    }

    };

    А здесь мы создали экземпляр объекта Object со свойством url и методом success и сохранили получившийся экземпляр в переменной oConfig2. (Код последнего примера взят из документации по библиотеке Ext Core.)

    Обратим внимание на два момента. Во-первых, функцию, реализующую метод success, мы объявили прямо в инициализаторе. Во-вторых, создание метода в данном случае — суть присваивание функции, которая реализует этот метод, свойству, имя которого станет именем метода. Следовательно, здесь тот же самый случай, что и с присваиванием функции переменной (см. раздел, посвященный функциям).

    Экземпляры объекта Object в библиотеке Ext Core обычно служат для задания различных необязательных параметров и создаются как раз с помощью инициализаторов. Так что мы часто будем иметь с ними дело.

    Объекты Web-обозревателя. Объектная модель документа DOM

    Объекты, предоставляемые Web-обозревателем, делятся на две группы:

    — объекты, представляющие Web-страницу и элементы, созданные с помощью разных тегов (абзац, заголовок, таблица, изображение и др.);

    — объекты, представляющие сам Web-обозреватель. Начнем с объектов первой группы.

    Как мы уже знаем, саму Web-страницу представляет объект HTMLDocument. Единственный экземпляр данного объекта хранится в переменной document и представляет Web-страницу, открытую в текущем окне Web-обозревателя.

    Отдельный элемент Web-страницы, независимо от тега, с помощью которого он создан, представляется объектом HTMLElement. На этом объекте основаны другие объекты, представляющие элементы Web-страницы, которые созданы на основе определенных тегов. Так, абзац представляется объектом HTMLParagraphElement, изображение — объектом HTMLImageElement, гиперссылка — объектом HTMLLinkElement, а таблица — объектом HTMLTableElement.

    Для каждого элемента загруженной Web-страницы Web-обозреватель создает экземпляр соответствующего объекта. Например, для каждого абзаца создается экземпляр объекта HTMLParagraphElement, для каждого изображения — экземпляр объекта HTMLImageElement, для каждой гиперссылки — экземпляр объекта HTMLLinkElement, а для каждой таблицы — экземпляр объекта HTMLTableElement.

    В результате в памяти компьютера создается структура взаимосвязанных экземпляров объектов, соответствующая структуре элементов Web-страницы. Она называется объектной моделью документа, или DOM (сокращение от Document Object Model — объектная модель документа).

    Объект HTMLDocument поддерживает ряд методов для доступа к нужному элементу Web-страницы, в смысле — к представляющему его экземпляру соответствующего объекта. Обычно для уникальной идентификации элемента Web-страницы используется значение атрибута тега ID. Мы поговорим об этом подробнее в главе 15.

    Объект HTMLElement поддерживает свойства и методы, общие для всех типов элементов Web-страницы. Они позволяют получить или задать значение какого-либо атрибута тега, привязать к нему стилевой класс, вставить в элемент Web-страницы другой элемент в качестве дочернего и пр. Объекты, созданные на основе HTMLElement, расширяют этот набор свойств и методов.

    DOM является одним из основополагающих стандартов Интернета, разрабатывается и утверждается организацией W3C. Все Web-обозреватели обязаны ее поддерживать.

    Что касается объектов второй группы, то их немного. Это, прежде всего, объект Window, представляющий окно Web-обозревателя и поддерживающий ряд свойств и методов, с помощью которых мы можем им управлять. Экземпляр этого объекта, представляющий текущее окно Web-обозревателя, хранится в переменной window. Кроме того, существует еще несколько объектов, представляющих Web-обозреватель, но они встречаются значительно реже.

    К рассмотрению объектов Web-обозревателя мы вернемся в главе 15. А пока что закончим с ними.

    Свойства и методы экземпляра объекта

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

    Однако язык JavaScript предоставляет возможность создать у экземпляра объекта сколько угодно свойств и методов, которые будут принадлежать только ему. Другие экземпляры того же объекта эти свойства и методы не получат.

    Пример:

    var dNow = new Date();

    dNow.someProperty = 3;

    Здесь мы создали у экземпляра объекта Date, хранящегося в переменной dNow, свойство someProperty и присвоили ему значение 3. Данное свойство будет принадлежать только этому экземпляру объекта Date.

    Пример:

    elNavbar.someMethod = function() {. };

    Здесь мы добавили к экземпляру объекта Element библиотеки Ext Core, хранящемуся в переменной elNavbar, метод someMethod. Опять же, данный метод будет принадлежать только этому экземпляру объекта.

    А в следующем примере мы добавили к созданному ранее конфигуратору oConfig свойство style и присвоили этому свойству строку с определением встроенного стиля:

    oConfig.style = "color: red;";

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

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

    Правила написания выражений

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

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

    — Запрещаются переносы строк внутри константы, ключевого слова, имени переменной, свойства, функции, метода или объекта. В противном случае мы получим сообщение об ошибке.

    — Признаком конца выражения служит символ точки с запятой (;).

    В качестве примера давайте возьмем одно из выражений из начала главы:

    y = y1 * y2 + x1 * x2;

    Мы можем записать его так, разнеся на две строки:

    y = y1 * y2 +

    x1 * x2;

    Или даже так:

    y =

    y1*

    y2+

    x1*

    x2;

    И в любом случае оно будет выполнено, т. к. при написании выражения мы не нарушили ни одного из перечисленных правил.

    Но если мы нарушим их, выполнив перенос строк внутри имени переменной y2:

    y = y1 * y

    2 + x1 * x2;

    получим сообщение об ошибке.

    JavaScript весьма либерально относится к тому, как мы пишем его выражения. Благодаря этому мы можем форматировать JavaScript-код с помощью разрывов строк и пробелов для удобства его чтения.

    Комментарии JavaScript

    Из глав 2 и 7 мы знаем о существовании комментариев — особых фрагментов кода HTML и CSS, которые не обрабатываются Web-обозревателем и служат для того, чтобы Web-дизайнер смог оставить какие-либо заметки для себя или своих коллег. Было бы странно, если бы JavaScript не предоставлял аналогичной возможности.

    Комментарии JavaScript бывают двух видов.

    Комментарий, состоящий из одной строки, создают с помощью символа / (слэш), который помещают в самом начале строки комментария:

    / Это комментарий

    var dNow = new Date();

    Однострочный комментарий начинается с символа / и заканчивается концом строки. Комментарий, состоящий из произвольного числа строк, создают с помощью последовательностей символов /* и */. Между ними помещают строки, которые станут комментарием:

    /*

    Это комментарий, состоящий из нескольких строк.

    */

    var dNow = new Date();

    Многострочный комментарий начинается с последовательности символов /* и заканчивается последовательностью */.

    Вот и все о языке JavaScript.

    Что дальше?

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

    В следующей главе мы изучим библиотеку Ext Core, которой будем пользоваться в дальнейшем. Также мы познакомимся с некоторыми объектами Web-обозревателя, с которыми будем работать напрямую. А еще узнаем о событиях и их обработке.

    ГЛАВА 15. Библиотека Ext Core и объекты Web-обозревателя 

    В предыдущей главе мы узнали, как создается поведение Web-страниц, и познакомились с Web-сценариями и языком программирования JavaScript, на котором они пишутся. Еще мы написали два простых Web-сценария, один из которых выводил на Web-страницу текущую дату, а другой менял цвет рамки у элементов полосы навигации при наведении на них курсора мыши. И незаметно для себя стали Web- программистами…

    Также мы получили в свое распоряжение библиотеку Ext Core, призванную облегчить труд Web-программистов — нас с вами. О, это замечательная библиотека!..

    Замечательная настолько, что ей будет посвящена почти вся эта глава. Еще мы рассмотрим пару объектов Web-обозревателя, с которыми мы будем работать напрямую, но разговор о них будет совсем коротким.

    Итак, просим любить и жаловать — библиотека Ext Core!

    Библиотека Ext Core 

    В этом разделе мы будем изучать самые полезные для нас на данный момент возможности библиотеки Ext Core. Полностью она описана в справочнике, доступном на ее "домашнем" Web-сайте.

    Зачем нужна библиотека Ext Core

    Но зачем нужна эта библиотека? Почему бы нам не работать напрямую с объектами Web-обозревателя, обращаясь к их свойствам и вызывая их методы? Почему, чтобы управлять содержимым Web-страницы, нужны дополнительные инструменты и дополнительные сложности?

    В том-то и дело, что библиотека Ext Core призвана устранять сложности, а не создавать их.

    Вспомним, что мы узнали в главе 14. Web-обозреватель представляет саму Web-страницу и отдельные ее элементы в виде экземпляров особых объектов — объектов Web-обозревателя. В результате в памяти компьютера формируется структура

    экземпляров объектов, представляющая содержимое Web-страницы. Она называется объектной моделью документа, или DOM.

    Организация W3C требует, чтобы DOM соответствовала разработанному стандарту, тесно связанному со стандартами HTML и CSS. Разумеется, все Web- обозреватели его поддерживают.

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

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

    Поэтому опытные Web-программисты и создают дополнительные библиотеки, добавляющие к инструментам, предусмотренным стандартом DOM, свои собственные, как правило, более мощные. К таким библиотекам относится и Ext Core.

    Далее. Стандарт — стандартом, но разные Web-обозреватели зачастую по-разному его поддерживают. Сплошь и рядом возникают ситуации, когда один и тот же Web- сценарий прекрасно работает, скажем, в Firefox, но никак не хочет правильно исполняться в Internet Explorer. Ладно, Internet Explorer, как говорится, — отрезанный ломоть, до сих пор не получивший поддержку HTML 5 и CSS 3, но ведь и Opera, и Chrome, и Safari тоже поддерживают некоторые аспекты стандарта DOM по-своему. Налицо несовместимость Web-обозревателей, которую приходится как-то обходить.

    К тому же, некоторые объекты Web-обозревателя до сих пор не стандартизированы, и, насколько известно автору, даже не было предпринято попытки их стандартизировать. К таким объектам относятся те, что представляют сам Web- обозреватель, в частности, объект Window.

    Поэтому сторонние библиотеки, расширяющие возможности DOM, еще и устраняют несовместимость Web-обозревателей. И Ext Core — не исключение.

    Сейчас такие библиотеки — Prototype, jQuery и "героиня" этой главы Ext Core — становятся все более и более популярными. Они применяются при программировании поведения для всех сложных Web-сайтов. В России даже выходят посвященные им книги; по крайней мере, автору встречалась книга, посвященная jQuery.

    Ну а мы вернемся к Ext Core.

    Использование библиотеки Ext Core

    Библиотека Ext Core распространяется с Web-страницы http://www.extjs.com/products/core/?ref=family своего "домашнего" Web-сайта. На ней мы найдем гиперссылки на Web-страницы загрузки библиотеки, краткое руководство программиста с примерами и полный справочник по ней.

    Библиотека Ext Core распространяется в виде архива ZIP, хранящегося в файле с именем вида ext-core-<номер версии>.zip и содержащего саму библиотеку, ее исходные коды в "читабельном" виде и несколько примеров. На момент написания книги была доступна версия 3.1.0, ее-то и использовал автор.

    Сама библиотека Ext Core хранится в файле Web-сценария ext-core.js, который находится в описанном архиве. Она написана на языке JavaScript и представляет собой объявления многочисленных объектов, их свойств и методов и переменных, хранящих экземпляры объектов, — всем этим Web-программист будет активно пользоваться.

    Код Ext Core нужно выполнить еще до загрузки Web-страницы, тогда библиотека сможет успешно создать все свои объекты, экземпляры объектов и переменные. Для этого в секцию тела Web-страницы (тег <HEAD>) помещают тег <SCRIPT> такого вида:

    <SCRIPT SRC="ext-core.js"></SCRIPT>

    Он указывает Web-обозревателю загрузить Web-сценарии, хранящиеся в файле ext- core.js и составляющие эту библиотеку, и выполнить их еще до того, как будет загружена секция тела Web-страницы. Таким образом, когда Web-страница будет выведена на экран, библиотека Ext Core окажется "во всеоружии".

    Что касается Web-сценариев, составляющих поведение Web-страницы, то они хранятся в отдельном файле — так требует концепция Web 2.0. Тег <SCRIPT>, загружающий и выполняющий эти Web-сценарии, помещают в самом конце HTML-кода Web-страницы, перед закрывающим тегом </BODY>:

    <SCRIPT SRC="<интернет-адрес файла Web-сценария, хранящего код поведения для Web-страницы>.js"></SCRIPT>

    Любые Web-сценарии, выполняющие манипуляции с элементами Web-страницы, должны быть выполнены только после того, как Web-страница будет полностью загружена, а соответствующая ей DOM — сформирована. Но как этого добиться? Даже если мы поместим тег <SCRIPT>, загружающий и выполняющий их, в самом конце HTML-кода Web-страницы, проблемы это не решит. Ведь, даже если Web-страница уже загружена, формирование DOM Web-страницы будет продолжаться, и когда оно закончится — неизвестно.

    Ext Core предоставляет нам элегантное решение: все Web-сценарии, которые должны быть выполнены после завершения загрузки Web-страницы, оформляются в виде тела функции, не принимающей параметров. Эта функция передается в качестве параметра методу onReady объекта Ext:

    Ext.onReady(function() {

    <код, выполняемый после загрузки Web-страницы> });

    Метод onReady сохраняет переданную ему функцию в особом свойстве единственного экземпляра объекта Ext и впоследствии, когда Web-страница будет полностью загружена, а ее DOM — сформирована, вызывает ее.

    Собственно, так мы и поступили в главе 14, когда создавали второй Web-сценарий.

    Ключевые объекты библиотеки Ext Core

    Как уже неоднократно говорилось, библиотека Ext Core объявляет несколько объектов, которые представляют различные элементы Web-страницы, специальные структуры данных, применяемые при программировании, и саму эту библиотеку. Настала пора рассмотреть некоторые из этих объектов хотя бы вкратце.

    НА ЗАМЕТКУ

    Язык JavaScript предоставляет средства для создания новых объектов либо "с нуля", либо на основе уже существующих. Однако эти средства довольно сложны, и начинающим Web-программистам они ни к чему. Поэтому мы не будем их рассматривать.

    Прежде всего, это объект Ext, о котором мы уже наслышаны. Он предоставляет набор методов для доступа к нужному элементу Web-страницы, знакомый нам метод onReady и несколько служебных методов. Кроме того, он служит "вместилищем" для остальных объектов библиотеки Ext.

    Единственный экземпляр объекта Ext создается самой библиотекой Ext Core и хранится в переменной Ext.

    С объектом Element (точнее, Ext.Element, поскольку он хранится в объекте Ext) мы также знакомы по главе 14. Он представляет элемент Web-страницы, независимо от формирующего его тега.

    Объект Element фактически заменяет "универсальный" объект Web-обозревателя HTMLElement и все созданные на его основе объекты, представляющие отдельные теги. Он поддерживает множество методов, позволяющих манипулировать элементом Web-страницы, привязывать к нему стилевые классы, задавать отдельные параметры стиля и вставлять в него другие элементы в качестве дочерних. Работать с экземплярами объекта Element несравнимо проще, чем с экземплярами объектов Web-обозревателя.

    Экземпляры объекта Element можно получить только в результате вызова определенных методов этого же или других объектов.

    Весьма примечательный объект CompositeElementLite (точнее, Ext.Composite- ElementLite) представляет массив экземпляров объекта Element (о массивах было рассказано в главе 14). Такие объекты-массивы называются коллекциями и, в отличие от обычных массивов, поддерживают дополнительный набор методов, позволяющих манипулировать отдельными экземплярами объектов, составляющими коллекцию (ее элементами).

    Экземпляры объекта CompositeElementLite получают в результате вызова определенных методов других объектов. Они содержат экземпляры объекта Element, представляющие набор элементов Web-страницы, которые удовлетворяют некому критерию, например, селектору CSS.

    Объект DomHelper (полное имя — Ext.DomHelper) служит для создания новых элементов Web-страницы на основе указанных тега, значений его атрибутов и содержимого. В результате получается экземпляр объекта Element, представляющий созданный элемент.

    Единственный экземпляр объекта DomHelper создается самой библиотекой Ext Core и хранится в переменной Ext.DomHelper.

    Объект EventObject (Ext.EventObject) служит для хранения сведений о возникшем в элементе Web-страницы событии. (О событиях будет рассказано далее, в соответствующем разделе.)

    Экземпляр объекта EventObject создается самой библиотекой Ext Core и передается в функцию — обработчик события первым параметром.

    Доступ к нужному элементу Web-страницы

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

    Здесь нам помогут методы объекта Ext, которые мы сейчас рассмотрим.

    Метод get возвращает экземпляр объекта Element, представляющий определенный элемент Web-страницы:

    Ext.get(<значение атрибута тега ID>|<экземпляр объекта HTMLElement>)

    Как видим, этот метод принимает один параметр. Им может быть строка, содержащая значение атрибута тега ID, по которому будет выполняться поиск элемента Web-страницы.

    Пример:

    var elNavbar = Ext.get("navbar");

    Здесь мы получили экземпляр объекта Element, представляющий "внешний" список navbar, что формирует полосу навигации.

    В главе 7 мы узнали, что атрибут тега ID обеспечивает привязку к элементу Web- страницы именованного стиля. Но чаще всего он используется, чтобы дать элементу уникальное в пределах Web-страницы имя. В таком случае говорят, что такой-то элемент Web-страницы имеет такое-то имя, например, "внешний" список, формирующий нашу полосу навигации, имеет имя navbar.

    Еще мы можем передать методу get экземпляр объекта HTMLElement, представляющий нужный нам элемент Web-страницы. Такой вызов данного метода применяют, если хотят создать на основе экземпляра объекта HTMLElement экземпляр объекта Element и получить в свои руки всю мощь Ext Core. Кстати, мы с этим потом столкнемся.

    Метод get имеет важную особенность, о которой мы обязательно должны знать. Дело в том, что библиотека Ext Core при инициализации объявляет внутренний массив, или, если точнее, хэш (см. главу 14). При первом доступе к какому-либо элементу Web-страницы метод get создает представляющий данный элемент экземпляр объекта Element и помещает его в этот массив. При повторном доступе к тому же самому элементу Web-страницы соответствующий ему экземпляр объекта Element просто извлекается из данного массива. Такой подход позволяет значительно увеличить быстродействие, ведь извлечение элемента массива выполняется много быстрее, чем создание экземпляра объекта.

    Сохранение каких-либо структур данных во внутреннем хранилище с целью ускорения к ним доступа называется кэшированием. А само это внутреннее хранилище (в случае библиотеки Ext Core — массив) называется кэшем.

    Однако кэширование имеет и недостатки. Предположим, что мы создали Web-страницу с множеством элементов, которыми планируем управлять программно, из Web-сценариев. Причем доступ к каждому из этих элементов мы получаем всего один раз за все время, пока Web-страница открыта в Web-обозревателе, после чего больше их не трогаем. В результате кэш быстро "засорится" экземплярами объекта Element, что представляют не нужные нам более элементы Web-страницы. Что приведет к лишней трате памяти.

    Поэтому создатели Ext Core предусмотрели метод fly, применяемый именно в таких случаях:

    Ext.fly(<значение атрибута тега ID>|<экземпляр объекта HTMLElement>)

    Он полностью аналогичен только что рассмотренному нами методу get с одним исключением — он кэширует элементы Web-страницы (в смысле — представляющие их экземпляры объекта Element) не в массиве, а в обычной переменной. Это значит, что в кэше хранится только элемент Web-страницы, к которому обращались при последнем вызове этого метода:

    var elNavbar = Ext.fly("navbar");

    Также метод fly работает быстрее, чем метод get, за счет того, что ему не нужно искать экземпляр объекта Element, соответствующего запрашиваемого элементу Web-страницы, в массиве, который может быть очень большим. Ему достаточно проверить всего одну переменную.

    — Метод get следует использовать, если данный элемент Web-страницы понадобится нам в дальнейшем.

    — Метод fly полезен, если нам требуется получить доступ к элементу Web-страницы всего один раз.

    Не принимающий параметров метод getBody возвращает экземпляр объекта Element, представляющий секцию тела Web-страницы (тег <BODY>):

    var elBody = Ext.getBody();

    Метод getDom возвращает экземпляр объекта Web-обозревателя HTMLElement, представляющий определенный элемент Web-страницы:

    Ext.getDom(<значение атрибута тега ID>|<экземпляр объекта Element>)

    Этот метод принимает один параметр, которым может быть строка с именем элемента Web-страницы (значением атрибута ID его тега) или экземпляр объекта Element, представляющий этот элемент.

    Пример:

    var htelNavbar = Ext.getDom("navbar");

    Здесь мы получили экземпляр объекта HTMLElement, представляющий "внешний" список navbar.

    Пример:

    var elCMain = Ext.get("cmain");

    var htelCMain = Ext.getDom(elCMain);

    Здесь мы в два этапа получили экземпляр объекта HTMLElement, представляющий контейнер cmain. На первом этапе мы с помощью метода get получили представляющий его экземпляр объекта Element библиотеки Ext Core, а на втором — вызовом метода getDom — экземпляр объекта HTMLElement Web-обозревателя.

    Свойство dom объекта Element возвращает экземпляр объекта Web-обозревателя HTMLElement, представляющий элемент Web-страницы:

    var elCMain = Ext.get("cmain");

    var htelCMain = elCMain.dom;

    Некоторые методы объектов библиотеки Ext Core требуют в качестве параметров экземпляр объекта HTMLElement. Так что свойство dom и метод getDom нам пригодятся.

    Доступ сразу к нескольким элементам Web-страницы

    Зачастую приходится выполнять одинаковые манипуляции не с одним, а сразу с несколькими элементами Web-страницы, соответствующие одному критерию (обычно это селектор CSS).

    Метод select объекта Ext возвращает экземпляр объекта CompositeElementLite, содержащий экземпляры объекта Element, которые представляют все элементы Web-страницы, что удовлетворяют заданному селектору CSS:

    Ext.select(<селектор CSS>)

    Единственным параметром этому методу передается строка с одним или несколькими селекторами. Если строка содержит несколько селекторов, их отделяют друг от друга запятыми.

    Библиотека Ext Core существенно расширяет набор селекторов по сравнению с поддерживаемыми стандартом CSS. Давайте их рассмотрим.

    - <имя тега> — элемент, созданный с помощью тега.

    - <имя тега 1> <имя тега 2> — элемент, созданный с помощью тега 2 и вложенный в тег 1, не обязательно непосредственно (может быть вложен в другой тег, вложенный в тег 1, или даже в несколько таких тегов последовательно).

    - <имя тега 1> > <имя тега 2> или <имя тега 1>/<имя тега 2> — элемент, созданный с помощью тега 2 и непосредственно вложенный в тег 1.

    - <имя тега 1> + <имя тега 2> — элемент, созданный с помощью тега 2, которому непосредственно предшествует тег 1 того же уровня вложенности.

    - <имя тега 1> ~ <имя тега 2> — элемент, созданный с помощью тега 2, которому предшествует тег 1 того же уровня вложенности, не обязательно непосредственно.

    - * — элемент, созданный с помощью любого тега.

    -.<имя стилевого класса> — элемент с привязанным стилевым классом.

    - [<имя атрибута тега>] — элемент, тег которого включает атрибут.

    - [<имя атрибута тега>=<значение>] — элемент, тег которого включает атрибут с заданным значением.

    - [<имя атрибута тега>!=<подстрока>] — элемент, тег которого включает атрибут со значением, не равным подстроке.

    - [<имя атрибута тега>^=<подстрока>] — элемент, тег которого включает атрибут со значением, начинающимся с заданной подстроки.

    - [<имя атрибута тега>$=<подстрока>] — элемент, тег которого включает атрибут со значением, заканчивающимся заданной подстрокой.

    - [<имя атрибута тега>*=<подстрока>] — элемент, тег которого включает атрибут со значением, включающим заданную подстроку.

    - [<имя атрибута тега>%=2] — элемент, тег которого включает атрибут со значением, которое без остатка делится на 2.

    -:first-child — первый потомок данного элемента.

    -:last-child — последний потомок данного элемента.

    -:only-child — единственный потомок данного элемента.

    -:nth-child(<номер>) — потомок данного элемента с заданным номером.

    -:nth-child(even) или: even — четные потомки данного элемента.

    -:nth-child(odd) или: odd — нечетные потомки данного элемента.

    -:first — первый элемент из соответствующих селектору.

    -:last — последний элемент из соответствующих селектору.

    -:nth(<номер>) — элемент с заданным номером из соответствующих селектору.

    -:contains(<подстрока>) — элемент, содержимое которого включает заданную подстроку.

    -:nodeValue(<подстрока>) — элемент, содержимое которого равно заданной подстроке.

    -:not(<селектор>) — элемент, не удовлетворяющий селектору.

    -:has(<селектор>) — элемент, который имеет хотя бы один потомок, удовлетворяющий селектору.

    -:next(<селектор>) — элемент, следующий за которым элемент того же уровня вложенности удовлетворяет селектору.

    -:prev(<селектор>) — элемент, предшествующий которому элемент того же уровня вложенности удовлетворяет селектору.

    - {<имя атрибута стиля>=<значение>} — элемент, стиль которого имеет атрибут с заданным значением.

    - {<имя атрибута стиля>!=<подстрока>} — элемент, стиль которого имеет атрибут со значением, не равным заданной подстроке.

    - {<имя атрибута стиля>^=<подстрока>} — элемент, стиль которого имеет атрибут со значением, начинающимся с заданной подстроки.

    - {<имя атрибута стиля>$=<подстрока>} — элемент, стиль которого имеет атрибут со значением, заканчивающимся заданной подстрокой.

    - {<имя атрибута стиля>*=<подстрока>} — элемент, стиль которого имеет атрибут со значением, включающим заданную подстроку.

    - {<имя атрибута стиля>%=2} — элемент, стиль которого имеет атрибут со значением, без остатка делящимся на 2.

    Если ни один подходящий элемент Web-страницы не был найден, метод select возвращает экземпляр объекта CompositeElementLite, не содержащий ни одного экземпляра объекта Element ("пустую" коллекцию).

    Здесь мы получаем экземпляр объекта CompositeElementLite, содержащий экземпляры объекта Element, которые представляют все блочные контейнеры:

    var clContainers = Ext.select("DIV");

    А здесь мы получаем блочный контейнер cmain:

    var clContainers = Ext.select("DIV[id=cmain]");

    Здесь мы получаем все пункты "внешнего" списка navbar, формирующего полосу навигации:

    var clOuterItems = Ext.select("UL[id=navbar] > LI");

    А здесь мы получаем все первые абзацы, непосредственно вложенные в контейнеры:

    var clP = Ext.select("DIV > P: first");

    Здесь мы получаем все горизонтальные линии, которым непосредственно предшествуют абзацы того же уровня вложенности:

    var clHR = Ext.select("P + HR");

    А здесь мы получаем все абзацы и теги адреса на Web-странице:

    var clPA = Ext.select("P, ADDRESS");

    Доступ к родительскому, дочерним и соседним элементам Web-страницы

    Теперь предположим, что мы наконец-то получили нужный нам элемент Web- страницы и хотим найти его родителя, потомка или "соседей" по уровню вложенности. Для этого Ext Core предоставляет нам множество методов объекта Element, которые будут описаны далее.

    Метод parent возвращает родитель данного элемента Web-страницы в виде экземпляра объекта Element:

    <экземпляр объекта Element>.parent([<селектор CSS>[, true]])

    Первый, необязательный, параметр задает селектор CSS, которому должен удовлетворять родитель, в виде строки; можно также указать несколько селекторов через запятую. Если непосредственный родитель не удовлетворяет этому селектору, метод проверит родитель родителя и т. д., пока не будет найден подходящий элемент или достигнут тег с нулевым уровнем вложенности (тег <HTML>).

    Если первый параметр не задан или с ним передана пустая строка, будет возвращен непосредственный родитель этого элемента.

    Если вторым, также необязательным, параметром передано значение true, метод parent вернет экземпляр объекта Web-обозревателя HTMLElement.

    Если подходящий родитель найден не будет, метод вернет значение null.

    Здесь мы сначала получаем в переменной elNavbar "внешний" список navbar, формирующий полосу навигации, а потом в переменной elCNavbar — его непосредственного родителя:

    var elNavbar = Ext.get("navbar");

    var elCNavbar = elNavbar.parent();

    Им окажется контейнер cnavbar.

    А здесь мы пытаемся получить родителя списка navbar, который создан с помощью тега <SPAN>:

    var elSpan = elNavbar.parent("SPAN");

    Поскольку такого родителя у списка не существует, в переменной elSpan окажется значение null.

    Метод select позволяет получить коллекцию дочерних элементов для данного элемента, удовлетворяющих заданному селектору, в виде экземпляра объекта CompositeElementLite:

    <экземпляр объекта Element>.select(<селектор CSS>)

    Единственным параметром этому методу передается строка с селектором или селекторами CSS.

    Пример:

    var clUL = elNavbar.select("LI > UL");

    В переменной clUL окажется коллекция пунктов списка navbar, которые содержат вложенные списки.

    Метод child возвращает первый встретившийся потомок данного элемента Web-страницы в виде экземпляра объекта Element:

    <экземпляр объекта Element>.child([<селектор CSS>[, true]])

    Первый, необязательный, параметр задает селектор CSS, которому должен удовлетворять потомок, в виде строки; можно также указать несколько селекторов через запятую. Если непосредственный потомок не удовлетворяет этому селектору, метод проверит потомки всех потомков данного элемента.

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

    Если вторым, также необязательным, параметром передано значение true, метод child вернет экземпляр объекта Web-обозревателя HTMLElement.

    Если подходящий потомок найден не будет, метод вернет значение null.

    Пример:

    var elUL = elNavbar.child();

    В переменной elUL окажется первый пункт списка navbar.

    Пример:

    var elUL = elNavbar.child("LI: nodeValue=CSS");

    В переменной elUL окажется пункт списка navbar, который содержит текст "CSS". Метод down отличается от метода child тем, что ищет только среди непосредственных потомков текущего элемента Web-страницы:

    <экземпляр объекта Element>.down([<селектор CSS>[, true]])

    Параметры метода down те же, что у методов parent и child.

    Пример:

    var elUL = elNavbar.down();

    В переменной elUL окажется первый пункт списка navbar.

    Методы next и prev возвращают, соответственно, следующий и предыдущий элемент Web-страницы того же уровня вложенности, что и данный элемент:

    <экземпляр объекта Element>.next|prev([<селектор CSS>[, true]])

    Параметры этих методов те же, что у методов parent и child.

    Пример:

    var elDiv = Ext.get("cmain"). next();

    В переменной elDiv окажется контейнер ccopyright — следующий за контейнером cmain.

    Пример:

    var elP = elNavbar.prev();

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

    Методы first и last возвращают, соответственно, первый и последний элемент Web-страницы того же уровня вложенности, что и данный элемент:

    <экземпляр объекта Element>.first|last([<селектор CSS>[, true]])

    Параметры этих методов те же, что у методов parent и child.

    Пример:

    var elCHeader = Ext.get("cmain"). first();

    var elCCopyright = Ext.get("cmain"). last();

    В переменной elCHeader окажется контейнер cheader, а в переменной elCCopyright — контейнер ccopyright. Это, соответственно, первый и последний из блочных контейнеров — "соседей" контейнера cmain.

    Метод is возвращает true, если данный элемент Web-страницы совпадает с заданными селектором, и false в противном случае.

    В примере из листинга 15.1 мы проверяем, создан ли контейнер cmain с помощью тега <P>. Разумеется, это не так.

    Листинг 15.1

    <экземпляр объекта Element>.is(<селектор CSS>)

    var elCMain = Ext.get("cmain");

    if (elCMain.is("P"))

    var s = "Это абзац."

    else

    var s = "Это не абзац. Тьфу на него!";

    Получение и задание размеров и местоположения элемента Web-страницы

    Добравшись до нужного элемента Web-страницы, мы можем начать работать с ним, например, получить и задать его размеры и местоположение с помощью описанных в этом разделе методов объекта Element.

    Методы getWidth и getHeight возвращают, соответственно, ширину и высоту данного элемента Web-страницы в виде числа в пикселах:

    <экземпляр объекта Element>.getWidth|getHeight([true])

    Если этим методам не передавать никаких параметров, они вернут полную ширину и высоту элемента Web-страницы, с учетом рамки и внутренних отступов. Если же им передать значение true, они вернут ширину и высоту только содержимого элемента, без учета рамки и внутренних отступов.

    Пример:

    var iWidth = Ext.get("cmain"). getWidth();

    В переменной iWidth окажется полная ширина контейнера cmain.

    Методы setWidth и setHeight задают, соответственно, ширину и высоту данного элемента Web-страницы:

    <экземпляр объекта Element>.setWidth|setHeight(<значение>)

    Единственный параметр, передаваемый данным методам, — числовое значение ширины или высоты в пикселах:

    Ext.get("cmain"). setWidth(700);

    Методы getX и getY возвращают, соответственно, горизонтальную и вертикальную

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

    Пример:

    var elCMain = Ext.get("cmain");

    var x = elCMain.getX();

    var y = elCMain.getY();

    Метод getOffsetTo возвращает смещение по горизонтали и вертикали данного элемента Web-страницы относительно другого элемента:

    <экземпляр объекта Element>.getOffsetTo(<экземпляр объекта Element>)

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

    Метод getOffsetTo возвращает массив из двух элементов (чисел в пикселах): первый представляет смещение по горизонтали, второй — по вертикали.

    Пример

    var m = Ext.get("cmain"). getOffsetTo(Ext.get("cnavbar"));

    var x = m[0];

    var y = m[1];

    Здесь мы получим в переменных x и y, соответственно, горизонтальное и вертикальное смещения контейнера cmain относительно контейнера cnavbar.

    Получение размеров Web-страницы и клиентской области окна Web-обозревателя

    Также часто бывает нужно узнать размеры всей Web-страницы и внутренней части окна Web-обозревателя, в которой выводится содержимое Web-страницы (клиентской области окна). Для этого предназначены методы особого объекта Ext.lib.Dom.

    Методы getDocumentWidth и getDocumentHeight возвращают полную, соответственно, ширину и высоту Web-страницы в числовом виде в пикселах. Параметров они не принимают:

    var pageWidth = Ext.lib.Dom.getDocumentWidth();

    var pageHeight = Ext.lib.Dom.getDocumentHeight();

    Методы getViewportWidth и getViewportHeight возвращают полную, соответственно, ширину и высоту клиентской области окна Web-обозревателя также в числовом виде и в пикселах. Параметров они не принимают:

    var clientWidth = Ext.lib.Dom.getViewportWidth();

    var clientHeight = Ext.lib.Dom.getViewportHeight();

    ВНИМАНИЕ!

    Описанные здесь методы почему-то не документированы в справочнике по Ext Core. Автор обнаружил их в JavaScript-коде этой библиотеки.

    Получение и задание значений атрибутов тега

    Часто приходится получать и задавать значения атрибутов тега, с помощью которого создан элемент Web-страницы. Для этого Ext Core предоставляет два удобных метода и одно свойство объекта Element.

    Метод getAttribute возвращает значение атрибута тега с указанным именем:

    <экземпляр объекта Element>.getAttribute(<имя атрибута тега>)

    В качестве параметра методу передается строка с именем атрибута тега. Метод возвращает строку с его значением.

    Пример:

    var s = Ext.get("cmain"). child("A: first"). getAttribute("href");

    Здесь мы получаем значение атрибута тега HREF (интернет-адрес) первой гиперссылки в контейнере cmain.

    Метод set задает новые значения для атрибутов тега:

    <экземпляр объекта Element>.set(<конфигуратор>)

    В главе 14 мы узнали о встроенном объекте JavaScript Object и выражениях-инициализаторах, с помощью которых создаются его экземпляры. Также мы узнали, что в Ext Core экземпляры этого объекта применяются для задания параметров многих методов. Так вот, метод set — первый из изученных нами, где используется такой подход.

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

    В терминологии Ext Core экземпляры объекта Object, задающие набор параметров для метода, называются конфигураторами.

    Пример:

    var oConf = { target: "_blank" };

    var s = Ext.get("cmain"). select("A"). set(oConf);

    Здесь мы задаем для всех гиперссылок в контейнере cmain значение атрибута стиля TARGET, равное "_blank". Для этого мы используем конфигуратор, содержащий свойство target со значением "_blank".

    Свойство id возвращает строку со значением атрибута тега ID, т. е. имя элемента

    Web-страницы:

    var sID = Ext.getBody(). child("DIV: last"). id;

    Здесь мы получаем имя последнего контейнера на Web-странице — "ccopyright".

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

    Ext.getBody(). child("DIV: last"). set({ id: "lastdiv" });

    ВНИМАНИЕ!

    Вообще, менять имя элемента Web-страницы в Web-сценарии — дурной тон программирования. Имя элемента должно задаваться всего один раз — при его создании.

    Управление привязкой стилевых классов

    Привязка и "отвязка" стилевых классов — одна из самых часто выполняемых в Web-сценариях операций. Было бы странно, если библиотека Ext Core не предоставляла средств для ее выполнения.

    Методы объекта Element, которые мы сейчас рассмотрим, выполняют привязку стилевых классов к элементу Web-страницы и удаление их из привязки ("отвязку").

    Метод addClass выполняет привязку указанного стилевого класса к данному элементу Web-страницы. Если такой стилевой класс уже есть в привязке, повторная его привязка не выполняется:

    <экземпляр объекта Element>.addClass(<имя стилевого класса>)

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

    Ext.select("P"). addClass("someclass");

    Здесь мы привязываем ко всем абзацам на Web-странице стилевой класс someclass.

    Метод removeClass удаляет указанный стилевой класс из привязки к данному элементу Web-страницы. Если такого стилевого класса в привязке нет, никаких действий не выполняется:

    <экземпляр объекта Element>.removeClass(<имя стилевого класса>)

    Параметр данного метода — строка с именем привязываемого стилевого класса:

    Ext.select("P"). removeClass("someclass");

    Здесь мы удаляем привязанный ранее ко всем абзацам на Web-странице стилевой класс someclass.

    Метод toggleClass привязывает заданный стилевой класс к элементу Web- страницы, если он еще не был привязан, и удаляет его из привязки в противном случае:

    <экземпляр объекта Element>.toggleClass(<имя стилевого класса>)

    Параметр данного метода — строка с именем привязываемого стилевого класса:

    Ext.select("P"). toggleClass("someclass");

    Метод replaceClass удаляет из привязки к данному элементу Web-страницы указанный стилевой класс и привязывает другой:

    <экземпляр объекта Element>.replaceClass(<имя стилевого класса, удаляемого из привязки>, <имя стилевого класса, добавляемого в привязку>)

    В качестве параметров этому методу передаются две строки с именами "отвязываемого" и привязываемого стилевых классов:

    Ext.select("P"). replaceClass("someclass", "otherclass");

    Метод radioClass привязывает указанный стилевой класс к данному элементу Web-

    страницы и удаляет его из привязки у всех элементов того же уровня вложенности:

    <экземпляр объекта Element>.radioClass(<имя стилевого класса>)

    Параметр данного метода — строка с именем привязываемого стилевого класса:

    elNavbar.child("UL LI: nodeValue=IMG"). radioClass("hovered");

    Здесь мы привязываем стилевой класс hovered к тому пункту вложенного списка, формирующего полосу навигации, который содержит текст "IMG", и удаляем его из привязки у всех остальных пунктов этого же списка.

    Метод hasClass возвращает true, если указанный стилевой класс присутствует в привязке к данному элементу Web-страницы, и false в противном случае:

    <экземпляр объекта Element>.hasClass(<имя стилевого класса>)

    Параметр этого метода — имя стилевого класса:

    if (Ext.get("cnavbar"). hasClass("hovered")) {

    var s = "К полосе навигации такой стилевой класс не привязан"

    else

    var s = "И что он там делает?..";

    Получение и задание значений атрибутов стиля

    Получение или задание значений атрибута стиля, примененного к какому-либо элементу Web-страницы, выполняется тоже весьма часто. В этом случае нам пригодятся несколько методов объекта Element, описанных далее.

    Метод getStyle возвращает значение указанного атрибута стиля для данного элемента Web-страницы:

    <экземпляр объекта Element>.getStyle(<имя атрибута стиля>)

    Параметр данного метода — строка с именем нужного атрибута стиля. Значение этого атрибута стиля возвращается также в виде строки.

    Значение атрибута стиля, возвращаемое методом getStyle, получается в результате сложения действия всех стилей, привязанных к элементу Web-страницы явно или неявно. Если указанный атрибут стиля не был задан ни в одном из привязанных к этому элементу стилей, метод getStyle вернет значение по умолчанию, устанавливаемое самим Web-обозревателем.

    Пример:

    var marginLeft = elNavbar.getStyle("margin-left");

    Метод getColor служит для получения значений атрибутов стиля, задающих цвет:

    <экземпляр объекта Element>.getColor(<имя атрибута стиля>, <значение цвета по умолчанию>[, <префикс>])

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

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

    Третий, необязательный, параметр — префикс, который добавляется в начале возвращаемого значения цвета. Если данный параметр не указан, в качестве префикса используется символ решетки #.

    Метод getColor возвращает RGB-код цвета в знакомом нам формате

    <префикс>RRGGBB,

    где RR, GG и BB — доля, соответственно, красной, зеленой и синей составляющей. Пример:

    var color = Ext.getBody(). getColor("color", "#FFFFFF");

    В переменной color окажется строка "#3B4043" — цвет текста наших Web-страниц. Мы не указали третий параметр метода getColor, поэтому возвращенное им значение имеет префикс по умолчанию — #.

    Пример:

    var color2 = Ext.getBody(). getColor("color", "#FFFFFF", "");

    В переменной color2 окажется строка "3B4043". Третьим параметром мы передали

    пустую строку, поэтому возвращенное методом getColor значение не имеет префикса (точнее, имеет — пустую строку).

    Метод setStyle задает новое значение для указанного атрибута стиля. Он имеет два формата вызова:

    <экземпляр объекта Element>.setStyle(<имя атрибута стиля>, <новое значение атрибута стиля>)

    Первым параметром в этом случае передается имя нужного атрибута стиля, а вторым — его новое значение. Оба эти значения передаются в виде строк.

    Пример:

    Ext.getBody(). setStyle("color", "black");

    Здесь мы задаем для цвета текста всей Web-страницы новое значение — black.

    Также мы можем передать методу setStyle единственный параметр — конфигуратор. Его свойства должны соответствовать атрибутам стиля, а значения свойств станут новыми значениями этих атрибутов стиля:

    <экземпляр объекта Element>.setStyle(<конфигуратор>)

    Пример:

    Ext.getBody(). setStyle({ color: "black", background-color: "white" });

    Здесь мы задаем новые значения цвета сразу для текста и фона Web-страницы. Метод setOpacity задает новое значение полупрозрачности для данного элемента

    Web-страницы:

    <экземпляр объекта Element>.setOpacity(<новое значение>)

    Новое значение полупрозрачности передается в виде числа от 0 (полная прозрачность) до 1 (полная непрозрачность):

    Ext.get("cheader"). setOpacity(0.5);

    Метод clearOpacity делает данный элемент Web-страницы полностью непрозрачным. Он не принимает параметров:

    Ext.get("cheader"). clearOpacity();

    Метод clearOpacity рекомендуется применять, чтобы сделать элемент Web-страницы непрозрачным. Вызов метода setOpacity с параметром, равным нулю, может не дать ожидаемых результатов в некоторых Web-обозревателях.

    Метод setDisplayed задает новое значение атрибута стиля display:

    <экземпляр объекта Element>.setDisplayed(<новое значение>)

    Этому методу можно передать строку с новым значением атрибута стиля display. Также можно передать значение true или false; первое значение выведет данный элемент Web-страницы на экран, второе скроет его.

    Пример:

    Ext.get("cheader"). setDisplayed(false); Ext.get("cheader"). setDisplayed("none");

    Оба выражения выполняют одно и то же действие — скрывают контейнер cnavbar.

    Управление видимостью элементов Web-страницы

    Еще библиотека Ext Core предлагает нам несколько методов объекта Element, позволяющих скрывать элементы Web-страницы и снова их показывать.

    Метод setVisibilityMode позволяет указать, с помощью какого атрибута стиля будет выполняться скрытие и показ данного элемента Web-страницы: display или visibility. (Эти атрибуты стиля были описаны в главе 9.)

    <экземпляр объекта Element>.setVisibilityMode(<имя атрибута стиля>)

    Единственным параметром этому методу передается строка с именем нужного атрибута стиля. Мы можем использовать значения свойств Ext.Element.DISPLAY и Ext.Element.VISIBILITY; первое свойство хранит имя атрибута стиля display, второе — visibility.

    Пример:

    Ext.get("navbar"). setVisibilityMode(Ext.Element.DISPLAY);

    Если метод setVisibilityMode для данного элемента Web-страницы ни разу не был вызван, для управления видимостью элемента Web-страницы будет использован атрибут стиля visibility.

    Метод setVisible скрывает или снова выводит данный элемент Web-страницы на экран:

    <экземпляр объекта Element>.setVisible(true|false)

    Если методу передано значение true, данный элемент Web-страницы будет выведен на экран, если же передано значение false — он будет скрыт:

    Ext.get("navbar"). setVisible(false);

    Методы show и hide, соответственно, показывают и скрывают данный элемент Web-страницы. Они не принимают параметров:

    Ext.get("navbar"). show();

    Метод toggle скрывает данный элемент Web-страницы, если он присутствует на экране, и выводит на экран, если он скрыт. Он не принимает параметров:

    Ext.get("navbar"). toggle();

    Метод isVisible возвращает true, если данный элемент Web-страницы видим, и false, если невидим. Он не принимает параметров.

    Пример:

    var elNavbar = Ext.get("navbar");

    if (elNavbar.isVisible())

    elNavbar.show();

    Кроме того, управлять видимостью элемента Web-страницы можно методом setDisplayed, рассмотренным в предыдущем разделе. Этот метод для скрытия и открытия элемента всегда использует атрибут стиля display.

    Добавление и удаление элементов Web-страницы

    А теперь — высший пилотаж Web-программирования! Программное добавление на Web-страницу новых элементов и программное же их удаление. Для этого применяют методы объекта DomHelper.

    Метод append добавляет новый элемент Web-страницы в качестве потомка в конец указанного элемента:

    Ext.DomHelper.append(<элемент — будущий родитель>, <конфигуратор> [, true])

    Первый параметр — элемент Web-страницы, который станет родителем для вновь создаваемого элемента. Это может быть либо строка с именем элемента, либо представляющий его экземпляр объекта Element.

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

    Все эти параметры задают в следующих свойствах конфигуратора:

    — tag — имя тега в виде строки;

    — html — HTML-код, представляющий содержимое элемента;

    — cls — стилевой класс, который будет привязан к элементу;

    — children или cn — массив конфигураторов, представляющих потомки данного элемента;

    <имя атрибута тега> — значение соответствующего атрибута тега.

    Метод append возвращает экземпляр объекта HTMLElement, представляющий созданный элемент Web-страницы. Но если мы передадим в качестве третьего, необязательного, параметра значение true, он вернет более удобный в работе и уже привычный для нас экземпляр объекта Element.

    Листинг 15.2

    var oConf = { tag: "P", html: "Привет от Web-сценария!", cls: "someclass", id: "newparagraph" }

    Ext.DomHelper.append("cmain", oConf);

    В листинге 15.2 мы добавили в конец контейнера cmain новый абзац, имеющий следующие параметры:

    — тег — <P> (задан свойством tag конфигуратора);

    — содержимое — текст "Привет от Web-сценария!" (задано свойством html конфигуратора);

    — стилевой класс — someclass (свойство cls);

    — имя — newparagraph (свойство id, соответствующее атрибуту тега ID).

    Чтобы свежедобавленному абзацу не было скучно, мы добавили сразу же после него маркированный список из трех пунктов (листинг 15.3).

    Листинг 15.3

    var oConf2 = { tag: "UL", children: [

    { tag: "LI", html: "Первый пункт" },

    { tag: "LI", html: "Второй пункт" },

    { tag: "LI", html: "Третий пункт" }

    ]

    };

    Ext.DomHelper.append("cmain", oConf2);

    Пункты списка мы задали с помощью свойства children конфигуратора. Этому свойству мы присвоили массив, каждый из элементов которого представляет собой конфигуратор, описывающий параметры одного из пунктов списка.

    Метод insertFirst аналогичен только что рассмотренному нами методу append за тем исключением, что вставляет созданный элемент Web-страницы в самое начало указанного элемента:

    Ext.DomHelper.insertFirst(<элемент — будущий родитель>, <конфигуратор> [, true])

    Как видим, этот метод принимает те же параметры, что и метод append.

    Пример:

    Ext.DomHelper.insertFirst("cmain", oConf);

    Это выражение вставляет абзац, описываемый конфигуратором oConf, в самое начало контейнера cmain.

    Методы insertBefore и insertAfter вставляют созданный элемент Web-страницы, соответственно, перед и после данного элемента на том же уровне вложенности:

    Ext.DomHelper.insertBefore|insertAfter(<элемент — будущий "сосед">, <конфигуратор>[, true])

    Первым параметром передается либо строка с именем элемента Web-страницы, который станет "соседом" вновь созданного элемента, либо представляющий его экземпляр объекта Element. Остальные параметры аналогичны соответствующим параметрам метода append.

    Пример:

    var oConf3 = { tag: "HR" } Ext.DomHelper.insertBefore("navbar", oConf3); Ext.DomHelper.insertAfter("navbar", oConf3);

    Мы только что поместили до и после списка navbar горизонтальные линии.

    Метод insertHtml позволяет создать новый элемент Web-страницы на основе строки с его HTML-кодом и поместить его возле указанного элемента или в него в качестве потомка:

    Ext.DomHelper.insertHtml(<местоположение>, <элемент — будущий "сосед" или родитель>, <HTML-код>)

    Первый параметр — строка, указывающая, куда будет помещен созданный методом элемент Web-страницы:

    — "beforeBegin" — созданный элемент будет помещен перед открывающим тегом указанного элемента и станет его предыдущим "соседом" по уровню вложенности;

    — "afterBegin" — созданный элемент будет помещен после открывающего тега указанного элемента и станет его первым потомком;

    — "beforeEnd" — созданный элемент будет помещен перед закрывающим тегом указанного элемента и станет его последним потомком;

    — "afterEnd" — созданный элемент будет помещен после закрывающего тега указанного элемента и станет его следующим "соседом" по уровню вложенности.

    Второй параметр — элемент Web-страницы, который станет "соседом" или родителем для вновь создаваемого элемента. Это должен быть представляющий его экземпляр объекта HTMLElement (не Element!).

    Третий параметр — строка с HTML-кодом, с помощью которого будет создан новый элемент.

    Метод insertHtml возвращает экземпляр объекта HTMLElement, представляющий созданный элемент Web-страницы. К сожалению, указать ему вернуть экземпляр объекта Element мы не можем.

    Пример:

    var htelCMain = Ext.getDom("cmain"); Ext.DomHelper.insertHtml("afterBegin", htelCMain, "<P ID=\"newparagraph\" CLASS=\"someclass\"></P>");

    Здесь мы добавили в начало контейнера cmain новый абзац с именем newparagraph и привязанным к нему стилевым классом someclass.

    Пример:

    var htelNavbar = Ext.getDom("navbar"); Ext.DomHelper.insertHtml("beforeBegin", htelNavbar, "<HR>"); Ext.DomHelper.insertHtml("afterEnd", htelNavbar, "<HR>");

    А здесь мы поместили до и после списка, формирующего полосу навигации, горизонтальные линии HTML.

    Метод overwrite создает новый элемент Web-страницы и помещает его внутрь указанного элемента, заменяя все его предыдущее содержимое:

    Ext.DomHelper.overwrite(<элемент — будущий родитель>, <конфигуратор>|<HTML-код>[, true])

    Первый параметр — элемент Web-страницы, который станет родителем для вновь создаваемого элемента. Это может быть либо строка с именем элемента, либо представляющий его экземпляр объекта Element.

    Второй параметр — либо конфигуратор, описывающий параметры создаваемого элемента, либо строка с HTML-кодом, на основе которого он будет создан.

    Метод overwrite возвращает экземпляр объекта HTMLElement, представляющий созданный элемент Web-страницы. Но если мы передадим в качестве третьего, необязательного, параметра значение true, он вернет экземпляр объекта Element.

    Пример:

    var oConf4 = { tag: "P", html: "Новое содержимое контейнера."}Ext.DomHelper.overwrite("cmain", oConf4);

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

    Метод markup принимает в качестве единственного параметра конфигуратор и возвращает строку с созданным на его основе HTML-кодом.

    Пример:

    Ext.DomHelper.markup(<HTML-код>)

    var s = Ext.DomHelper.markup(oConf4);

    В переменной s окажется строка "<P>Новое содержимое контейнера.</P>".

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

    Метод createChild создает новый элемент Web-страницы и делает его потомком данного элемента:

    <экземпляр объекта Element>.createChild(<конфигуратор> [, <элемент, перед которым будет вставлен созданный элемент>])

    Первым параметром данному методу передается конфигуратор, описывающий параметры создаваемого элемента Web-страницы.

    Если второй параметр опущен, созданный элемент Web-страницы будет помещен в самом конце данного элемента и станет его последним потомком. Если же в качестве его передать какой-либо элемент-потомок в виде экземпляра объекта Element, создаваемый элемент будет вставлен перед ним.

    Метод createChild возвращает экземпляр объекта Element, представляющий созданный элемент.

    Пример:

    var elCMain = Ext.get("cmain");

    elCMain.createChild(oConf, elCMain.first());

    Здесь мы вставляем абзац, описываемый конфигуратором oConf, в самое начало контейнера cmain — перед первым его потомком.

    Метод insertFirst принимает в качестве параметра конфигуратор, создает на его основе элемент Web-страницы и помещает его в начало данного элемента в качестве его первого потомка:

    <экземпляр объекта Element>.insertFirst(<конфигуратор>) Ext.get("cmain"). createChild(oConf);

    Метод replaceWith принимает в качестве параметра конфигуратор, создает на его основе элемент Web-страницы и полностью заменяет им данный элемент.

    В примере из листинга 15.4 мы удаляем полностью контейнер cmain и помещаем на его место другой контейнер, описываемый конфигуратором oCont5, с новым содержимым и тем же именем.

    Листинг 15.4

    <экземпляр объекта Element>.replaceWith(<конфигуратор>)

    var oConf5 = { tag: "DIV", html: "<P>Новый контейнер с новым содержимым.</P>", id: "cmain"

    } Ext.get("cmain"). replaceWith(oConf5);

    Метод wrap принимает в качестве параметра конфигуратор, создает на его основе элемент Web-страницы и помещает в него данный элемент, делая его потомком созданного элемента:

    <экземпляр объекта Element>.wrap([<конфигуратор>])

    Как видим, при вызове этого метода мы можем не указывать конфигуратор. В таком случае метод wrap создаст блочный контейнер на основе тега <DIV>.

    Пример:

    Ext.select("UL[id=navbar]"). wrap();

    Здесь мы заключаем список navbar, формирующий полосу навигации, в блочный контейнер. Обратим внимание, что мы не передали методу wrap никаких параметров — он сам "поймет", что именно блочный контейнер мы хотим создать.

    А в следующем примере мы заключаем все абзацы, непосредственно вложенные в контейнер cmain, в большие цитаты:

    Ext.select("DIV[id=cmain] > P"). wrap({ tag: "BLOCKQUOTE" });

    Да, библиотека Ext Core представляет весьма мощные средства для создания новых элементов Web-страницы. К сожалению, удалить ненужные элементы можно

    только методом remove объекта Element. Он немедленно удаляет данный элемент Web- страницы со всем его содержимым, не принимает параметров и не возвращает значения.

    Пример:

    Ext.get("cmain"). remove();

    Здесь мы удаляем контейнер cmain со всем его содержимым.

    Обработка событий 

    Теперь самое время рассмотреть один ключевой вопрос Web-программирования: события, их возникновение и обработка.

    Понятие события и его обработки

    Рассматривая примеры Web-сценариев, мы исходили из предположения, что они выполняются при загрузке Web-страницы. Как мы уже знаем из главы 14, Web- сценарий исполняется в том месте HTML-кода Web-страницы, в котором присутствует создающий его тег <SCRIPT>. При этом неважно, является Web-сценарий внутренним (помещенном прямо в HTML-код) или внешним (хранящимся в отдельном файле Web-сценария).

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

    Событием в терминологии Web-программирования называется некое условие, которое возникает в Web-обозревателе в ответ на действия посетителя или в процессе работы самого Web-обозревателя. Так, щелчок левой кнопкой мыши на элементе Web-страницы приводит к возникновению события "щелчок левой кнопкой мыши", а перемещение курсора мыши над элементом Web-страницы — "перемещение курсора мыши". Нажатие клавиши на клавиатуре приводит к возникновению события "нажатие клавиши", а ошибка в загрузке изображения — "ошибка загрузки".

    Существует много разнообразных событий, как говорится, на все случаи жизни. Ежесекундно их возникает десятки, если не сотни.

    Так вот, мы можем заставить Web-сценарий выполняться в ответ на возникновение определенного события в определенном элементе Web-страницы. Для этого нужный Web-сценарий особым образом привязывается к данному элементу Web- страницы и событию. Такие Web-сценарии называются обработчиками событий.

    Что может делать обработчик события? Да что угодно! При наведении курсора мыши он может привязывать к элементу Web-страницы другой стилевой класс, меняя его представление. (Именно такой обработчик события мы создали в главе 14.) При щелчке левой кнопкой мыши на элементе Web-страницы — разворачивать или сворачивать блочный контейнер, открывая или скрывая его содержимое. А при изменении размеров окна Web-обозревателя — менять размеры блочных контейнеров, чтобы полностью занять ими клиентскую область окна.

    Теперь уясним следующие моменты, связанные с обработчиками событий.

    — Обработчик события оформляется в виде функции, которая принимает два параметра. Подробнее об этом мы поговорим потом.

    — Обработчик события привязывается к конкретному элементу Web-страницы, в котором возникают события, требующие обработки. Так, если нужно обработать событие "щелчок левой кнопкой мыши" в каком-либо абзаце, обработчик привязывается к данному абзацу.

    — Обработчик события привязывается к конкретному событию. Так, если мы привязали обработчик к событию "щелчок левой кнопкой мыши", он будет выполняться только при возникновении именно этого события. Другие события, скажем, "двойной щелчок левой кнопкой мыши", он обрабатывать не будет.

    — Обработчик события выполняется только при возникновении заданного события в элементе Web-страницы, к которому он привязан. Во время загрузки Web- страницы он не выполняется.

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

    События объекта Element

    Самые полезные для нас на данный момент события, поддерживаемые объектом Element библиотеки Ext Core, представлены в табл. 15.1. Их довольно много, и некоторые из них поддерживаются только определенными элементами Web- страницы.

    Таблица 15.1. События объекта Element


    Привязка и удаление обработчиков событий

    Метод on объекта Element выполняет привязку указанной функции к указанному событию данного элемента Web-страницы в качестве обработчика:

    <экземпляр объекта Element>.on(<событие>, <функция-обработчик>)

    Первым параметром методу передается строка с названием события, к которому выполняется привязка обработчика. Названия событий приведены в первом столбце табл. 15.1.

    Второй параметр — функция, которая станет обработчиком события. Эта функция должна принимать следующие параметры:

    — первый — экземпляр объекта EventObject, представляющий сведения о событии и позволяющий им управлять (мы рассмотрим этот объект потом);

    — второй — экземпляр объекта HTMLElement, представляющий элемент Web-страницы, в котором изначально возникло данное событие.

    Кроме того, в функцию-обработчик неявно передается еще один параметр — экземпляр объекта HTMLElement, представляющий элемент Web-страницы, в котором в данный момент обрабатывается данное событие, — тот самый элемент, к которому привязан этот обработчик. Событие могло возникнуть в нем изначально, а могло всплыть из дочернего элемента; подробнее об этом будет рассказано в следующем разделе. Данный параметр доступен в теле функции-обработчика через переменную this.

    Пример:

    Ext.get("navbar"). on("mouseover", function(e, t) { Ext.get(this). addClass("hovered");});

    Здесь мы привязываем к списку navbar обработчик события mouseover. Первый параметр метода on определяет название события, которое мы хотим обрабатывать. Второй параметр этого метода содержит объявление функции-обработчика.

    В теле функции-обработчика мы обращаемся к переменной this, чтобы получить экземпляр объекта HTMLElement, представляющий элемент Web-страницы, чье событие мы обрабатываем. Чтобы получить из него соответствующий экземпляр объекта Element, мы используем метод get. После чего привязываем к полученному экземпляру объекта Element стилевой класс hovered вызовом метода addClass.

    Отметим, что наша функция-обработчик принимает два параметра, которые, впрочем, нигде в ее теле не используются. Так что мы можем вообще не указывать их в объявлении функции-обработчика:

    Ext.get("navbar"). on("mouseover", function() {Ext.get(this). addClass("hovered");});

    Мы можем оформить обработчик события в виде функции, имеющей имя, а потом указать это имя в качестве второго параметра метода on:

    function navbarMouseOver() { Ext.get(this). addClass("hovered");}

    Ext.get("navbar"). on("mouseover", navbarMouseOver);

    Это полезно, если мы хотим привязать один обработчик сразу к нескольким событиям одного или нескольких элементов Web-страницы.

    Метод removeAllListeners объекта Element удаляет все привязанные к данному элементу Web-страницы обработчики событий. Он не принимает параметров.

    Пример:

    Ext.get("navbar"). removeAllListeners();

    Всплытие и действие по умолчанию

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

    Давайте рассмотрим пункты списков, формирующих полосу навигации, и "внешнего", и вложенного. Мы привязали к ним обработчики событий mouseOver и mouseOut — это выполняет второй Web-сценарий, написанный нами в главе 14. Его фрагмент приведен в листинге 15.5.

    Листинг 15.5

    var ceLinks = Ext.select("UL[id=navbar] LI"); ceLinks.on("mouseover", function(e, t) { Ext.get(this). addClass("hovered");});

    ceLinks.on("mouseout", function(e, t) { Ext.get(this). removeClass("hovered");});

    Откроем Web-страницу index.htm в Web-обозревателе и наведем курсор мыши на пункт "HTML" "внешнего" списка. Рамка вокруг него тотчас станет более темной. Уберем курсор — и рамка примет прежний цвет. Обработчики событий mouseOver и mouseOut работают.

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

    Но при этом изменит свой цвет и рамка вокруг пункта "HTML" "внешнего" списка! Выходит, данные события возникают не только в пункте "AUDIO" вложенного списка, но и в пункте "HTML" "внешнего" списка, в который он вложен. Почему?

    Вот тут мы столкнулись со всплытием событий. Событие, возникшее в каком-либо элементе Web-страницы, после этого возникает в его родителе, далее в родителе родителя и т. д., пока оно не достигнет секции тела Web-страницы (тега <BODY>). Можно сказать, что событие "всплывает" из элемента в элемент "вверх" по уровню их вложенности друг в друга.

    Давайте рассмотрим всплытие событий на примере нашей полосы навигации. Мы наводим курсор мыши на пункт "AUDIO" вложенного списка. В нем (в теге <LI>) возникает событие mouseOver. Поскольку к данному пункту списка привязан обработчик этого события, Web-обозреватель его выполняет. Далее событие "всплывает" в сам вложенный список (тег <UL>). Никаких обработчиков событий мы к нему не привязали, поэтому событие сразу же следует в пункт "HTML" "внешнего" списка, в котором находится вложенный список. К нему, опять же, привязан обработчик события mouseOver, который выполняется Web-обозревателем. Далее событие "всплывает" во "внешний" список, контейнер cnavbar и секцию тела Web-страницы, в которой и прекращает свое существование.

    Теперь посмотрим на пункт "AUDIO" вложенного списка. В него вложен тег <CODE>, в который, в свою очередь, вложена гиперссылка (тег <A>), а в нее — уже текст "AUDIO". Когда мы наводим курсор мыши на этот текст, событие mouseOver возникает-то в теге <A>! Потом оно "всплывает" в тег <CODE>, а уже после этого — в тег <LI>, формирующий пункт списка "AUDIO", где и обрабатывается.

    А сейчас представим, что событие mouseOver не всплывает. Тогда, чтобы обработать его в пункте вложенного списка, нам пришлось бы привязывать разные обработчики к тегам <LI>, <CODE> и <A>. Представляете, сколько потребуется кода! Который в этом случае будет еще и заметно сложнее…

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

    Теперь об обработке всплывающих событий. Мы уже знаем, что функция — обработчик события принимает три параметра: два — явно, третий — неявно.

    — Первый явный параметр — экземпляр объекта EventObject, хранящий сведения о событии. Разговор о нем пока отложим.

    — Второй явный параметр — экземпляр объекта HTMLElement, представляющий элемент Web-страницы, в котором изначально возникло это событие. Отметим — возникло изначально, т. е. событие гарантированно не всплыло в него из какого-либо дочернего элемента.

    — Неявный параметр, доступный в теле функции-обработчика через переменную this, — экземпляр объекта HTMLElement, представляющий элемент Web-страницы, в котором это событие обрабатывается, т. е. тот элемент, к которому привязан обработчик. Причем событие могло как возникнуть в этом элементе изначально, так и всплыть из дочернего элемента.

    Если событие всплыло в данный элемент Web-страницы из дочернего элемента, то оба эти параметра указывают на разные элементы Web-страницы. Помните об этом!

    Ради эксперимента давайте изменим код второго Web-сценария, написанного в главе 14, так, как показано в листинге 15.6.

    Листинг 15.6

    var ceLinks = Ext.select("UL[id=navbar] LI"); ceLinks.on("mouseover", function(e, t) { Ext.get(t). addClass("hovered");});

    ceLinks.on("mouseout", function(e, t) { Ext.get(t). removeClass("hovered");});

    Здесь мы в теле функций-обработчиков получаем не элемент, в котором обрабатывается событие (переменная this), а элемент, в котором это событие изначально возникло (второй параметр этих функций t). Что получится в результате?

    Наведем курсор мыши на текст пункта "AUDIO" списка. В теге <A> возникнет событие mouseover, которое потом всплывет в тег <CODE> и далее — в тег <LI>. В теге <LI> оно и будет обработано. Только стилевой класс hovered в результате будет привязан не к тегу <LI>, а к тегу <A>! А поскольку для этого тега больше никаких параметров рамки не задано (стилевой класс hovered задает только цвет рамки — см. главу 14), рамка вокруг него не появится. Совсем другой результат!

    В табл. 15.1, перечисляющей события объекта Element, прямо указано, какие события всплывают, а какие — нет. Да-да, не все события относятся к всплывающим… Но такие "невсплывающие" события обычно очень специфичны и требуют обработки, что называется, на месте.

    Многие из всплывающих событий поддерживают возможность прерывания их всплытия на любом уровне. Это реализуется особым методом объекта EventObject, представляющем сведения о событии. Мы рассмотрим его чуть позже.

    Некоторые события, возникшие в определенных элементах Web-страницы, Web-обозреватель обрабатывает сам, реализуя так называемое действие по умолчанию. Так, действие по умолчанию для события click в гиперссылке — переход на целевую Web-страницу, а для события focus — получение гиперссылкой фокуса ввода.

    Обработчик, привязанный к событию, для которого Web-обозреватель выполняет действие по умолчанию, выполняется перед тем, как действие по умолчанию будет выполнено. Это предоставляет возможность отмены действия по умолчанию (не для всех событий) — вызовом особого метода все того же объекта EventObject. Мы рассмотрим данный метод очень скоро.

    Получение сведений о событии. Объект EventObject

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

    Метод getCharCode возвращает код алфавитно-цифрового символа, введенного с клавиатуры, в кодировке Unicode в виде числа. Он не принимает параметров.

    Коды алфавитно-цифровых символов можно узнать с помощью утилиты Таблица символов, поставляемой в составе Windows.

    Метод getKey возвращает код нажатой на клавиатуре клавиши в кодировке Unicode в виде числа. Он не принимает параметров.

    Коды клавиш клавиатуры можно найти на Web-странице http://msdn.microsoft.com/en-us/library/ms927178.aspx.

    Методы getPageX и getPageY возвращают, соответственно, горизонтальную и вертикальную координаты курсора мыши относительно Web-страницы в виде чисел в пикселах. Они не принимают параметров.

    Метод preventDefault отменяет действия по умолчанию для события. Он не принимает параметров и не возвращает значения.

    Метод stopPropagation отменяет дальнейшее всплытие события. Он не принимает параметров и не возвращает значения.

    Метод stopEvent отменяет действия по умолчанию для события и отменяет его дальнейшее всплытие. Фактически он объединяет действие методов preventDefault и stopPropagation. Этот метод также не принимает параметров и не возвращает значения.

    Мы можем исправить код нашего второго Web-сценария, написанного в главе 14, как показано в листинге 15.7, и посмотреть, что получится.

    Листинг 15.7

    var ceLinks = Ext.select("UL[id=navbar] LI");

    ceLinks.on("mouseover", function(e, t) { Ext.get(this). addClass("hovered"); e.stopEvent();});

    ceLinks.on("mouseout", function(e, t) { Ext.get(this). removeClass("hovered"); e.stopEvent();});

    Объект CompositeElementLite

    Вернемся в начало этой главы и вспомним, как мы получали доступ к нужному нам элементу Web-страницы.

    Мы можем получить доступ к одному элементу Web-страницы:

    var elCMain = Ext.get("cmain");

    Или сразу к нескольким:

    var clContainers = Ext.select("DIV");

    Мы помним, что метод select объекта Ext возвращает экземпляр объекта CompositeElementLite — коллекцию экземпляров объекта Element, представляющих все подходящие под указанный селектор CSS элементы Web-страницы. Настала пора рассмотреть объект CompositeElementLite подробнее.

    Прежде всего, объект CompositeElementLite поддерживает все методы объекта Element, предназначенные для управления привязкой стилевых классов, атрибутами

    тега и стиля, привязкой обработчиков событий и т. п. Так что мы можем привязывать стилевые классы и обработчики событий сразу к нескольким элементам Web-страницы. (Собственно, мы это уже делали.)

    Метод getCount возвращает число элементов данной коллекции. Он не принимает параметров:

    var i = clContainers.getCount();

    В переменной i окажется число элементов в полученной ранее коллекции

    clContainers — 5.

    Метод item возвращает элемент данной коллекции с указанным индексом в виде экземпляра объекта Element:

    <экземпляр объекта CompositeElementLite>.item(<индекс>)

    Как видим, этот метод принимает единственный параметр — индекс требуемого элемента коллекции в виде числа.

    Пример:

    var elDiv = clContainers.item(i — 1);

    В переменной elDiv окажется последний элемент коллекции clContainers. Поскольку элементы коллекции, как и элементы обычного массива, нумеруются, начиная с нуля, мы передали методу item значение, на единицу меньшее, чем число элементов в коллекции.

    А в следующем примере мы последовательно извлекаем все элементы коллекции clContainers и выполняем над ними какие-то действия:

    for(var k = 0; k < i; k++) {

    var elDiv = clContainers.item(k);

    // Что-то делаем

    }

    Метод indexOf возвращает индекс указанного элемента в данной коллекции в виде числа:

    <экземпляр объекта CompositeElementLite>.indexOf(<элемент>)

    Единственным параметром этому методу передается искомый элемент. Им может быть строка с именем элемента, экземпляр объекта Element или HTMLElement.

    Если переданный элемент в коллекции отсутствует, метод indexOf возвращает –1.

    Пример:

    var ind = clContainers.indexOf("cnavbar");

    В переменной ind окажется индекс контейнера cnavbar в коллекции clContainers — 1.

    Метод each вызывает для каждого элемента данной коллекции указанную функцию:

    <экземпляр объекта CompositeElementLite>.each(<функция>)

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

    — элемент коллекции в виде экземпляра объекта Element;

    — сама эта коллекция в виде экземпляра объекта CompositeElementLite;

    — индекс элемента коллекции в виде числа.

    Кроме того, элемент коллекции доступен в теле этой функции через переменную this.

    В примере из листинга 15.8 мы привязываем к каждому контейнеру, входящему в коллекцию clContainers, стилевой класс hovered.

    Листинг 15.8

    clContainers.each(function(el, cl, ind)

    {

    el.addClass("hovered");

    }

    );

    Листинг 15.9

    Другой вариант того же Web-сценария иллюстрирует листинг 15.9.

    clContainers.each(function(el, cl, ind)

    {

    this.addClass("hovered");

    }

    );

    Еще проще написать так:

    clContainers.addClass("hovered");

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

    Объекты Web-обозревателя

    Как видим, библиотека Ext Core позволяет сделать очень многое, написав несколько строчек JavaScript-кода. Если бы мы пользовались для этого исключительно объектами Web-обозревателя, объем кода вырос бы на порядок — не меньше.

    Но с помощью Ext Core мы можем сделать не все. Некоторые вещи доступны только через объекты Web-обозревателя.

    Один из таких объектов — HTMLDocument, представляющий Web-страницу. Единственный его экземпляр, представляющий текущую Web-страницу, доступен через переменную document. Это мы уже знаем.

    Из всех свойств объекта HTMLDocument интерес для нас представляют немногие. Его методы и события нам вряд ли пригодятся.

    Свойство title хранит текст заголовка Web-страницы (содержимое тега <TITLE>) в виде строки. Заголовок, как мы помним из главы 1, выводится в строке заголовка окна Web-обозревателя, в котором открыта данная Web-страница.

    Пример:

    var sTitle = document.title;

    В переменной sTitle окажется строка с текстом заголовка Web-страницы. А в следующем примере мы задаем для Web-страницы новый заголовок: document.title = "Заголовок";

    Свойство location хранит экземпляр объекта Location, представляющий интернет-адрес Web-страницы. Нам будет полезно только свойство href, хранящее интернет-адрес Web-страницы в виде строки:

    var sHREF = document.location.href;

    В переменной sHREF окажется строка с интернет-адресом Web-страницы. Пример:

    document.location.href = "http://www.w3.org";

    Здесь мы переходим на Web-страницу http://www.w3.org. Да-да, с помощью свойства href объекта Location мы можем заставить Web-обозреватель открыть другую Web-страницу, присвоив этому свойству ее интернет-адрес!

    Объект Window представляет окно Web-обозревателя. Единственный экземпляр этого объекта, представляющий текущее окно Web-обозревателя, хранится в переменной window. Это мы тоже знаем.

    Рассмотрим полезные для нас методы и события объекта Window.

    Метод alert выводит на экран окно-предупреждение с указанным текстом и кнопкой ОK. Такие окна-предупреждения выводят посетителю сообщение, которое он обязательно должен прочитать:

    window.alert(<текст, выводимый в окне-предупреждении>)

    Единственный передаваемый параметр — строка с текстом, который будет выведен в окне-предупреждении:

    window.alert("Привет от объекта Window!");

    Метод confirm выводит на экран окно-предупреждение с указанным текстом и кнопками ОK и Отмена. Такие окна-предупреждения обычно используются, чтобы запросить у посетителя подтверждение или отмену какого-либо действия:

    window.confirm(<текст, выводимый в окне-предупреждении>)

    Единственный передаваемый параметр — строка с текстом, который будет выведен в окне-предупреждении.

    Метод confirm возвращает true, если посетитель нажал кнопку ОK, и false, если он нажал кнопку Отмена.

    Событие resize возникает, когда посетитель изменяет размеры окна Web-обозревателя.

    Пример:

    Ext.fly(window). on(function()

    {

    // Что-то делаем

    }

    );

    Здесь показано, как рекомендуется привязывать обработчик к событию resize окна Web-обозревателя (это справедливо и для других событий окна).

    Объект HTMLElement, как мы уже знаем, представляет элемент Web-страницы. Рассмотрим некоторые его свойства.

    Свойство textContent хранит текстовое содержимое элемента Web-страницы в виде строки. Если элемент не имеет текстового содержимого, оно хранит значение null.

    Пример:

    var htelCHeader = Ext.getDom("cheader");

    var s = htelCHeader.textContent; htelCHeader.textContent = "!" + s +"!";

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

    Свойство innerHTML хранит HTML-код содержимого данного элемента Web-страницы в виде строки.

    Пример:

    var htelCHeader = Ext.getDom("cheader");

    var s = htelCHeader.textContent; htelCHeader.innerHTML = "<EM>" + s + "</EM>";

    Объектам Web-обозревателя впору посвящать отдельную книгу — настолько это объемная тема. Мы рассмотрели только несколько инструментов, которые предоставляют они и которые не найти в библиотеке Ext Core. Впоследствии мы еще вернемся к объектам Web-обозревателя, но это будет нескоро.

    Что дальше?

    Эта глава почти полностью была посвящена библиотеке Ext Core — инструменту, значительно упрощающему работу Web-программиста. Мы рассмотрели несколько объектов этой библиотеки и такое количество их методов, что просто голова кругом.

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

    ГЛАВА 16. Создание интерактивных Web-страниц 

    В предыдущей главе мы изучили библиотеку Ext Core. От обилия объектов, свойств, методов и событий голова идет кругом… Как же применить все это богатство на практике?

    Этому будет целиком посвящена данная глава. Мы научимся создавать Web- страницы, реагирующие (причем с пользой для дела) на действия посетителя, — интерактивные Web-страницы.

    Давайте сначала составим список того, чему хотим "научить" наши Web-страницы.

    — Изменять размеры блочных контейнеров, формирующих дизайн Web-страниц, чтобы они занимали всю клиентскую область окна Web-обозревателя.

    — Менять цвет рамки у пунктов полосы навигации при наведении на них курсора мыши. (Собственно, мы уже это сделали в главе 14; здесь мы просто разберем, как все работает.)

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

    — Скрывать и открывать вложенные списки, формирующие полосу навигации, при щелчке на пункте "внешнего" списка, в который они вложены.

    — Выделять пункт полосы навигации, соответствующий открытой в данный момент Web-странице.

    — Реализовать скрытие и открытие текста примеров на Web-страницах, посвященных отдельным тегам и атрибутам стиля, при щелчке мышью на заголовке "Пример: ".

    Довольно много, не так ли? Но библиотека Ext Core поможет нам сделать все с минимальным объемом кода.

    Управление размерами блочных контейнеров

    И первое, что мы сделаем, — заставим блочные контейнеры на наших Web-страницах изменять свои размеры так, чтобы занимать всю клиентскую область окна Web-обозревателя и при этом не выходить за ее пределы.

    Сначала откроем таблицу стилей main.css и найдем в ней стиль переопределения тега <BODY>. Изменим его так, как показано в листинге 16.1.

    Листинг 16.1

    BODY { color: #3B4043; background-color: #F8F8F8; font-family: Verdana, Arial, sans-serif; margin: 0px; overflow: hidden }

    Мы добавили в этот стиль атрибут overflow со значением hidden, тем самым убрав у всей Web-страницы полосы прокрутки. Они нам не нужны, более того, появляясь в самый неподходящий момент, они могут нарушить местоположение блочных контейнеров.

    Сохраним таблицу стилей. И сразу же откроем файл Web-сценариев main.js. В самом его начале, еще до вызова метода onReady объекта Ext, вставим код листинга 16.2.

    Листинг 16.2

    function adjustContainers() {

    var clientWidth = Ext.lib.Dom.getViewportWidth();

    var clientHeight = Ext.lib.Dom.getViewportHeight();

    var cNavbarWidth = Ext.get("cnavbar"). getWidth();

    var cHeaderHeight = Ext.get("cheader"). getHeight();

    var cCopyrightHeight = Ext.get("ccopyright"). getHeight();

    Ext.get("cheader"). setWidth(clientWidth);

    var cNavbarHeight = clientHeight — cHeaderHeight — cCopyrightHeight -30; Ext.get("cnavbar"). setHeight(cNavbarHeight); Ext.get("cmain"). setHeight(cNavbarHeight); Ext.get("cmain"). setWidth(clientWidth — cNavbarWidth—10); Ext.get("ccopyright"). setWidth(clientWidth);

    }

    В конце тела функции, которую мы передаем в качестве параметра методу onReady

    объекта Ext, вставим два выражения:

    Ext.fly(window). on("resize", adjustContainers);

    adjustContainers();

    Сохраним файл main.js. Откроем Web-страницу index.htm в Web-обозревателе — и сразу отметим, что блочные контейнеры приняли такие размеры, чтобы занять всю клиентскую область окна Web-обозревателя. Попробуем изменить размеры окна и понаблюдаем, как меняются размеры контейнеров.

    Но как все это работает? Сейчас разберемся.

    Код, добавленный нами в файл main.js, объявляет функцию adjustContainers, которая станет обработчиком события resize окна Web-обозревателя. Именно эта функция будет задавать размеры контейнеров. Давайте рассмотрим ее код построчно.

    Сначала мы получаем ширину и высоту клиентской области окна Web-обозревателя:

    var clientWidth = Ext.lib.Dom.getViewportWidth();

    var clientHeight = Ext.lib.Dom.getViewportHeight();

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

    Затем получаем ширину контейнера cnavbar, высоту контейнеров cheader и ccopyright:

    var cNavbarWidth = Ext.get("cnavbar"). getWidth();

    var cHeaderHeight = Ext.get("cheader"). getHeight();

    var cCopyrightHeight = Ext.get("ccopyright"). getHeight();

    Эти значения также понадобятся нам в дальнейшем.

    Далее задаем ширину контейнера cheader равной ширине клиентской области окна Web-обозревателя:

    Ext.get("cheader"). setWidth(clientWidth);

    Вычисляем высоту контейнеров cnavbar и cmain:

    var cNavbarHeight = clientHeight — cHeaderHeight — cCopyrightHeight -30;

    Для этого вычитаем из высоты клиентской области высоту контейнеров cheader и ccopyright и дополнительно 30 пикселов — чтобы создать отступ между нижним краем контейнера ccopyright и нижним краем клиентской области.

    Задаем полученное ранее значение в качестве высоты контейнеров cnavbar и cmain:

    Ext.get("cnavbar"). setHeight(cNavbarHeight); Ext.get("cmain"). setHeight(cNavbarHeight);

    Задаем ширину контейнера cmain:

    Ext.get("cmain"). setWidth(clientWidth — cNavbarWidth — 10);

    Ее значение получаем, вычтя из ширины клиентской области ширину контейнера cnavbar и еще 10 пикселов — величину внешнего отступа между ними (он задан в именованном стиле navbar атрибутом стиля margin-right).

    Напоследок задаем ширину контейнера ccopyright равной ширине клиентской области окна Web-обозревателя:

    Ext.get("ccopyright"). setWidth(clientWidth);

    На этом выполнение функции adjustContainers закончилось.

    Теперь рассмотрим два выражения, помещенные в тело функции, являющейся параметром метода onReady объекта Ext.

    Привязываем функцию adjustContainers в качестве обработчика к событию resize окна Web-обозревателя:

    Ext.fly(window). on("resize", adjustContainers);

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

    adjustContainers();

    Вот и все. Согласитесь — ничего сложного.

    Выделение пункта полосы навигации при наведении на него курсора мыши

    Ну, это мы уже сделали. В листинге 16.3 приведен написанный нами в главе 14 JavaScript-код.

    Листинг 16.3

    var ceLinks = Ext.select("UL[id=navbar] LI");

    ceLinks.on("mouseover", function(e, t) { Ext.get(this). addClass("hovered");

    });

    ceLinks.on("mouseout", function(e, t) { Ext.get(this). removeClass("hovered");

    });

    Разберем его построчно.

    Сначала получаем все пункты списков, формирующих полосу навигации, — и "внешнего", и всех вложенных:

    var ceLinks = Ext.select("UL[id=navbar] LI");

    Затем привязываем к событию mouseover полученных пунктов функцию-обработчик, которую там же и объявляем:

    ceLinks.on("mouseover", function(e, t) { Ext.get(this). addClass("hovered");});

    Этот обработчик сначала получит из переменной this экземпляр объекта HTMLElement, представляющий пункт списка, к которому, собственно, он и привязан. Вызовом метода get он преобразует его в экземпляр объекта Element и вызовом метода addClass привяжет к нему стилевой класс hovered (его определение см. в главе 14), который изменит цвет рамки этого пункта.

    Также привязываем обработчик к событию mouseout полученных пунктов:

    ceLinks.on("mouseout", function(e, t) { Ext.get(this). removeClass("hovered");});

    Он уберет стилевой класс hovered из привязки к данному пункту списка, и его рамка примет прежний цвет.

    Переход на целевую Web-страницу при щелчке на пункте полосы навигации

    На очереди — реализация перехода на целевую Web-страницу при щелчке на любом месте в пункте полосы навигации, не обязательно точно на гиперссылке.

    Откроем таблицу стилей main.css и найдем в ней стили гиперссылок с селекторами вида #navbar A<псевдокласс>. Удалим их и вместо них напишем стиль, приведенный в листинге 16.4.

    Листинг 16.4

    #navbar A: focus,

    #navbar A: hover,

    #navbar A: active,

    #navbar A: visited { color: #576C8C; text-decoration: none }

    Он задает для гиперссылок в списке navbar независимо от их состояния одинаковый цвет текста и отсутствие подчеркивания. Негласный стандарт для гиперссылок полосы навигации требует, чтобы они всегда имели один и тот же вид.

    Далее откроем файл Web-сценария main.js и вставим в функцию, которая передается методу onReady объекта Ext в качестве параметра, код листинга 16.5.

    Листинг 16.5

    ceLinks.on("click", function(e, t) { var elA = Ext.get(this). child("A"); if (elA) {

    var href = elA.getAttribute("HREF");

    e. stopEvent();

    window.location.href = href;

    }

    });

    В листинге 16.5 мы привязываем к событию click всех пунктов всех списков, формирующих полосу навигации, функцию-обработчик, которую там же и объявляем.

    Рассмотрим тело этой функции построчно.

    Сначала получаем из переменной this экземпляр объекта HTMLElement, представляющий элемент Web-страницы, в котором обрабатывается событие, преобразуем его в экземпляр объекта Element и сразу же получаем его потомок, созданный тегом <A>:

    var elA = Ext.get(this). child("A");

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

    Поскольку у нас пока еще не все пункты списков имеют гиперссылки, обязательно проверяем, существует ли вообще эта гиперссылка:

    if (elA) {

    Если она существует, метод child в предыдущем выражении вернет ссылку на экземпляр объекта Element, которое языком JavaScript будет преобразовано в логическое значение true. Условие будет выполнено, следовательно, выполнится блок "то" условного выражения.

    Если же такой гиперссылки нет, метод child вернет значение null. JavaScript преобразует его в значение false, условие не будет выполнено, и код блока "то" условного выражения будет пропущен.

    Первое выражение блока "то" извлекает значение атрибута HREF тега <A> — интернет-адрес гиперссылки:

    var href = elA.getAttribute("HREF");

    Останавливаем всплытие события и отменяем его действие по умолчанию:

    e. stopEvent();

    И вот почему…

    Мы выполняем программный переход на другую Web-страницу, так что "услуги" гиперссылки нам в этом случае не нужны. Поэтому мы отменяем действие по умолчанию события click для гиперссылки — переход на целевую Web-страницу.

    Событие click, возникнув в пункте вложенного списка, продолжит всплытие, пока не окажется в пункте "внешнего" списка, к которому тоже привязан обработчик этого события. Этот обработчик также выполнится и произведет переход на Web-страницу, на которую указывает гиперссылка уже пункта "внешнего" списка, что нам совсем не нужно. Поэтому мы останавливаем дальнейшее всплытие события.

    Далее выполняем переход на полученный интернет-адрес:

    window.location.href = href;

    }

    На этом блок "то" условного выражения закончен.

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

    Что ж, настало время создать остальные Web-страницы нашего Web-сайта. Точнее, переделать их под новый дизайн.

    Откроем папку tags, где хранятся файлы Web-страниц, описывающих различные теги HTML. Переименуем их все, добавив расширение bak. После этого откроем в Блокноте Web-страницу index.htm.

    Дальнейшие действия рассмотрим на примере Web-страницы t_audio.htm, описывающей тег <AUDIO>.

    1. Копируем открытую Web-страницу index.htm под именем t_audio.htm в папку tags.

    2. Открываем старую Web-страницу, которая сейчас хранится в файле t_audio.htm.bak.

    3. Копируем HTML-код из секции тела Web-страницы t_audio.htm.bak в контейнер cmain только что созданной Web-страницы t_audio.htm. Код, формирующий гиперссылку на Web-страницу index.htm, можно опустить — эта гиперссылка нам больше не нужна.

    4. Исправляем в коде Web-страницы t_audio.htm интернет-адреса, указывающие на файлы других Web-страниц, таблицы стилей и Web-сценария. Здесь все просто:

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

    Вот теги <LINK> и <SCRIPT>, указывающие на внешнюю таблицу стилей и файлы

    Web-сценариев:

    <LINK REL="stylesheet" HREF="../main.css" TYPE="text/css">

    <SCRIPT SRC="../ext-core.js"></SCRIPT>

    <SCRIPT SRC="../main.js"></SCRIPT>

    А вот HTML-код, формирующий гиперссылки на Web-страницы index.htm и t_img.htm:

    <LI><A HREF="../index.htm">HTML</A>

    <LI><CODE><A HREF="t_img.htm">IMG</A></CODE></LI>

    Интернет-адреса остальных гиперссылок формируются аналогично.

    5. Сохраняем готовую Web-страницу t_audio.htm.

    Остается проделать все это с остальными Web-страницами, описывающими теги, что мы к этому времени создали.

    Создадим также Web-страницы с описанием технологии CSS, примеров и сведениями о разработчиках. Они будут храниться в файлах css_index.htm, samples_index.htm и about.htm.

    Во втором и третьем пункте "внешнего" списка, формирующего полосу навигации, создадим вложенные списки по аналогии с тем, что мы уже создали в первом его пункте. Пусть они будут содержать по два-три пункта, потом мы добавим остальные.

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

    Конечно, наш Web-сайт все еще неполон, но для его отладки созданных Web- страниц вполне хватит. Потом, закончив с программированием, мы доделаем его до конца.

    Скрытие и открытие вложенных списков

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

    Легко!

    — Изначально все вложенные списки у нас будут скрыты.

    — При открытии Web-страницы, содержащей один из разделов Web-сайта ("HTML", "CSS" или "Примеры"), будет открываться тот вложенный список, что вложен в соответствующий пункт "внешнего" списка.

    — При открытии Web-страницы, описывающей тег, атрибут стиля или пример, будет открываться вложенный список, содержащий указывающий на эту Web- страницу пункт.

    — В данный момент может быть открыт только один вложенный список — остальные будут скрыты.

    — Для скрытия и раскрытия вложенного списка мы будем менять у него значение атрибута стиля display (см. главу 9) с помощью методов объекта Element, управляющих видимостью элемента Web-страницы.

    Откроем файл Web-сценария main.js и запишем где-либо в его начале, еще до вызова метода onReady объекта Ext, объявление функции, приведенное в листинге 16.6.

    Листинг 16.6

    function showInnerList(iIndex) {

    var elNavbar = Ext.get("navbar");

    var ceInnerLists = elNavbar.select("UL");

    ceInnerLists.setDisplayed(false);

    if (iIndex) {

    var sSelector = "UL: nth(" + iIndex + ")";

    elNavbar.child(sSelector). setDisplayed(true);

    }

    }

    Данная функция с именем showInnerList будет скрывать все вложенные списки и открывать только тот из них, порядковый номер которого передан ей в качестве единственного параметра. Если параметр опущен, но никакой вложенный список открыт не будет.

    Рассмотрим код этой функции построчно.

    Сначала получаем "внешний" список, формирующий полосу навигации:

    var elNavbar = Ext.get("navbar");

    Затем получаем все вложенные в него списки:

    var ceInnerLists = elNavbar.select("UL");

    Далее скрываем все вложенные списки, для чего используем метод setDisplayed — так проще:

    ceInnerLists.setDisplayed(false);

    Проверяем, был ли функции showInnerList передан параметр:

    if (iIndex) {

    Если он был передан, переменная iIndex будет содержать число, которое преобразуется в значение true, и условие выполнится. В противном случае переменная iIndex получит значение null, которое будет преобразовано в false, и условие не выполнится.

    Если параметр функции showInnerList был передан, выполняется следующий код. Формируем строку с селектором CSS, который будет выбирать вложенный список, чей порядковый номер был передан с параметром:

    var sSelector = "UL: nth(" + iIndex + ")";

    Выбираем вложенный список с заданным номером и открываем его:

    elNavbar.child(sSelector). setDisplayed(true);

    }

    На этом выполнение функции showInnerList завершится.

    Теперь вставим в конец тела функции, которая передается в качестве параметра методу onReady объекта Ext, такое выражение:

    showInnerList(outerIndex);

    Здесь мы вызываем функцию showInnerList, передавая ей в качестве параметра значение переменной outerIndex. Эта переменная будет хранить номер вложенного списка, который требуется открыть.

    Теперь откроем Web-страницу index.htm и в секцию ее заголовка (в теге <HEAD>) вставим такой код:

    <SCRIPT>

    outerIndex = 1;

    </SCRIPT>

    Мы присваиваем переменной outerIndex число 1 — номер вложенного списка, который должен быть открыт при открытии Web-страницы index.htm (это список раздела "HTML"). Когда будут выполняться Web-сценарии, хранящиеся в файле main.js, в том числе и вызов функции showInnerList, значение этой переменной будет передано данной функции в качестве параметра.

    Здесь мы немного нарушили требования концепции Web 2.0, предписывающие хранить поведение Web-страницы отдельно от ее содержимого. Но в данном случае это оправдано, т. к. этот Web-сценарий у разных Web-страниц нашего Web-сайта будет различаться, и создавать ради него для каждой Web-страницы "персональный" файл Web-сценария слишком расточительно.

    Такой же Web-сценарий мы вставим в секцию заголовка Web-страниц, описывающих теги HTML. И не забываем сохранять исправленные Web-страницы.

    В секцию заголовка Web-страницы css_index.htm и Web-страниц, описывающих атрибуты стиля CSS, мы вставим аналогичный код:

    <SCRIPT>

    outerIndex = 2;

    </SCRIPT>

    Он укажет, что при открытии данных Web-страниц должен быть открыт второй по счету вложенный список — раздела "CSS".

    В секцию заголовка Web-страницы samples_index.htm и Web-страниц, содержащих примеры, мы вставим код… сами догадайтесь, какой. (Подсказка: он должен раскрыть третий вложенный список.)

    А вот в секцию заголовка Web-страницы about.htm вставим такой код:

    <SCRIPT>

    outerIndex = null;

    </SCRIPT>

    Поскольку четвертый пункт "внешнего" списка не содержит вложенного списка, открывать там ничего не нужно — следует только скрыть уже открытые вложенные списки.

    Сохраним все исправленные Web-страницы и опробуем их в деле. Неплохо получилось, правда?

    Выделение пункта полосы навигации, соответствующего открытой в данный момент Web-странице

    Что ж, скрытие и открытие вложенных списков, формирующих полосу навигации, в "содружестве" с библиотекой Ext Core реализуется весьма просто. Но работа над полосой навигации еще не закончена.

    Хорошим тоном сейчас считается как-то выделять пункт полосы навигации, соответствующий открытой в данный момент Web-страницы. Выделяют его обычно рамкой или "инверсными" цветами (в качестве цвета текста используется цвет фона, а в качестве цвета фона — цвет текста). Давайте и мы сделаем нечто подобное.

    — Пункт вложенного списка мы будем выделять "инверсными" цветами.

    — Пункт "внешнего" списка, не имеющий вложенного списка, мы также будем выделять "инверсными" цветами.

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

    Заодно давайте объединим установку выделения на пункт полосы навигации со скрытием и открытием вложенных списков.

    Откроем таблицу стилей main.css и добавим в нее стили из листинга 16.7.

    Листинг 16.7

    selected,

    #navbar.selected A: link,

    #navbar.selected A: focus,

    #navbar.selected A: hover,

    #navbar.selected A: active,

    #navbar.selected A: visited { color: #F8F8F8; background-color: #576C8C }

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

    Сохраним таблицу стилей. И откроем файл Web-сценария main.js. Удалим из него объявление функции showInnerList, созданное ранее, и вставим на его место объявление функции, приведенной в листинге 16.8.

    Листинг 16.8

    function selectItem(iIndex, sText) { var elNavbar = Ext.get("navbar"); elNavbar.select("LI"). removeClass("selected"); var ceInnerLists = elNavbar.select("UL"); ceInnerLists.setDisplayed(false);

    var sSelector = "> LI: nth(" + iIndex + ")"; var elOuterItem = elNavbar.child(sSelector); var elInnerList = elOuterItem.child("UL");

    if (elInnerList) {

    elInnerList.setDisplayed(true);

    if (sText) {

    sSelector = "LI: has(:nodeValue(" + sText + "))";

    elOuterItem.child(sSelector). addClass("selected");

    }

    } else elOuterItem.addClass("selected");

    }

    Эта функция с именем selectItem будет выполнять несколько действий:

    — Если первым параметром передан порядковый номер пункта "внешнего" списка, который содержит вложенный список, она откроет данный список и скроет все остальные вложенные списки.

    — Если первым параметром передан порядковый номер пункта "внешнего" списка, который не содержит вложенный список, она выделит данный пункт "внешнего" списка и, опять же, скроет все остальные вложенные списки.

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

    Рассмотрим ее код построчно.

    Получаем "внешний" список, формирующий полосу навигации:

    var elNavbar = Ext.get("navbar");

    Убираем из привязки ко всем пунктам всех списков, формирующих полосу навигации, — и "внешнего", и вложенных — стилевой класс selected:

    elNavbar.select("LI"). removeClass("selected");

    Так мы снимем выделение со всех пунктов всех списков. Сворачиваем все вложенные списки:

    var ceInnerLists = elNavbar.select("UL");

    ceInnerLists.setDisplayed(false);

    Эти три выражения перекочевали из функции showInnerList без изменений. Формируем строку, содержащую селектор CSS, который выбирает пункт "внешнего" списка с переданным функции selectItem в качестве первого параметра порядковым номером, и получаем этот пункт:

    var sSelector = "> LI: nth(" + iIndex + ")";

    var elOuterItem = elNavbar.child(sSelector);

    Получаем вложенный список, присутствующий в этом пункте, разумеется, если он там есть:

    var elInnerList = elOuterItem.child("UL");

    Если вложенный список есть, метод child вернет экземпляр объекта Element, в противном случае — значение null.

    Проверяем, что мы получили в результате вызова метода child в предыдущем выражении:

    if (elInnerList) {

    Если этот метод вернул экземпляр объекта Element, последний будет преобразован в значение true, и условие выполнится. Если же он вернул значение null, оно будет преобразовано в false, и условие не выполнится.

    Если вложенный список существует, открываем его:

    elInnerList.setDisplayed(true);

    Проверяем, был ли функции selectItem передан второй параметр — текст пункта вложенного списка, который следует выделить:

    if (sText) {

    Если он был передан, формируем строку с селектором CSS, выбирающим пункт вложенного списка, потомок которого содержит текст, переданный этим самым вторым параметром:

    sSelector = "LI: has(:nodeValue(" + sText + "))";

    Получаем пункт вложенного списка, удовлетворяющий сформированному ранее селектору, и сразу же привязываем к нему стилевой класс selected:

    elOuterItem.child(sSelector). addClass("selected");

    }

    Если полученный ранее пункт "внешнего" списка с указанным порядковым номером не содержит вложенного списка, привязываем к этому пункту стилевой класс selected:

    } else elOuterItem.addClass("selected");

    На этом выполнение функции завершается.

    В теле функции, передаваемой в качестве параметра методу onReady объекта Ext, ранее мы добавили вызов функции showInnerList. Удалим его и вместо него вставим такой код:

    selectItem(outerIndex, innerText);

    Здесь мы вызываем функцию selectItem, передавая ей в качестве параметров значения переменных outerIndex и innerText. Первая переменная будет хранить номер вложенного списка, который требуется открыть, а вторая — текст пункта вложенного списка, который нужно выделить.

    Откроем Web-страницу index.htm, найдем код, вставленный ранее в ее секцию заголовка, и дополним его таким образом:

    <SCRIPT> outerIndex = 1; innerText = null;

    </SCRIPT>

    Мы добавили выражение, присваивающее переменной innerText значение null. При выполнении Web-сценариев, хранящихся в файле main.js, в том числе и вызове функции selectItem, значения обеих этих переменных будут переданы данной функции в качестве параметров. В результате откроется первый вложенный список, и ни один его пункт не будет выделен.

    Откроем Web-страницу t_audio.htm и дополним вставленный ранее код в ее секцию заголовка так:

    <SCRIPT> outerIndex = 1; innerText = "AUDIO";

    </SCRIPT>

    В результате будет открыт первый вложенный список и в нем выделен пункт "AUDIO".

    Аналогично заменим вставленный ранее код во всех остальных Web-страницах нашего Web-сайта и проверим их в действии.

    Скрытие и открытие текста примеров

    На Web-страницах, описывающих теги HTML и атрибуты стиля CSS, мы поместили текст примеров применения того или иного тега или атрибута стиля. Часто его делают скрывающимся и открывающимся в ответ на щелчок мышью — так можно сделать Web-страницы визуально менее громоздкими.

    Давайте и мы реализуем нечто подобное на своих Web-страничках. Хотя бы в самом простом варианте.

    — Текст примера мы поместим в блочный контейнер, к которому привяжем стилевой класс sample. Этот стилевой класс будет служить двум целям: во-первых, он обозначит данный контейнер как "вместилище" для примера, во-вторых, он задаст для контейнера особое представление, чтобы его выделить среди остального содержимого Web-страницы.

    — Особое представление для контейнера с текстом примера будет включать внутренние отступы и рамку.

    — Все содержимое контейнера изначально будет скрыто, за исключением первого его потомка (у нас — заголовок "Пример: " шестого уровня).

    — При щелчке мышью на первом потомке контейнер (т. е. остальное его содержимое) будет открываться или снова скрываться.

    — При наведении курсора мыши на первый потомок контейнера будет меняться цвет его рамки. Кроме того, мы зададим для него особое представление — форму курсора в виде "указующего перста". Так мы дадим посетителю понять, что данный элемент Web-страницы реагирует на щелчки мышью.

    — На Web-странице могут быть несколько контейнеров с текстом примеров. Это следует учесть при написании Web-сценария.

    Откроем таблицу стилей main.css и добавим в нее такие стили:

    sample { padding: 5px; border: thin dotted #B1BEC6 }

    sample >:first-child { margin: 0px 0px; cursor: pointer }

    Первый стиль — это стилевой класс sample, помечающий контейнер как "вместилище" для примера. Он задает для контейнера параметры внутренних отступов и рамки.

    Второй стиль задает для первого непосредственного потомка данного контейнера нулевые внешние отступы (чтобы убрать ненужное свободное пространство выше и ниже его) и форму курсора в виде "указующего перста".

    Теперь откроем файл Web-сценария main.js и поместим где-либо в его начале, перед вызовом метода onReady объекта Ext, код листинга 16.9.

    Листинг 16.9

    function showHideSample(e, t) {

    var elDiv = Ext.fly(t). parent(".sample");

    var ceSampleText = elDiv.select("*:not(:first-child)"); ceSampleText.setVisibilityMode(Ext.Element.DISPLAY); ceSampleText.toggle();

    }

    function prepareSamples() {

    var ceSamples = Ext.select(".sample");

    ceSamples.each(function(el, cl, ind){ var elH6 = el.child(":first"); elH6.on("click", showHideSample); elH6.on("mouseover", function(e, t) {

    Ext.get(this). parent("DIV"). addClass("hovered");

    });

    elH6.on("mouseout", function(e, t) { Ext.get(this). parent("DIV"). removeClass("hovered");

    });

    var ceSampleText = el.select("*:not(:first-child)");

    ceSampleText.setDisplayed(false);

    });

    }

    Мы объявили функции showHideSample и prepareSamples. Первая будет обрабатывать событие click в первом потомке контейнера с текстом примера и в ответ на это событие скрывать или открывать данный контейнер. Вторая будет выполнять подготовительные действия: при открытии Web-страницы скрывать контейнеры с текстом примеров и привязывать к ним обработчики событий.

    Рассмотрим код этих функций построчно. И начнем с функции prepareSamples.

    Сначала получаем все контейнеры с привязанным стилевым классом sample, т. е. содержащие текст примеров:

    var ceSamples = Ext.select(".sample");

    Затем для каждого полученного контейнера выполняем описанные далее манипуляции:

    ceSamples.each(function(el, cl, ind){

    Получаем первый потомок контейнера:

    var elH6 = el.child(":first");

    Привязываем к нему в качестве обработчика события click функцию

    showHideSample:

    elH6.on("click", showHideSample);

    Привязываем к нему функцию — обработчик события mouseover, которую там же и объявляем:

    elH6.on("mouseover", function(e, t) { Ext.fly(this). parent("DIV"). addClass("hovered");

    });

    Эта функция получит родитель элемента Web-страницы, в котором возникло данное событие, т. е. контейнер с текстом примера, и привяжет к нему стилевой класс hovered.

    Не забываем привязать функцию-обработчик события mouseout, которая будет убирать из привязки к контейнеру стилевой класс hovered:

    elH6.on("mouseout", function(e, t) { Ext.fly(this). parent("DIV"). removeClass("hovered");

    });

    Получаем все остальные элементы-потомки контейнера (не являющиеся первым потомком):

    var ceSampleText = el.select("*:not(:first-child)");

    И скрываем их:

    ceSampleText.setDisplayed(false);

    });

    На очереди — функция showHideSample.

    Получаем родитель элемента Web-страницы, в котором возникло событие:

    var elDiv = Ext.fly(t). parent(".sample");

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

    Получаем все элементы-потомки контейнера, не являющиеся первым потомком:

    var ceSampleText = elDiv.select("*:not(:first-child)");

    Указываем, что для управления их видимостью будет использован атрибут стиля display, и изменяем их видимость (открываем, если они скрыты, и скрываем, если они открыты):

    ceSampleText.setVisibilityMode(Ext.Element.DISPLAY);

    ceSampleText.toggle();

    Использовать здесь метод toggle проще, чем setDisplayed — нам не придется проверять, открыты данные элементы Web-страницы или нет. Правда, перед этим потребуется указать, что видимостью элемента будет управлять атрибут стиля display, но это мелочи.

    Еще нам нужно поставить вызов функции prepareSamples в самый конец функции, передаваемой методу onReady объекта Ext:

    prepareSamples();

    Теперь откроем любую Web-страницу, содержащую описание тега HTML или атрибута стиля CSS, и поместим текст примера в блочный контейнер с привязанным стилевым классом sample. Листинг 16.10 иллюстрирует HTML-код Web-страницы t_audio.htm.

    Листинг 16.10

    <DIV CLASS="sample">

    <H6>Пример:</H6>

    <PRE>&lt;AUDIO SRC=&quot;sound.wav&quot; CONTROLS&gt;&lt;/AUDIO&gt;</PRE>

    <H6>Результат:</H6>

    <AUDIO SRC="sound.wav" CONTROLS></AUDIO>

    </DIV>

    Внесем исправления во все аналогичные Web-страницы и проверим их в действии.

    Что дальше?

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

    Часть IV будет посвящена самым современным подходам в Web-дизайне и Web- программировании: подгружаемому и генерируемому содержимому и семантической разметке. Уже в следующей главе мы научимся подгружать часть содержимого Web-страницы из сторонних файлов программно.

    А библиотека Ext Core нам в этом поможет.









    Главная | В избранное | Наш E-MAIL | Добавить материал | Нашёл ошибку | Наверх