Введение в современные веб-технологии

События. Объект event

Разбить на страницы
Показывать лекцию целиком

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

Вот список основных событий:

  • События окна и документа: onload – документ загружен и противоположное событие onunload – документ выгружен из окна. Применяется к элементам body и frameset и объекту window.
  • События мыши: onclick – (одиночный) щелчок мыши и ondblclick – двойной щелчок. Одиночный щелчок может быть разложен на два события, onmousedown – кнопка мыши нажата и onmouseup – кнопка мыши отпущена. Отслеживаются также перемещения мыши: onmouseover – указатель мыши пересек границу объекта внутрь, onmouseout - указатель мыши пересек границу объекта наружу и onmousemove – мышь перемещается над объектом. Применяется к большинству элементов.
  • События клавиатуры: onkeypressed – нажата и отпущена клавиша, производящая символ. Это событие разделяется на два, onkeydown – клавиша нажата и onkeyup –клавиша отпущена. Применяется к большинству элементов.
  • События HTML-формы onsubmit и onreset и элементов формы onfocus, onblur и onchange рассмотрены в лекции "HTML формы".
  • Объект window.event создается браузером автоматически в момент возникновения события и доступен только внутри функции-обработчике события. Следует подчеркнуть, что модель объекта event в Internet Explorer существенно отличается от используемой в других браузерах, и требуется большое внимание при разработке динамических страниц, предназначенных для разных браузеров. Тем не менее, целый ряд свойств оказывается одинаковым во всех современных браузерах.

  • type – тип события без приставки "on". Например, событие onclick имеет тип click.
  • button – нажатая кнопка мыши (0 – ни одна, 1 – левая, 2 – правая, 3 – левая и правая, 4 – средняя, 5 – левая и средняя, 6 – правая и средняя, 7 – все три). Это свойство имеет смысл для событий мыши. В случае остальных событий имеет значение 0 независимо от действительного состояния кнопок.
  • clientX, clientY – координаты указателя мыши относительно верхнего левого угла клиентской области окна браузера.
  • screenX, screenY - координаты указателя мыши относительно верхнего левого угла дисплея.
  • keyCode – целочисленный код клавиши, которая вызвала событие клавиатуры. Коды клавиш различаются для различных аппаратных платформ (PC или Macintosh).
  • shiftKey, ctrlKey, altKey – истина ( true ), если во время события нажата также клавиша Shift, Ctrl или Alt, соответственно.
  • cancelBubble – запрещает ( true ) или разрешает ( false, по умолчанию) передачу события вверх по иерархии от элемента-источника к родительскому элементу. Каждое событие происходит на единственном элементе-источнике. Если элементу-источнику не сопоставлена функция-обработчик, событие передается вверх по дереву элементов, пока не встретит функцию-обработчик или не достигнет корневого элемента. Если функция-обработчик найдена, но не содержит cancelBubble=true, то передача вверх продолжается, если содержит, то прерывается.
  • На этом список общих свойств заканчивается. Большинство остальных свойств объекта event доступны в различных браузерах под разными именами.

  • Элемент-источник события в Internet Explorer доступен как свойство srcElement, в Mozilla Firefox и других как target.
  • События onmouseover и onmouseout в Internet Explorer показывают свойства-объекты откуда ( fromElement ) и куда ( toElement ). В Mozilla Firefox эти объекты являются свойством relatedTarget.
  • Список различий можно продолжить. Несмотря на различия в объектной модели между браузерами, писать веб-страницы, работающие в разных браузерах, можно и нужно. Рассмотрим это на примерах.

    Передача объекта event функции-обработчику

    Как было сказано, событие передается вверх по иерархии от элемента-источника к родительскому элементу и дальше. Процесс может быть остановлен присвоением свойству cancelBubble значения true.

    <script type="text/javascript">
        function checkCancel() {
          if (window.event.shiftKey)
            window.event.cancelBubble = true;
        }
        function showSrc() {
          if (window.event.srcElement.tagName == "IMG")
            alert(window.event.srcElement.src);
        }
      </script>
    ...
    <body onclick="showSrc()">
      <img onclick="checkCancel()" src="sample.gif">
    </body>
    ...

    В этом примере и элемент img, и его родительский элемент body имеют обработчик события onclick. При щелчке по изображению сначала вызывается функция checkCancel, которая проверяет состояние клавиши Shift. Если клавиша нажата, передача вверх по иерархии прекращается, если нет, выполняется также функция showSrc. К сожалению, этот пример работает только в Internet Explorer (начиная с версии 4.0). Различия в объектной модели Internet Explorer и большинства других браузеров максимальны именно в объекте event и доступе к нему. В случае Internet Explorer объект event доступен в функции-обработчике как глобальный объект, в других браузерах этот объект создается элементом-источником и доступен при вызове обработчика. Перепишем код следующим образом:

    <script type="text/javascript">
        function checkCancel(e) {
          if (e.shiftKey)
            e.cancelBubble = true;
        }
        function showSrc(e) {
          if (e.srcElement.tagName == "IMG")
            alert(e.srcElement.src);
        }
      </script>
    ...
    <body onclick="showSrc(event)">
      <img onclick="checkCancel(event)" src="sample.gif">
    </body>
    ...

    В этом варианте функция checkCancel работает как в Internet Explorer, так и в Mozilla Firefox, Apple Safari и Google Chrome, однако функция showSrc по-прежнему работает только в Internet Explorer. Дело в том, что элемент-источник события доступен в Internet Explorer как свойство события srcElement, а в других браузерах как target. Для учета различий можно применить один из нескольких методов:

  • Написать отдельные страницы для каждого браузера. Его мы обсуждать не будем.
  • Определить используемый браузер (при помощи объекта window.navigator ).
    function showSrc(e) {
          // userAgent в Internet Explorer любой версии содержит 
          // подстроку 'MSIE', которой нет в других браузерах
          var isIE = window.navigator.userAgent.indexOf('MSIE') > -1;
          if (isIE) {
            if (e.srcElement.tagName == "IMG")
              alert(e.srcElement.src);
          }
          else {
            if (e.target.tagName == "IMG")
              alert(e.target.src);
          }
        }

    Такой подход использовался и используется, однако он имеет существенный недостаток: мы не знаем, существует ли свойство target у объекта event в "другом" браузере.

  • Определить наличие интересующего свойства. Этот подход считается более плодотворным.
    function showSrc(e) {
          var elem = null;
          if (e.srcElement)
            elem = e.srcElement;
          else if (e.target)
            elem = e.target;
    
          if (elem != null) {
            if (elem.tagName == "IMG")
              alert(elem.src);
          }
          else {
            alert("Ваш браузер не поддерживает DOM модель");
          }
        }
  • Передача элемента-источника функции-обработчику

    Элемент (объект) может быть передан в сценарий как аргумент функции-обработчика.

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <title>Динамический стиль JavaScript</title>
    
    <script type="text/javascript">
      function ChangeStyleOver(elem) {
        elem.style.color = 'red';
        elem.style.fontSize = 'large';
      }
      function ChangeStyleOut(elem) {
        elem.style.color = 'Black';
        elem.style.fontSize = 'normal';
      }
    </script>
    
    </head>
    <body>
      <p style='color: Black; font-size: normal;' onmouseover="ChangeStyleOver(this)"
        onmouseout="ChangeStyleOut(this)">
        Наведите мышь, чтобы увидеть изменения</p>
    </body>
    <html>

    В этом примере стиль абзаца (элемент p ) задан в самом элементе. При наведении мыши (событие onmouseover ) вызывается функция ChangeStyleOver, в которую передается ссылка на объект-элемент (в этом контексте this указывает на элемент p ). Функция изменяет цвет текста (атрибут стиля color / свойство объекта style color ) и размер шрифта ( font-size / fontSize ). Обратите внимание на общее правило: все имена свойств начинаются со строчной буквы; если имя CSS атрибута содержит дефис (-), то дефис удаляется и следующая буква делается заглавной (стиль "спина верблюда"). Когда пользователь отводит мышь от элемента (событие onmouseout ), функция ChangeStyleOut возвращает стиль элемента к исходному. Изменения вступают в силу немедленно.

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

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
      <title>Динамический стиль CSS</title>
      <style type="text/css">
        p {
          color: Black;
          font-size: normal;
        }
        p:hover {
          color: red;
          font-size: large;
        }
      </style>
    </head>
    <body>
      <p>
        Наведите мышь, чтобы увидеть изменения</p>
    </body>
    <html>

    Элемент !DOCTYPE в данном примере обязателен, если просматривать страницу в Internet Explorer 7.0 или более новом. В предыдущих версиях пример не работает, поскольку псевдо-класс :hover поддерживается только элементом a (гиперссылкой).

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

    Для того, чтобы обработать событие, произошедшее на том или ином элементе, элемент-источник и событие должны быть связаны со сценарием обработки события (функцией-обработчиком). Сделать это можно различными способами.

  • Инструкция может помещаться в самом элементе.

    <p onmouseover="this.style.color='red'" onmouseout="this.style.color='blue'">
        текст</p>

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

  • В элементе помещается вызов функции-обработчика.

    <p onmouseover="ChangeStyleOver()" onmouseout="ChangeStyleOut()">
        текст</p>

    Функция-обработчик может принимать аргументы, которые при вызове помещаются в круглые скобки. Специальными случаями является объект-источник ( this ) и событие ( event ), рассмотренные выше. Даже если список аргументов пуст, круглые скобки обязательны.

  • Обработчик события может быть помещен в специальный блок сценария ( script ) с указанием элемента и события.

    <script type="text/javascript" for="oButton" event="onclick()">
        var sMessage1 = "Flip"
        var sMessage2 = "Flop"
        if (oButton.innerText == sMessage1) {
            oButton.innerText = sMessage2;
        }
        else {
          if (oButton.innerText == sMessage2) {
            oButton.innerText = sMessage1;
          }
        }
      </script>
    
      <button id="oButton">Flip</button>

    В этом примере сценарий выполняется, когда произошло событие, определенное в атрибуте EVENT, на элементе, id которого задан в атрибуте FOR. Пример работает только в Internet Explorer.

  • Функция-обработчик может быть связана с объектом-источником и событием программно, если по какой-либо причине нежелательно помещать ее вызов в тэг элемента. Рассмотрим этот прием на примере.

  • Пример: Фотогалерея

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

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
      <title>Моя галерея</title>
    </head>
    <body>
      <div>
        <div id="gallery">
          <a href="images/big/Photo1.jpg">
            <img src="images/small/Photo1.jpg" alt="Photo 1" /></a>
          <a href="images/big/Photo2.jpg">
            <img src="images/small/Photo2.jpg" alt="Photo 2" /></a>
          <a href="images/big/Photo3.jpg">
            <img src="images/small/Photo3.jpg" alt="Photo 3" /></a>
        </div>
      </div>
    </body>
    </html>

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

    <script type="text/javascript" src="Gallery.js"></script>

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

    #BigImgContainer
    {
    	border:solid 1px #ccc;
    	background:#fff;
    	width:150px;
    	height:150px;
    	position:absolute;
    	top:100px;
    	left:200px;
    	padding:8px;
    	display:none;
    }
    #BigImgContainer div
    {
    	color:Red;
    }

    Весь сценарий реализован в файле Gallery.js.

    /*
    Все свойства и методы объект document доступны только после загрузки страницы
    поэтому помещаем код в обработчик window.onload
    */
    window.onload = function() {
        // находим элемент, в котором должны располагаться 
        // гиперссылки на изображения
        var oGallery = document.getElementById('gallery');
        if (!oGallery) {
            // элемент не найден. сообщаем инструкции по использованию
            alert("Для правильной работы в документе должен быть элемент с id='gallery'\nПример:\n\
    <div id='gallery'>\n\
            <a href='MyBigPicture.jpg'><img src='SmallPic.jpg'></a>\n</div>");
            return; //выход из функции. Дальнейшие инструкции не выполняются
        } // if
    
        //Галерея найдена. Можно продолжать.
        // загружаем таблицу стилей
        var oLink = document.createElement('link');
        oLink.rel = 'stylesheet';
        oLink.type = 'text/css';
        oLink.href = 'BigImg.css';
        //и добавляем ее в элемент head
        document.documentElement.firstChild.appendChild(oLink);
    
        // создаем рамку, в которую будет загружаться изображение
        var oDiv = document.createElement('div');
        oDiv.id = 'BigImgContainer';
        //создаем вспомогательный элемент, 
        //отображаемый во время загрузки изображения
        var oLoading = document.createElement('div');
        oLoading.innerHTML = 'Идет загрузка...';
        // создаем объект изображение
        var oBigImg = new Image();
        //добавляем созданные объекты в дерево документа
        oDiv.appendChild(oLoading);
        oDiv.appendChild(oBigImg);
        document.body.appendChild(oDiv);
    
        //находим все гиперссылки в блоке gallery
        var oAnchors = oGallery.getElementsByTagName('a');
        for (var i = 0, n = oAnchors.length; i < n; i++) {
            //каждый элемент связываем с функцией-обработчиком
            oAnchors[i].onclick = function() {
                oDiv.style.width = ''; // вернуть к заданному по умолчанию
                oDiv.style.height = '';
                oDiv.style.display = 'block';
                oBigImg.style.display = 'none';
                // Загрузка мжет занять время. Показываем сообщение
                oLoading.style.display = 'block';
                //запускаем загрузку изображения
                oBigImg.src = this.href; // this == oAnchors[i]
                // Функция должна возвращать false, 
                // чтобы предотвратить переход по ссылке
                return false;
            } //oAnchors[i].onclick
        } //for
    
        //функция будет выполняться каждый раз после загрузки нового изображения
        oBigImg.onload = function() {
            // мы не знаем размеров изображения, пока оно не загружено
            oDiv.style.width = this.width + 'px'; // this == oBigImg
            oDiv.style.height = this.height + 'px';
            oLoading.style.display = 'none';
            oBigImg.style.display = 'block';
        } //oBigImg.onload
    
        // скрываем рамку с изображением по щелчку мыши
        oDiv.onclick = function() {
            this.style.display = 'none'; // this == oDiv
        } //oDiv.onclick
    }  //window.onload

    Сценарий содержит единственную анонимную функцию, которая вызывается автоматически после окончания загрузки HTML-документа. Внутри функции производит проверка наличия элемента, в котором должны располагаться ссылки, после чего создается контейнер для большой фотографии и объект Image, изначально не связанный с файлом изображения. Далее сценарий находит все гиперссылки, расположенные в объекте oGallery ( <div id="gallery"> ), и создает для каждой из них собственный обработчик события onclick. Гиперссылка имеет встроенное действие по щелчку мыши, но событие onclick происходит раньше, что позволяет предотвратить встроенное действие. Внутри обработчика происходит визуализация контейнера oDiv и инициализация загрузки большого изображения. Загрузка всегда происходит асинхронно, и функция-обработчик не дожидается ее окончания и возвращает false, чтобы предотвратить переход по гиперссылке. По окончании загрузки изображения срабатывает обработчик события oBigImg.onload. Только в этот момент сценарий может определить размер полученного изображения и скорректировать под него размеры контейнера. Наконец, обработчик oDiv.onclick позволяет убрать контейнер с изображением с экрана. Сценарий работает в любом браузере, поддерживающем JavaScript.

    Об использовании ключевого слова this

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

    //Пример 1
      function MyFoo(x, y) {
        this.x = x;
        this.y = y;
      }
    
      //Корректный вызов.
      var obj1 = new MyFoo(1, 2);
      /*
      MyFoo является конструктором объекта obj1, this является ссылкой на этот объект,
       который получает свойства x и y со значениями 1 и 2.
      */
    
      // Некорректный вызов.
      var obj2 = MyFoo(1, 2);
      /*
      MyFoo вызвана как обычная функция. Такой вызов осуществляется 
       в контексте объекта window. Объект window приобретает свойства x и y 
       со значениями 1 и 2, переменная obj2 значение undefined 
       (поскольку MyFoo не возвращает значения).
      */
      //Пример 2
      function SumSquares() {
        return this.x * this.x + this.y * this.y;
      }
      // Добавляем функцию SumSquares к методам объектов MyFoo.
      MyFoo.prototype.SumSquares = SumSquares;
      // Обратите внимание на отсутствие круглых скобок.
    
      //Корректный вызов.
      var a = obj1.SumSquares();
      // Здесь круглые скобки обязательны
    
      //Некорректный вызов.
      var b = SumSquares();
      /*
      Функция вызвана в контексте объекта window, у которого нет (не предполагается) свойств x и y.
      */
      //Чтобы избежать ошибок как в примере 2, 
      //лучше задать метод SumSquares как анонимную функцию.
      MyFoo.prototype.SumSquares = function() {
        return this.x * this.x + this.y * this.y;
      }

    Вопросы

  • Что такое событие?
  • Перечислите основные события.
  • Перечислите свойства объекта event.
  • Как получить доступ к объекту event в функции-обработчике события - в IE и других браузерах?
  • Как получить доступ к элементу-источнику события, имея объект event?
  • Как передать обработчику события ссылку на элемент-источник?
  • Как происходит связывание элемента-источника и события с функцией-обработчиком?
  • Какой смысл имеет ключевое слово this в функциях-обработчиках событий?
  • Страницы:

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

    Вот список основных событий:

  • События окна и документа: onload – документ загружен и противоположное событие onunload – документ выгружен из окна. Применяется к элементам body и frameset и объекту window.
  • События мыши: onclick – (одиночный) щелчок мыши и ondblclick – двойной щелчок. Одиночный щелчок может быть разложен на два события, onmousedown – кнопка мыши нажата и onmouseup – кнопка мыши отпущена. Отслеживаются также перемещения мыши: onmouseover – указатель мыши пересек границу объекта внутрь, onmouseout - указатель мыши пересек границу объекта наружу и onmousemove – мышь перемещается над объектом. Применяется к большинству элементов.
  • События клавиатуры: onkeypressed – нажата и отпущена клавиша, производящая символ. Это событие разделяется на два, onkeydown – клавиша нажата и onkeyup –клавиша отпущена. Применяется к большинству элементов.
  • События HTML-формы onsubmit и onreset и элементов формы onfocus, onblur и onchange рассмотрены в лекции "HTML формы".
  • Объект window.event создается браузером автоматически в момент возникновения события и доступен только внутри функции-обработчике события. Следует подчеркнуть, что модель объекта event в Internet Explorer существенно отличается от используемой в других браузерах, и требуется большое внимание при разработке динамических страниц, предназначенных для разных браузеров. Тем не менее, целый ряд свойств оказывается одинаковым во всех современных браузерах.

  • type – тип события без приставки "on". Например, событие onclick имеет тип click.
  • button – нажатая кнопка мыши (0 – ни одна, 1 – левая, 2 – правая, 3 – левая и правая, 4 – средняя, 5 – левая и средняя, 6 – правая и средняя, 7 – все три). Это свойство имеет смысл для событий мыши. В случае остальных событий имеет значение 0 независимо от действительного состояния кнопок.
  • clientX, clientY – координаты указателя мыши относительно верхнего левого угла клиентской области окна браузера.
  • screenX, screenY - координаты указателя мыши относительно верхнего левого угла дисплея.
  • keyCode – целочисленный код клавиши, которая вызвала событие клавиатуры. Коды клавиш различаются для различных аппаратных платформ (PC или Macintosh).
  • shiftKey, ctrlKey, altKey – истина ( true ), если во время события нажата также клавиша Shift, Ctrl или Alt, соответственно.
  • cancelBubble – запрещает ( true ) или разрешает ( false, по умолчанию) передачу события вверх по иерархии от элемента-источника к родительскому элементу. Каждое событие происходит на единственном элементе-источнике. Если элементу-источнику не сопоставлена функция-обработчик, событие передается вверх по дереву элементов, пока не встретит функцию-обработчик или не достигнет корневого элемента. Если функция-обработчик найдена, но не содержит cancelBubble=true, то передача вверх продолжается, если содержит, то прерывается.
  • На этом список общих свойств заканчивается. Большинство остальных свойств объекта event доступны в различных браузерах под разными именами.

  • Элемент-источник события в Internet Explorer доступен как свойство srcElement, в Mozilla Firefox и других как target.
  • События onmouseover и onmouseout в Internet Explorer показывают свойства-объекты откуда ( fromElement ) и куда ( toElement ). В Mozilla Firefox эти объекты являются свойством relatedTarget.
  • Список различий можно продолжить. Несмотря на различия в объектной модели между браузерами, писать веб-страницы, работающие в разных браузерах, можно и нужно. Рассмотрим это на примерах.

    Передача объекта event функции-обработчику

    Как было сказано, событие передается вверх по иерархии от элемента-источника к родительскому элементу и дальше. Процесс может быть остановлен присвоением свойству cancelBubble значения true.

    <script type="text/javascript">
        function checkCancel() {
          if (window.event.shiftKey)
            window.event.cancelBubble = true;
        }
        function showSrc() {
          if (window.event.srcElement.tagName == "IMG")
            alert(window.event.srcElement.src);
        }
      </script>
    ...
    <body onclick="showSrc()">
      <img onclick="checkCancel()" src="sample.gif">
    </body>
    ...

    В этом примере и элемент img, и его родительский элемент body имеют обработчик события onclick. При щелчке по изображению сначала вызывается функция checkCancel, которая проверяет состояние клавиши Shift. Если клавиша нажата, передача вверх по иерархии прекращается, если нет, выполняется также функция showSrc. К сожалению, этот пример работает только в Internet Explorer (начиная с версии 4.0). Различия в объектной модели Internet Explorer и большинства других браузеров максимальны именно в объекте event и доступе к нему. В случае Internet Explorer объект event доступен в функции-обработчике как глобальный объект, в других браузерах этот объект создается элементом-источником и доступен при вызове обработчика. Перепишем код следующим образом:

    <script type="text/javascript">
        function checkCancel(e) {
          if (e.shiftKey)
            e.cancelBubble = true;
        }
        function showSrc(e) {
          if (e.srcElement.tagName == "IMG")
            alert(e.srcElement.src);
        }
      </script>
    ...
    <body onclick="showSrc(event)">
      <img onclick="checkCancel(event)" src="sample.gif">
    </body>
    ...

    В этом варианте функция checkCancel работает как в Internet Explorer, так и в Mozilla Firefox, Apple Safari и Google Chrome, однако функция showSrc по-прежнему работает только в Internet Explorer. Дело в том, что элемент-источник события доступен в Internet Explorer как свойство события srcElement, а в других браузерах как target. Для учета различий можно применить один из нескольких методов:

  • Написать отдельные страницы для каждого браузера. Его мы обсуждать не будем.
  • Определить используемый браузер (при помощи объекта window.navigator ).
    function showSrc(e) {
          // userAgent в Internet Explorer любой версии содержит 
          // подстроку 'MSIE', которой нет в других браузерах
          var isIE = window.navigator.userAgent.indexOf('MSIE') > -1;
          if (isIE) {
            if (e.srcElement.tagName == "IMG")
              alert(e.srcElement.src);
          }
          else {
            if (e.target.tagName == "IMG")
              alert(e.target.src);
          }
        }

    Такой подход использовался и используется, однако он имеет существенный недостаток: мы не знаем, существует ли свойство target у объекта event в "другом" браузере.

  • Определить наличие интересующего свойства. Этот подход считается более плодотворным.
    function showSrc(e) {
          var elem = null;
          if (e.srcElement)
            elem = e.srcElement;
          else if (e.target)
            elem = e.target;
    
          if (elem != null) {
            if (elem.tagName == "IMG")
              alert(elem.src);
          }
          else {
            alert("Ваш браузер не поддерживает DOM модель");
          }
        }
  • Передача элемента-источника функции-обработчику

    Элемент (объект) может быть передан в сценарий как аргумент функции-обработчика.

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <title>Динамический стиль JavaScript</title>
    
    <script type="text/javascript">
      function ChangeStyleOver(elem) {
        elem.style.color = 'red';
        elem.style.fontSize = 'large';
      }
      function ChangeStyleOut(elem) {
        elem.style.color = 'Black';
        elem.style.fontSize = 'normal';
      }
    </script>
    
    </head>
    <body>
      <p style='color: Black; font-size: normal;' onmouseover="ChangeStyleOver(this)"
        onmouseout="ChangeStyleOut(this)">
        Наведите мышь, чтобы увидеть изменения</p>
    </body>
    <html>

    В этом примере стиль абзаца (элемент p ) задан в самом элементе. При наведении мыши (событие onmouseover ) вызывается функция ChangeStyleOver, в которую передается ссылка на объект-элемент (в этом контексте this указывает на элемент p ). Функция изменяет цвет текста (атрибут стиля color / свойство объекта style color ) и размер шрифта ( font-size / fontSize ). Обратите внимание на общее правило: все имена свойств начинаются со строчной буквы; если имя CSS атрибута содержит дефис (-), то дефис удаляется и следующая буква делается заглавной (стиль "спина верблюда"). Когда пользователь отводит мышь от элемента (событие onmouseout ), функция ChangeStyleOut возвращает стиль элемента к исходному. Изменения вступают в силу немедленно.

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

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
      <title>Динамический стиль CSS</title>
      <style type="text/css">
        p {
          color: Black;
          font-size: normal;
        }
        p:hover {
          color: red;
          font-size: large;
        }
      </style>
    </head>
    <body>
      <p>
        Наведите мышь, чтобы увидеть изменения</p>
    </body>
    <html>

    Элемент !DOCTYPE в данном примере обязателен, если просматривать страницу в Internet Explorer 7.0 или более новом. В предыдущих версиях пример не работает, поскольку псевдо-класс :hover поддерживается только элементом a (гиперссылкой).

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

    Для того, чтобы обработать событие, произошедшее на том или ином элементе, элемент-источник и событие должны быть связаны со сценарием обработки события (функцией-обработчиком). Сделать это можно различными способами.

  • Инструкция может помещаться в самом элементе.

    <p onmouseover="this.style.color='red'" onmouseout="this.style.color='blue'">
        текст</p>

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

  • В элементе помещается вызов функции-обработчика.

    <p onmouseover="ChangeStyleOver()" onmouseout="ChangeStyleOut()">
        текст</p>

    Функция-обработчик может принимать аргументы, которые при вызове помещаются в круглые скобки. Специальными случаями является объект-источник ( this ) и событие ( event ), рассмотренные выше. Даже если список аргументов пуст, круглые скобки обязательны.

  • Обработчик события может быть помещен в специальный блок сценария ( script ) с указанием элемента и события.

    <script type="text/javascript" for="oButton" event="onclick()">
        var sMessage1 = "Flip"
        var sMessage2 = "Flop"
        if (oButton.innerText == sMessage1) {
            oButton.innerText = sMessage2;
        }
        else {
          if (oButton.innerText == sMessage2) {
            oButton.innerText = sMessage1;
          }
        }
      </script>
    
      <button id="oButton">Flip</button>

    В этом примере сценарий выполняется, когда произошло событие, определенное в атрибуте EVENT, на элементе, id которого задан в атрибуте FOR. Пример работает только в Internet Explorer.

  • Функция-обработчик может быть связана с объектом-источником и событием программно, если по какой-либо причине нежелательно помещать ее вызов в тэг элемента. Рассмотрим этот прием на примере.

  • Пример: Фотогалерея

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

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
      <title>Моя галерея</title>
    </head>
    <body>
      <div>
        <div id="gallery">
          <a href="images/big/Photo1.jpg">
            <img src="images/small/Photo1.jpg" alt="Photo 1" /></a>
          <a href="images/big/Photo2.jpg">
            <img src="images/small/Photo2.jpg" alt="Photo 2" /></a>
          <a href="images/big/Photo3.jpg">
            <img src="images/small/Photo3.jpg" alt="Photo 3" /></a>
        </div>
      </div>
    </body>
    </html>

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

    <script type="text/javascript" src="Gallery.js"></script>

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

    #BigImgContainer
    {
    	border:solid 1px #ccc;
    	background:#fff;
    	width:150px;
    	height:150px;
    	position:absolute;
    	top:100px;
    	left:200px;
    	padding:8px;
    	display:none;
    }
    #BigImgContainer div
    {
    	color:Red;
    }

    Весь сценарий реализован в файле Gallery.js.

    /*
    Все свойства и методы объект document доступны только после загрузки страницы
    поэтому помещаем код в обработчик window.onload
    */
    window.onload = function() {
        // находим элемент, в котором должны располагаться 
        // гиперссылки на изображения
        var oGallery = document.getElementById('gallery');
        if (!oGallery) {
            // элемент не найден. сообщаем инструкции по использованию
            alert("Для правильной работы в документе должен быть элемент с id='gallery'\nПример:\n\
    <div id='gallery'>\n\
            <a href='MyBigPicture.jpg'><img src='SmallPic.jpg'></a>\n</div>");
            return; //выход из функции. Дальнейшие инструкции не выполняются
        } // if
    
        //Галерея найдена. Можно продолжать.
        // загружаем таблицу стилей
        var oLink = document.createElement('link');
        oLink.rel = 'stylesheet';
        oLink.type = 'text/css';
        oLink.href = 'BigImg.css';
        //и добавляем ее в элемент head
        document.documentElement.firstChild.appendChild(oLink);
    
        // создаем рамку, в которую будет загружаться изображение
        var oDiv = document.createElement('div');
        oDiv.id = 'BigImgContainer';
        //создаем вспомогательный элемент, 
        //отображаемый во время загрузки изображения
        var oLoading = document.createElement('div');
        oLoading.innerHTML = 'Идет загрузка...';
        // создаем объект изображение
        var oBigImg = new Image();
        //добавляем созданные объекты в дерево документа
        oDiv.appendChild(oLoading);
        oDiv.appendChild(oBigImg);
        document.body.appendChild(oDiv);
    
        //находим все гиперссылки в блоке gallery
        var oAnchors = oGallery.getElementsByTagName('a');
        for (var i = 0, n = oAnchors.length; i < n; i++) {
            //каждый элемент связываем с функцией-обработчиком
            oAnchors[i].onclick = function() {
                oDiv.style.width = ''; // вернуть к заданному по умолчанию
                oDiv.style.height = '';
                oDiv.style.display = 'block';
                oBigImg.style.display = 'none';
                // Загрузка мжет занять время. Показываем сообщение
                oLoading.style.display = 'block';
                //запускаем загрузку изображения
                oBigImg.src = this.href; // this == oAnchors[i]
                // Функция должна возвращать false, 
                // чтобы предотвратить переход по ссылке
                return false;
            } //oAnchors[i].onclick
        } //for
    
        //функция будет выполняться каждый раз после загрузки нового изображения
        oBigImg.onload = function() {
            // мы не знаем размеров изображения, пока оно не загружено
            oDiv.style.width = this.width + 'px'; // this == oBigImg
            oDiv.style.height = this.height + 'px';
            oLoading.style.display = 'none';
            oBigImg.style.display = 'block';
        } //oBigImg.onload
    
        // скрываем рамку с изображением по щелчку мыши
        oDiv.onclick = function() {
            this.style.display = 'none'; // this == oDiv
        } //oDiv.onclick
    }  //window.onload

    Сценарий содержит единственную анонимную функцию, которая вызывается автоматически после окончания загрузки HTML-документа. Внутри функции производит проверка наличия элемента, в котором должны располагаться ссылки, после чего создается контейнер для большой фотографии и объект Image, изначально не связанный с файлом изображения. Далее сценарий находит все гиперссылки, расположенные в объекте oGallery ( <div id="gallery"> ), и создает для каждой из них собственный обработчик события onclick. Гиперссылка имеет встроенное действие по щелчку мыши, но событие onclick происходит раньше, что позволяет предотвратить встроенное действие. Внутри обработчика происходит визуализация контейнера oDiv и инициализация загрузки большого изображения. Загрузка всегда происходит асинхронно, и функция-обработчик не дожидается ее окончания и возвращает false, чтобы предотвратить переход по гиперссылке. По окончании загрузки изображения срабатывает обработчик события oBigImg.onload. Только в этот момент сценарий может определить размер полученного изображения и скорректировать под него размеры контейнера. Наконец, обработчик oDiv.onclick позволяет убрать контейнер с изображением с экрана. Сценарий работает в любом браузере, поддерживающем JavaScript.

    Об использовании ключевого слова this

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

    //Пример 1
      function MyFoo(x, y) {
        this.x = x;
        this.y = y;
      }
    
      //Корректный вызов.
      var obj1 = new MyFoo(1, 2);
      /*
      MyFoo является конструктором объекта obj1, this является ссылкой на этот объект,
       который получает свойства x и y со значениями 1 и 2.
      */
    
      // Некорректный вызов.
      var obj2 = MyFoo(1, 2);
      /*
      MyFoo вызвана как обычная функция. Такой вызов осуществляется 
       в контексте объекта window. Объект window приобретает свойства x и y 
       со значениями 1 и 2, переменная obj2 значение undefined 
       (поскольку MyFoo не возвращает значения).
      */
      //Пример 2
      function SumSquares() {
        return this.x * this.x + this.y * this.y;
      }
      // Добавляем функцию SumSquares к методам объектов MyFoo.
      MyFoo.prototype.SumSquares = SumSquares;
      // Обратите внимание на отсутствие круглых скобок.
    
      //Корректный вызов.
      var a = obj1.SumSquares();
      // Здесь круглые скобки обязательны
    
      //Некорректный вызов.
      var b = SumSquares();
      /*
      Функция вызвана в контексте объекта window, у которого нет (не предполагается) свойств x и y.
      */
      //Чтобы избежать ошибок как в примере 2, 
      //лучше задать метод SumSquares как анонимную функцию.
      MyFoo.prototype.SumSquares = function() {
        return this.x * this.x + this.y * this.y;
      }

    Вопросы

  • Что такое событие?
  • Перечислите основные события.
  • Перечислите свойства объекта event.
  • Как получить доступ к объекту event в функции-обработчике события - в IE и других браузерах?
  • Как получить доступ к элементу-источнику события, имея объект event?
  • Как передать обработчику события ссылку на элемент-источник?
  • Как происходит связывание элемента-источника и события с функцией-обработчиком?
  • Какой смысл имеет ключевое слово this в функциях-обработчиках событий?
  • Вернуться к учебному плану