XUL/Работаем с событиями

Материал из MediaWiki
Перейти к навигации Перейти к поиску

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

Термины

Обычно различают три момента, свойственные распространению событий в приложении:

  • Capturing - захват события другими элементами на его пути к элементу-цели;
  • Bubbling - всплытие события вверх по дереву элементов, после того, как оно всё-таки добралось до элемента-цели;
  • Canceling - отмена реагирования элементом по умолчанию при приходе события.

Первые два пункта уже известны Qt программистам. При возникновении события, низлежащая система создаёт объект, и выбрасывает его в мир. Захват есть ни что иное, как перехват события на пути к элементу другими обработчиками (аналогично фильтрам событий в Qt). Перехватчик может запретить событию распространяться дальше. Как только событие всё-таки пришло в пункт назначения, оно может быть обработано. После обработки события тулкит направляет его вверх по дереву элементов, если не указано другое. Это и есть всплытие. Мы можем явно указать тулкиту, что не хотим, чтобы событие всплывало вверх (т.е. утверждение, что события не тонут, неверно - тонут и ещё как :) ).

Описанные здесь интерфейсы и типы событий относятся к DOM2.

При обработке событий в JavaScript используют первый способ, Capturing.

Базовый интерфейс элемента

Итак, рассмотрим базовый интерфейс для элемента, который хочет работать с событиями:


interface EventTarget
{
  void       addEventListener(in DOMString type, 
                              in EventListener listener, 
                              in boolean useCapture);

  void       removeEventListener(in DOMString type, 
                                 in EventListener listener, 
                                 in boolean useCapture);

  boolean    dispatchEvent(in Event evt)
                                raises(EventException);
};

Этот интерфейс наследуется всеми XUL элементами, а это значит, что каждый XUL элемент может подписываться на события. Если мы хотим начать или перестать отслеживать события какого-то объекта, мы должны вызвать именно его методы addEventListener, removeEventListener или dispatchEvent.

Рассмотрим методы:

  • addEventListener - добавляет фильтр событий

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


function func()
{
    alert("We are here");
}

addEventListener("load", func, false);

Здесь мы установили на глобальный объект window фильтр на событие load (загрузка документа) с именем func. Как только документ загрузится, сгенерируется событие load, и выполнится наша функция func(). Можно ставить два одинаковых обработчика на одинаковые элементы, только если последний булев параметр различается. В таком случае снимать обработчики нужно тоже по отдельности (см. removeEventListener).

  • removeEventListener - снимает обработчик

Аргументы аналогичны.

  • dispatchEvent - создаёт событие, как будто оно возникло само

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


function dispatch()
{
    var event = document.createEvent('Events');
    event.initEvent('load', false, false);
    window.dispatchEvent(event);
}

function func()
{
    alert("We are here");
}

addEventListener("load", func, false);

setTimeout("dispatch();", 3000);

По таймеру через 3 секунды вызываем функцию dispatch(), которая создаёт объект события, и передаёт его глобальному объекту window. Т.к. на этом объекте уже стоит обработчик func(), то он и вызывается повторно.

Функция-обработчик

Функция-обработчик - это функция, которая автоматически вызывается при возникновении события. Она может принимать в качестве аргумента непосредственно сам объект события. Функция, объявленная без параметров, не использует объект события. В этом примере мы передаём объект события в функцию-обработчик:


function func(evt)
{
    alert(evt);
}

addEventListener("load", func, false);

Как только документ загрузится, сгенерируется событие load, и выполнится наша функция func() с аргументом события, которое мы и показываем в сообщении. Ещё один способ сделать это через замыкание:


function func(evt)
{
    alert(evt);
}

// сложнее, но по сути тоже самое
addEventListener("load", function(e) { func(e); }, false);

Обработчики-атрибуты

XUL позволяет задавать обработчики для событий прямо внутри элементов в XUL документе. В этом случае имя атрибута складывается из префикса «on» и типа события (load, mouseover, click и т.д.). Например:


<button label="click me!" onclick="alert('Clicked');" />

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


<button label="click me!" onmouseover="alert('Over');" />

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

Объект event

Объект event создаётся тулкитом в обработчиках событий, заданных в качестве атрибутов элементов. Например:


<button label="click me!" onclick="alert(event);" />

Здесь при щелчке на кнопку мы выводим на экран сообщение с типом полученного события (в данном случае это MouseEvent).

Базовый интерфейс события

Базовый интерфейс события выглядит следующим образом:


interface Event
{
  const unsigned short      CAPTURING_PHASE        = 1;
  const unsigned short      AT_TARGET              = 2;
  const unsigned short      BUBBLING_PHASE         = 3;

  readonly attribute DOMString        type;
  readonly attribute EventTarget      target;
  readonly attribute EventTarget      currentTarget;
  readonly attribute unsigned short   eventPhase;
  readonly attribute boolean          bubbles;
  readonly attribute boolean          cancelable;
  readonly attribute DOMTimeStamp     timeStamp;

  void               stopPropagation();
  void               preventDefault();
  void               initEvent(in DOMString eventTypeArg, 
                               in boolean canBubbleArg, 
                               in boolean cancelableArg);
};

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

  • stopPropagation - останавливает распространение события

Остановить распространение события можно как в случае с Capturing, так и в случае с Bubbling. Вызов этого метода означает, что мы больше не хотим, чтобы кто-то ещё обработал данное конкретное событие.

  • preventDefault - отмена действия по умолчанию

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


<script type="application/x-javascript">
function func(evt)
{
    evt.preventDefault();
}
</script>

<button label="click me!"
        onmousedown="func(event);"
        onmouseup="func(event);"
        onclick="alert('Clicked');" />

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

  • initEvent - инициализирует ранее созданный объект события

В качестве аргументов принимает

    1. тип события в виде строки («click», «mousedown», и т.д.);
    2. булево значение, определяющее, может ли событие всплывать;
    3. булево значение, определяющее, можно ли отменять действие по умолчанию.

Например:


// создали объект события
var event = document.createEvent('Events');

// инициализация
event.initEvent('mouseover', true, true);

// отсылка какому-либо объекту
obj.dispatchEvent(event);

Предопределённые события

DOM предоставляет пользователю несколько типов предопределённых событий, каждое из которых имеет свои собственные свойства и методы. Рассмотрим их подробнее.

UIEvent (событие пользовательского интерфейса)

Имеет такое представление:


interface UIEvent : Event
{
  readonly attribute views::AbstractView  view;
  readonly attribute long             detail;

  void       initUIEvent(in DOMString typeArg, 
                         in boolean canBubbleArg, 
                         in boolean cancelableArg, 
                         in views::AbstractView viewArg, 
                         in long detailArg);
};

Типы данного события могут быть «DOMFocusIn», «DOMFocusOut», «DOMActivate».

MouseEvent (событие мыши)

Имеет такое представление:


interface MouseEvent : UIEvent
{
  readonly attribute long             screenX;
  readonly attribute long             screenY;
  readonly attribute long             clientX;
  readonly attribute long             clientY;
  readonly attribute boolean          ctrlKey;
  readonly attribute boolean          shiftKey;
  readonly attribute boolean          altKey;
  readonly attribute boolean          metaKey;
  readonly attribute unsigned short   button;
  readonly attribute EventTarget      relatedTarget;

  void       initMouseEvent(in DOMString typeArg, 
                            in boolean canBubbleArg, 
                            in boolean cancelableArg, 
                            in views::AbstractView viewArg, 
                            in long detailArg, 
                            in long screenXArg, 
                            in long screenYArg, 
                            in long clientXArg, 
                            in long clientYArg, 
                            in boolean ctrlKeyArg, 
                            in boolean altKeyArg, 
                            in boolean shiftKeyArg, 
                            in boolean metaKeyArg, 
                            in unsigned short buttonArg, 
                            in EventTarget relatedTargetArg);
};

Как видно, событие мыши наследуется от события UIEvent. Событие мыши включает в себя координаты курсора на экране, координаты курсора относительно элемента, флаги, определяющие состояние клавиш Ctrl, Alt, Shift, номер нажатой кнопки. Номер нажатой кнопки имеет смысл только в обработчике события нажатия или отжатия кнопки («mousedown», «mouseup»). Например:


<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>

<window
    title="Example"
    width="320"
    height="200"
    xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">

<script type="application/x-javascript">

function func(e)
{
    // выводим в шелл сообщение с глобальными координатами
    // мыши и номером нажатой кнопки
    dump(e.screenX
            + ","
            + e.screenY
            + " Mouse button: "
            + e.button
            + "\n");
}

addEventListener("mousemove", func, false);
addEventListener("mousedown", func, false);

</script>

</window>

Типы данного события могут быть «click», «mousedown», «mouseup», «mouseover», «mousemove», «mouseout».

MutationEvent (событие изменения документа)

Имеет такое представление:


interface MutationEvent : Event
{

  // attrChangeType
  const unsigned short      MODIFICATION       = 1;
  const unsigned short      ADDITION           = 2;
  const unsigned short      REMOVAL            = 3;

  readonly attribute Node             relatedNode;
  readonly attribute DOMString        prevValue;
  readonly attribute DOMString        newValue;
  readonly attribute DOMString        attrName;
  readonly attribute unsigned short   attrChange;

  void       initMutationEvent(in DOMString typeArg, 
                               in boolean canBubbleArg, 
                               in boolean cancelableArg, 
                               in Node relatedNodeArg, 
                               in DOMString prevValueArg, 
                               in DOMString newValueArg, 
                               in DOMString attrNameArg, 
                               in unsigned short attrChangeArg);
};

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

Свойства:

  • attrChange - тип модификации документа при событии DOMAttrModified

Может быть MODIFICATION, ADDITION, or REMOVAL.

  • attrName - имя модифицированного элемента при событии DOMAttrModified
  • newValue - новое значение модифицированного элемента при событии DOMAttrModified и DOMCharDataModified
  • prevValue - прошлое значение модифицированного элемента при событии DOMAttrModified и DOMCharDataModified
  • relatedNode

Типы данного события могут быть «DOMSubtreeModified», «DOMNodeInserted», «DOMNodeRemoved», «DOMNodeRemovedFromDocument», «DOMNodeInsertedIntoDocument», «DOMAttrModified», «DOMCharacterDataModified».

HTML события

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

  • load - загрузка документа завершена;
  • unload - документ убран из окна или фрейма;
  • abort - неожиданная остановка загрузки, например остановка загрузки HTML страницы до того, как какое-то изображение успело загрузится;
  • error - произошла ошибка загрузки документа или выполнения скрипта;
  • select - пользователь выделил текст в текстовом поле ввода;
  • change - элемент потерял фокус, и при этом ещё изменился с момента получения фокуса (поле ввода например);
  • submit - отправка формы;
  • reset - возврат формы к значениям по умолчанию;
  • focus - элемент получил фокус;
  • blur - элемент потерял фокус;
  • resize - размер контейнера, в котором отображается документ, изменился;
  • scroll - прокрутка контейнера, в котором отображается документ.

Например:


<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>

<window
    title="Example"
    width="320"
    height="200"
    xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">

<button id="b" label="click me" />
<textbox id="t" />

<script type="application/x-javascript">

function fFocus()
{
    dump("Focus\n");
}

function fChange()
{
    dump("Change\n");
}

// мы хотим получать события фокуса для кнопки
document.getElementById("b").addEventListener("focus", fFocus, false);

// мы также хотим получать события изменения для поля ввода,
// не забываем, что оно приходит только после потери фокуса,
// и только если содержимое поля ввода изменилось
document.getElementById("t").addEventListener("change", fChange, false);

</script>

</window>

Свои события

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


function dispatch()
{
    // создаём событие
    var event = document.createEvent('Events');
    event.initEvent('myevent', false, false);

    // добавили в событие своё свойство
    event.customdata = "Hello";

    // отослали событие
    window.dispatchEvent(event);
}

function func(evt)
{
    // наше событие должно иметь свойство "customdata",
    // показываем его в сообщении
    alert(evt.customdata);
}

// добавили обработчик своего события
addEventListener("myevent", func, false);

// создаём и посылаем своё событие
dispatch();

Ссылки