JavaScript. Подробное руководство, 6-е издание, стр. 177

События, адресатом которых является элемент документа, часто распространяются вверх по дереву документа и этот процесс называется «всплытием». Если, например, пользователь щелкнет мышью на элементе <button>, событие «click» будет передано кнопке. Если это событие останется необработанным (и его распространение не будет остановлено) функцией, зарегистрированной в элементе кнопки, событие всплывет до элемента, в который вложена эта кнопка, и будет вызван обработчик события

<b>«click»,</b>
зарегистрированный в этом контейнерном элементе.

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

<b>addEventListener(),</b>
который позволяет регистрировать множество обработчиков:

<b>window.addEventListener(&quot;load&quot;, function() {...}, false);</b>

<b>request.addEventListener(&quot;readystatechange&quot;, function() {...}, false);</b>

Обратите внимание, что первым аргументом этой функции передается имя события. Несмотря на то что метод

<b>addEventListener()</b>
был определен стандартом уже более десяти лет тому назад, корпорация Microsoft только недавно реализовала его в IE9. В IE8 и в. более ранних версиях необходимо использовать похожий метод, который называется
<b>attachEvent()</b>
:

<b>window.attachEvent(&quot;onload&quot;, function() {...});</b>

Подробнее о функциях

<b>addEventlistener()</b>
и
<b>attachEvent()</b>
рассказывается в главе 17.

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

<b>onerror</b>
объекта
<b>Window</b>
присвоить функцию, она будет вызываться при появлении ошибочных ситуаций (или необработанных исключений) в программном коде (раздел 14.6). Кроме того, функции
<b>setTimeout()</b>
и
<b>setlnterval()</b>
(они являются методами глобального объекта Window и потому в клиентском JavaScript считаются глобальными функциями) вызывают указанные им функции по истечении определенного интервала времени. Функции, которые передаются
<b>setTimeout(),</b>
регистрируются не так, как настоящие обработчики событий, и обычно они называются «функциями обратного вызова», а не «обработчиками», но они, как и обработчики событий, выполняются асинхронно. Подробнее о функциях
<b>setTimeout()</b>
и
<b>setlnterval()</b>
рассказывается в разделе 14.1.

Пример 13.5 демонстрирует применение функций

<b>setTimeout(), addEventListener()</b>
и
<b>attachEvent()</b>
внутри функции
<b>onLoad(),</b>
которая регистрирует обработчик события окончания загрузки документа.
<b>onLoad()</b>
- весьма полезная функция, и мы часто будем использовать ее в примерах на протяжении оставшейся части книги.

Пример 13.5.

<b>onLoad():</b>
вызов функции по окончании загрузки документа

<b>// Регистрирует функцию f, которая должна вызываться по окончании загрузки документа.</b>

<b>// Если документ уже загружен, функция f будет вызвана асинхронно и в кратчайшие сроки, </b>

<b>function onLoad(f) {</b>

<b>  if (onLoad.loaded) // Если документ уже загружен</b>

<b>    window.setTimeout(f, 0); // Вызвать f, как можно скорее</b>

<b>  else if (window.addEventListener) // Стандартный метод регистрации событий </b>

<b>    window.addEventListener(&quot;load&quot;, f, false); </b>

<b>  else if (window.attachEvent) // В IE8 и в более ранних версиях</b>

<b>    window.attachEvent(&quot;onload&quot;, f); // используется этот метод</b>

<b>}</b>

<b>// Сначала установить флаг, указывающий, что документ еще не загружен. </b>

<b>onLoad.loaded = false;</b>

<b>// И зарегистрировать функцию, которая сбросит флаг после загрузки документа. </b>

<b>onLoad(function() { onLoad.loaded = true; });</b>

13.3.3. Модель потоков выполнения в клиентском JavaScript

Ядро языка JavaScript не имеет механизма одновременного выполнения нескольких потоков управления, и клиентский язык JavaScript не добавляет такой возможности. Стандарт HTML5 определяет механизм поддержки фонового потока выполнения «Web Workers» (подробнее об этом механизме рассказывается ниже), тем не менее JavaScript-код на стороне клиента выполняется в единственном потоке управления. Даже когда параллельное выполнение возможно, интерпретатор JavaScript не может обнаружить этот факт.

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

Выполнение в единственном потоке означает, что веб-броузер должен прекратить откликаться на действия пользователя на время выполнения сценария или обработчика события. Это накладывает определенные требования: сценарии и обработчики событий в JavaScript не должны исполняться слишком долго. Если сценарий производит объемные и интенсивные вычисления, это вызовет задержку во время загрузки документа, и пользователь не увидит его содержимое, пока сценарий не закончит свою работу. Если продолжительные по времени операции выполняются в обработчике события, броузер может оказаться неспособным откликаться на действия пользователя, заставляя его думать, что программа «зависла». [29] (