6. Программируем гипертекстовые переходы. JavaScript (от intuit.ru)
Рассматриваются вопросы работы с коллекцией гипертекстовых ссылок и программирования гипертекстовых переходов в зависимости от условий просмотра HTML-страниц и действий пользователя.
Для начала нам нужно разделить несколько понятий: применимость URL в атрибутах HTML-контейнеров; коллекция гипертекстовых ссылок; объекты класса URL.
Адреса URL могут использоваться в атрибутах многих HTML-контейнеров, например:
Гипертекстовая ссылка в HTML-документе - это область HTML-страницы, по которой можно "кликнуть" (или выбрать ее иным способом), чтобы перейти к просмотру другого HTML-документа. Из всех перечисленных выше вариантов применения URL гипертекстовыми ссылками являются лишь 1-е два. В объектной модели документа (DOM) они собраны в единую коллекцию гипертекстовых ссылок document.links[]. Нумерация в ней начинается с нуля (как обычно), в порядке появления ссылок в документе.
Обратите внимание, что в принципе могут существовать ссылки, по которым невозможно "кликнуть", т.к. они занимают нулевую площадь web-страницы; например, контейнер <A> с пустым содержимым, т.е. <A HREF="//ya.ru/"></A>, или контейнер <AREA>, ограничивающий фигуру нулевой площади. Тем не менее все они считаются гипертекстовыми ссылками и содержатся в коллекции document.links[]. С другой стороны, в коллекцию document.links[] не попадают якоря, то есть контейнеры <A>, не имеющие атрибута HREF (якоря нужны, чтобы задать место, на которое можно сослаться из другой гиперссылки). Все контейнеры <A> (как якоря, так и гиперссылки) собраны в коллекции document.anchors[]; в этой лекции, однако, она нас не будет интересовать.
В объектной модели документа DOM объекты класса URL имеют такие свойства, как href, protocol, hostname и т.д. (полный перечень см. ниже). В класс URL входят объекты коллекции document.links[], а также объект window.location, рассматривавшийся в лекции 4. Конечно, помимо общих свойств, перечисленных ниже, эти объекты могут иметь свои специфичные только для них свойства, методы и события. Например, у объекта window.location есть метод reload(), тогда как у ссылок его нет, но у них есть обработчик события onClick. Мы начнем с рассмотрения объектов класса URL.
Объекты URL
Объект класса URL обладает свойствами, которые определены схемой URL. В качестве примера рассмотрим ссылку:
http://1.ru:80/dir/page.cgi?product=phone&id=3#mark
Тогда ее свойства примут следующие значения (обратите внимание, что значение поля search начинается со знака "?", а значение hash - со знака "#")
Свойство | Значение |
---|---|
href | http://1.ru:80/dir/page.cgi?product=phone&id=3#mark |
protocol | http: |
hostname | 1.ru |
port | 80 |
host | 1.ru:80 |
pathname | dir/page.cgi |
search | ?product=phone&id=3 |
hash | #mark |
Как Вы помните из прошлых лекций, к свойствам можно обращаться, используя точечную нотацию (document.links[0].host) или скобочную нотацию (document.links[0]["host"]). Свойства объекта класса URL дают программисту возможность менять только часть URL-адреса. Наиболее интересно это выглядит в объекте window.location, когда при изменении его свойства href происходит загрузка нового документа.
Свойство href является свойством по умолчанию. Это значит, что вместо window.location.href="…" можно писать window.location="…", а опуская window (который является объектом по умолчанию), можно писать location.href="…" и даже location="…" - эффект будет тот же: загрузится страница с новым адресом. С этим, однако, стоит быть осторожнее, чтобы данный оператор присваивания не находился в контексте, где объектом по умолчанию может быть какой-либо другой объект, например, внутри оператора with.
Обратите внимание, что свойства объекта URL взаимозависимы, точнее, свойство href зависит от остальных свойств, а остальные свойства зависят от href. Так, если присвоить новый URL свойству href объекта, то автоматически изменятся и все остальные свойства, разобрав данный URL на составные части. И наоборот, если, например, изменить значение свойства protocol с http: на ftp:, то изменится и значение свойства href.
Коллекция ссылок links[]
К встроенным гипертекстовым ссылкам относятся собственно ссылки (<A HREF=…>…</A>) и ссылки "чувствительных" графических картинок. Все вместе они составляют коллекцию (встроенный массив) гипертекстовых ссылок документа document.links[].
К сожалению, обратиться по имени к гипертекстовой ссылке (т.е. как document.имя_ссылки) нельзя, даже несмотря на то, что у ссылки может быть задан атрибут NAME. Говоря точнее, такое обращение не рекомендуется в силу различий между браузерами. Поэтому обращаться к ним можно только через коллекцию ссылок по индексу: document.links[3] - это 4-я ссылка в документе. Стандарт также предусматривает обращение к ссылкам через коллекцию по имени: document.links["имя_ссылки"], однако это работает не во всех браузерах (в Mozilla Firefox работает, в IE7 нет). Поэтому в дальнейшем, в целях совместимости, мы будем обращаться к ссылкам через коллекцию по их индексу.
В качестве примера распечатаем гипертекстовые ссылки некоторого документа:
for(i=0;i<document.links.length;i++) document.write(document.links[i].href+"<p>");
В результате можем получить список, например, такой:
http://1.ru/help/index.html http://1.ru/help/terms.html http://1.ru/help/shop.html
Вставим в документ контейнер MAP с 2 ссылками, привязанными к областям (даже) нулевого размера:
<MAP NAME=test> <AREA SHAPE=rect COORDS="0,0,0,0" HREF="javascript:alert('Область 1')"> <AREA SHAPE=rect COORDS="0,0,0,0" HREF="javascript:alert('Область 2')"> </MAP>
И снова распечатаем массив ссылок - получим уже:
http://1.ru/help/index.html http://1.ru/help/terms.html http://1.ru/help/shop.html javascript:alert('Область 1'); javascript:alert('Область 2');
Две новые ссылки - это ссылки из контейнера MAP, который не отображается, но ссылки из него попадают в коллекцию ссылок links[]. При этом они могут попасть между обычными гипертекстовыми ссылками, если контейнер MAP расположить внутри текста документа. Итак, ссылки, создаваемые контейнерами <A HREF="…"> и <AREA HREF="…">, равноправно присутствуют в коллекции document.links[].
Замена атрибута HREF
Выше мы перечислили свойства объекта класса URL. Теперь покажем, как при помощи JavaScript-кода можно этими свойствами управлять.
Пример 6.1 (кликните, чтобы открыть его в новом окне; посмотреть исходный HTML-код можно, нажав правой кнопкой мыши и выбрав соответствующий пункт).
Рассмотрим меню типа "записная книжка". Конечно, это не настоящая записная книжка. Поле формы заполняется только при выборе гипертекстовой ссылки, расположенной над этим полем. Единственная цель данного примера - показать, как изменяется значение атрибута HREF (оно отображается в поле status окна браузера при наведении указателя мыши на ссылку). В этом примере изменение свойства href у ссылок производится точно так же, как изменение свойства src у картинок и свойства value у полей ввода в форме:
document.links[i+3].href=… document.images[i+3].src=… document.f.elements[i].value=…
На нашей странице имеется 6 ссылок, 6 картинок и 3 поля ввода. 1-е три ссылки и картинки (с номерами 0,1,2) всегда неизменны. Последние же три ссылки и картинки (т.е. с номерами 3,4,5) мы меняем вышеприведенными операторами (поэтому в них индекс i+3, где i=0,1,2). Как вы видите, мы меняем значение href целиком. Однако URL можно менять и частично.
Изменение части URL
Гипертекстовая ссылка - это объект класса URL, у которого помимо свойства href есть и другие, соответствующие частям URL, и их тоже можно менять. Проиллюстрируем эту возможность. Допустим, у нас имеется ссылка:
<A HREF="//1.ru/department/internet/js/">Курс по JavaScript</A>
Проверьте, что значение свойства document.links[0].pathname сейчас равно "department/internet/js/". Применим оператор:
document.links[0].pathname="news/"
Теперь эта ссылка указывает на адрес http://1.ru/news/.
События MouseOver и MouseOut
Эти 2 события позволяют совершать действия при наведении или уводе курсора мыши на объекты, например, обесцвечивать и проявлять картинки, менять содержимое поля status и т.п. Применительно к гипертекстовым ссылкам, первое событие генерируется браузером, если курсор мыши находится над ссылкой, а второе - когда он покидает ссылку.
Пример 6.2. Рассмотрим пример с записной книжкой, но теперь для появления подменю будем использовать обработчик события onMouseOver. Для этого достаточно заменить строчки вида:
<A HREF="javascript:ShowMenu(0);void(0);">…</A>
открывавшие подменю, когда пользователь кликал по ссылке, на строчки вида:
<td><a href="javascript:void(0);" onMouseOver="ShowMenu(0);">…</A>
открывающие подменю при наведении указателя мыши на пункт меню верхнего уровня. Выражения "javascript:void(0);" мы оставили в атрибуте HREF, чтобы ничего не происходило, если пользователь случайно кликнет по пункту меню верхнего уровня.
Пример 6.3. В предыдущем примере нам не требовалось совершать какие-то действия при уводе указателя мыши с пункта меню. Теперь рассмотрим пример, когда это требуется, и для этого мы будем использовать обработчик события onMouseOut. При наведении указателя мыши на ссылку мы будем подменять картинку, а при уводе указателя мыши с картинки - восстанавливать ее. В исходном HTML-документе это будет выглядеть следующим образом:
<A HREF="javascript:void(0);" onMouseOver="document.pic1.src='pic1.gif';" onMouseOut="document.pic1.src='pic1_.gif';"> <IMG NAME=pic1 src=pic1_.gif></A>
Как уже рассказывалось в лекции 4, при возникновении события MouseOver у гиперссылки браузер показывает URL этой ссылки в поле статуса, а при возникновении события MouseOut восстанавливает в поле статуса прежнюю надпись. Перехватить первое событие (например, отменить вывод URL в строке статуса) можно, указав в его обработчике return true. Перехватить второе событие невозможно.
Примечание. В настоящее время обработчики событий onMouseOver и onMouseOut работают уже не только с гипертекстовыми ссылками, но и с изображениями, полями ввода, таблицами, их строками и ячейками, кнопками и другими HTML-элементами.
URL-схема "JavaScript:"
Для программирования гипертекстовых переходов в спецификацию URL разработчики JavaScript ввели, по аналогии с URL-схемами http, ftp и т.п., отдельную URL-схему javascript. Она уже упоминалась во вводной лекции при обсуждении вопроса о том, куда можно поместить JavaScript-программу в HTML-документе. Здесь мы рассмотрим ее подробнее.
Схема URL javascript: используется следующим образом:
<A HREF="JavaScript:код_программы">…</A> <FORM ACTION="JavaScript:код_программы" …> … </FORM>
Рассмотрим пример гипертекстовой ссылки, в URL которой использована схема javascript:
<A HREF="javascript:alert('Спасибо!');">Кликните</A>
Как видим, если кликнуть по ссылке, то вместо перехода по какому-либо адресу просто появляется окно предупреждения.
В общем случае, после "javascript:" может стоять произвольная программа JavaScript. Что в этом случае будет происходить, если кликнуть такую ссылку? Ответ следующий: сначала JavaScript-программа будет исполнена, в результате чего будет вычислено ее значение (которым всегда считается значение последнего оператора в программе). Затем, если это значение неопределено (undefined), то далее ничего не произойдет; если же полученное значение определено, то на экран будет выведена HTML-страница с этим результатом (преобразованным в строку и воспринятым как HTML-документ, который в том числе может содержать и HTML-тэги).
В примере выше метод alert() не возвращает никакого значения, поэтому после появления окна предупреждения более ничего не происходит. Рассмотрим другие примеры. Кликнув по ссылке
<A HREF="javascript: 2+8; 5+7;">ссылка</A>
мы получим страницу, на которой написан результат последнего выражения 12 (первое выражение 2+8, конечно, тоже будет вычислено, но его результат никуда не пойдет). Если же кликнуть по такой ссылке:
<A HREF="javascript: '<H1>Ready!<H1>';">Кликните</A>
то в окне браузера откроется страница, исходный HTML-код которой есть <H1>Ready!<H1>. Если нужно, чтобы при клике по ссылке просто выполнились какие-то действия и более ничего не происходило (в частности, не происходил переход к какой-либо другой странице), то в конце программы можно поместить оператор void(0) либо void(выражение). Эта функция вычисляет переданное в нее выражение и не возвращает никакого значения. Пример:
<A HREF="javascript: document.bgColor='green'; void(0);">Кликните</A>
Если кликнуть по такой ссылке, то изменится цвет фона, и больше ничего не произойдет. Без void(0) нам бы открылась web-страница со словом green.
Все сказанное справедливо и для использования URL-схемы "javascript:" в атрибуте ACTION контейнера <FORM>, за одним нюансом: в этом случае необходимо, чтобы в качестве метода доступа был указан MEthOD="POST". Дело в том, что при использовании метода GET при отправке данных формы (т.е. при возникновении события Submit) к адресу URL, указанному в атрибуте ACTION, добавляется строка "?имя=значение&имя=значение&…", составленная из имен и значений элементов формы. Конечно же, такое выражение не является корректной JavaScript-программой, и браузеры могут выдавать сообщения об ошибке (а могут и не выдавать, а просто не выполнять программу). Пример:
<FORM NAME=f MEthOD=post ACTION="javascript:alert('Длина строки='+form.e.value.length);"> <INPUT NAME=e> <INPUT TYPE=submit VALUE="Длина"> </FORM>
Если ввести любую строку в поле ввода и нажать кнопку, то будет выведена длина строки. Этот пример был создан лишь для демонстрации URL-схемы "javascript:"; он весьма искусственный, т.к. в данном случае более уместно было бы использовать обработчик события onClick или onSubmit. Об этом и других применениях JavaScript в обработке форм рассказывается в лекции, посвященной программированию форм.
Обработка события Click
У гипертекстовой ссылки помимо URL, указанного в атрибуте HREF, можно указать действия, которые браузер должен выполнить, когда пользователь кликнет по данной ссылке, перед тем, как перейти по указанному URL. Соответствующая программа JavaScript называется обработчиком события Click и помещается в атрибут onClick контейнера <A>. Если обработчик события возвращает значение false (это можно реализовать путем помещения в конец обработчика команды return false), то переход по адресу URL, указанному в атрибуте HREF, не будет совершен. Если же обработчик возвращает true либо ничего не возвращает, то после выполнения обработчика события будет совершен переход по адресу URL. Например:
<A onClick="return confirm('Хотите посетить сайт INTUIT?')" HREF="//1.ru/">Перейти на сайт INTUIT</A>
В этом примере confirm() возвращает либо true, либо false, в зависимости от того, на какую кнопку нажмет пользователь в предложенном запросе. Соответственно, переход на указанный адрес либо произойдет, либо нет. Если бы мы в этом примере опустили слово return, то обработчик события ничего бы не возвращал (независимо от действий пользователя на запрос confirm) и переход на указанный URL совершался бы в любом случае.
Если в атрибуте ссылки (например, HREF, onClick и т.п.) пишется JavaScript-код, в котором надо сослаться на свойство или метод этой ссылки, то, как и в случае форм, можно пользоваться сокращенной записью - не указывать объект данной ссылки, либо (для большей ясности кода) вместо него писать this. Например, пусть у нас имеется 5-я ссылка в документе, и мы хотим в ее обработчике onMouseOver сослаться на свойство href данной ссылки, или вызвать метод click() данной ссылки. Тогда в этом контексте вместо document.links[5].href можно писать this.href или просто href, а вместо document.links[5].click() писать this.click() или просто click(). Это не только укорачивает код, но и избавляет нас от необходимости привязываться к конкретному номеру данной ссылки, который в любой момент может измениться.
И последнее замечание. Часто для того, чтобы скрипт запускался, когда посетитель кликает ссылку, программисты пишут что-то такое:
<A HREF="#" onClick="программа JavaScript">…</A> <A HREF="javascript:void(0)" onClick="программа JavaScript">…</A> <A HREF="javascript: программа JavaScript">…</A>
При этом код выполняется и, на 1-й взгляд, все нормально. Но на 2-й взгляд становится видно, что после клика на ссылку могут прекратить грузиться недогруженные элементы страницы (большие картинки и т.п.), останавливаются анимированные GIF'ы и, быть может, происходит что-то еще из этой серии. Все дело тут в том, что браузер считает клик пользователя по ссылке переходом на другую страницу, поэтому полагает, что заботиться о текущей странице больше не надо, ведь она с секунды на секунду заменится новой. Решение здесь такое: если вы рассчитываете при клике пользователя по ссылке оставить его на текущей странице, то не забудьте прописать выход return false из обработчика события onClick, например:
<a href="#" onClick="программа JavaScript;return false;">
В этом случае после вызова программы JavaScript выполнение клика по ссылке прекратится, поэтому браузер не будет считать, что произошел переход на другую страницу.