Мастер-классы по Javascript Екатеринбург Ростов-на-Дону Москва Узнать больше...
Содержание (скрыть) Содержание (показать)

События "onload", "onbeforeunload" и "onerror"

  1. Загрузка SCRIPT
    1. onload
    2. onerror
    3. IE<9: onreadystatechange
    4. Кросс-браузерное решение
  2. Обработчики на window
    1. window.onerror
    2. window.onload
    3. window.onunload
    4. window.onbeforeunload
  3. Обработчики на IMG, IFRAME, LINK
  4. Итого

Браузер позволяет отслеживать загрузку внешних ресурсов — скриптов, ифреймов, картинок и других.

Для этого есть два события:

  • onload — если загрузка успешна.
  • onerror — если при загрузке произошла ошибка.

Кроме того, обработчик window.onerror вызывается при ошибках JavaScript, которые не были пойманы try..catch.

Загрузка SCRIPT

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

Подгрузить внешний скрипт — достаточно просто:

var script = document.createElement('script');
script.src = "my.js";
document.documentElement.appendChild(script);

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

onload

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

var script = document.createElement('script');
script.src = "http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.js"
document.documentElement.appendChild(script);

*!*
script.onload = function() {
  alert(jQuery); 
}
*/!*

..А что, если загрузка скрипта не удалась? Например, такого скрипта на сервере нет (ошибка 404).

onerror

Любые ошибки загрузки (но не выполнения) скрипта отслеживаются обработчиком onerror:

var script = document.createElement('script');
script.src = "http://ajax.googleapis.com/404.js"
document.documentElement.appendChild(script);

*!*
script.onerror = function() {
  alert("Ошибка: " + this.src); 
}
*/!*

IE<9: onreadystatechange

Примеры выше работают во всех браузерах, кроме IE<9.

В IE для отслеживания загрузки есть другое событие: onreadystatechange. Оно срабатывает многократно, при каждом обновлении состояния загрузки.

Текущая стадия процесса находится в script.readyState:

loading
В процессе загрузки.
loaded
Получен ответ с сервера — скрипт или ошибка. Скрипт на фазе loaded может быть ещё не выполнен.
complete
Скрипт выполнен.

Например, рабочий скрипт:

var script = document.createElement('script');
script.src = "http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.js";
document.documentElement.appendChild(script);

*!*
script.onreadystatechange = function() {
  alert(this.readyState); // loading -> loaded -> complete
}
*/!*

Скрипт с ошибкой:

var script = document.createElement('script');
script.src = "http://ajax.googleapis.com/404.js";
document.documentElement.appendChild(script);

*!*
script.onreadystatechange = function() {
  alert(this.readyState);  // loading -> loaded
}
*/!*

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

Нет особой стадии для ошибки. В примере выше это видно, обработка останавливается на loaded.

Итак, самое надёжное средство для IE<9 поймать загрузку (и ошибку) — это повесить обработчик на событие loaded. Так как скрипт может быть ещё не выполнен к этому моменту, то вызов функции лучше сделать через setTimeout(.., 0).

На тот случай, если скрипт прыгнет сразу на complete — поставим обработчик и на этой стадии тоже.

Пример вызывает afterLoad после загрузки скрипта. Работает только в IE:

var script = document.createElement('script');
script.src = "http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.js";
document.documentElement.appendChild(script);

function afterLoad() {
  alert("Загрузка завершена: " + typeof(jQuery));
}

*!*
script.onreadystatechange = function() {
  if (this.readyState == "complete") { // на случай пропуска loaded
    afterLoad(); // (2)
  }

  if (this.readyState == "loaded") {
    setTimeout(afterLoad, 0);  // (1)
    
    // убираем обработчик, чтобы не сработал на complete
    this.onreadystatechange = null; 
  }
}
*/!*

Вызов (1) выполнится при первой загрузке скрипта, а (2) — при второй, когда он уже будет в кэше, и стадия станет сразу complete.

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

Кросс-браузерное решение

Для кросс-браузерности поставим обработчик на все три события: onload, onerror, onreadystatechange.

Пример ниже выполняет функцию afterLoad после загрузки скрипта.

Работает везде:

var script = document.createElement('script');
script.src = "http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.js";
document.documentElement.appendChild(script);

function afterLoad() {
  alert("Загрузка завершена: " + typeof(jQuery));
}

script.onload = script.onerror = function() {
  if (!this.executed) { // выполнится только один раз
    this.executed = true;
    afterLoad();
  }
};

script.onreadystatechange = function() {
  var self = this;
  if (this.readyState == "complete" || this.readyState == "loaded") {
    setTimeout(function() { self.onload() }, 0);// сохранить "this" для onload
  }
};

Обработчики на window

window.onerror

Обработчик window.onerror вызывается в случае ошибки, выпавшей из всех блоков try..catch:

Аргументы обработчика:

message
Сообщение об ошибке.
source
Файл
lineno
Номер строки с ошибкой

Например:

<script>
window.onerror = function(message, source, lineno) {
  alert("Ошибка:"+message +"\n" +
    "файл:" + source + "\n" +
    "строка:" + lineno);
};

</script>

<body>
  <button onclick="alert(blablabla);">alert(blablabla)</button>
</body>
Этот документ в ифрейме:
Открыть в новом окне

Реакция браузера на ошибку (запуск отладчика?) может быть отменена через return false из обработчика.

window.onload

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

Пример ниже выведет alert лишь после полной загрузки окна, включая IFRAME, стиль, картинки:

<iframe src="http://example.com/" style="height:60px"></iframe>
<link type="text/css" rel="stylesheet" href="/some.css">
<img src="/some.png">
<script>
*!*
  window.onload = function() {
    alert('Документ и все ресурсы загружены')
  }
*/!*
</script>

window.onunload

Когда человек уходит со страницы или закрывает окно, срабатывает window.unload. В нём можно, например, закрыть вспомогательные popup-окна, но отменить сам переход нельзя.

Это позволяет другое событие — window.onbeforeunload.

window.onbeforeunload

Если посетитель инициировал переход на другую страницу или нажал «закрыть окно», то обработчик onbeforeunload может приостановить процесс и спросить подтверждение.

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

window.onbeforeunload = function() {
  return "Данные не сохранены. Точно перейти?";
};

Особенности:

  • Событие не поддерживается Opera
  • Firefox игнорирует текст, а показывает своё сообщение. Это сделано в целях безопасности.

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

Открыть в новом окне

Обработчики на IMG, IFRAME, LINK

Поддержка событий для других типов ресурсов:

IMG
Пддерживают onload/onerror во всех браузерах.
IFRAME
Поддерживает onload во всех браузерах. Это событие срабатывает как при успешной загрузке, так и при ошибке.
LINK (стили)
Поддерживает onload/onerror в некоторых браузерах. Замечательная статья про обработчик на загрузку стиля: When is a styleshet really loaded?.

Итого

В этой статье мы рассмотрели события onload/onerror и window.onbeforeunload.

Их можно обобщить, разделив на рецепты:

Отловить загрузку скрипта (включая ошибку)
Ставим обработчики на onload + onerror + (для IE<9) onreadystatechange, как указано в рецепте выше
Отловить загрузку изображения (включая ошибку)
Ставим обработчики на onload + onerror
var img = document.createElement('img');
img.onload = function() { alert("OK "+this.src };
img.onerror = function() { alert("FAIL "+this.src }
img.src = ...
Изображения начинают загружаться сразу при создании, не нужно их для этого вставлять в HTML.

Чтобы работало в IE<9, src нужно ставить после onload/onerror.

Отловить загрузку IFRAME
Поддерживается только обработчик onload. Он сработает, когда IFRAME загрузится, со всеми подресурсами, а также в случае ошибки.

<iframe src="http://example.com/" style="height:60px"></iframe>
<script>
*!*
  document.getElementsByTagName('iframe')[0].onload = function() {
    alert('IFRAME загружен')
  }
*/!*
</script>

Отловить полную загрузку страницы
Обработчик window.onload (сработает, когда окно загрузится со всеми ресурсами).
Далее мы рассмотрим событие onDOMContentLoaded, которое срабатывает при загрузке документа и не ждёт картинок, стилей, ифреймов.
Отловить загрузку файла со стилями
When is a styleshet really loaded?.

Обычно, до того как изображение загрузится (или при отключенных картинках), посетитель видит пустое место с текстом из «ALT». Но этот атрибут не допускает HTML-форматирования.

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

Реализуйте «красивый» (HTML) аналог alt при помощи CSS/JavaScript, который затем будет заменён картинкой сразу же как только она загрузится. А если загрузка не состоится — то не заменён.

Демо: (нажмите «перезагрузить», чтобы увидеть процесс загрузки и замены)

Картинки для bing специально нет, так что текст остается «как есть».

Исходный документ: tutorial/browser/events/img-onload-src/index.html содержит разметку текста и изображения.

Решение, шаг 1
Решение
Решение, шаг 1

Текст на странице пусть будет изначально DIV, с классом img-replace и атрибутом data-src для картинки.

Функция replaceImg() должна искать такие DIV и загружать изображение с указанным src. По onload осуществляется замена DIV на картинку.

Решение, шаг 2
Решение, шаг 2

Решение: tutorial/browser/events/img-onload/index.html.

Создайте функцию preloadImages(sources, callback), которая предзагружает изображиния из массива sources, и после загрузки вызывает функцию callback.

Пример использования:

addScripts(["1.jpg", "2.jpg", "3.jpg"], callback);

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

Такая функция может полезна, например, для фоновой загрузки картинок в онлайн-галерею.

Исходный документ с картинками: tutorial/browser/events/images-load-src/index.html.

Там также содержится код для проверки, действительно ли изображения загрузились. Он должен выводить «0», затем «300».

Подсказка
Решение
Подсказка

Создайте переменную-счетчик для подсчёта количества загруженных картинок, и увеличивайте при каждом onload/onerror.

Когда счетчик станет равен количеству картинок — вызывайте callback.

Решение

Создайте функцию addScript(src, callback), которая загружает скрипт с данным src, и после его загрузки и выполнения вызывает функцию callback.

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

Пример использования:

// go.js содержит функцию go()
addScript("go.js", function() {
 go();
});

Ошибки загрузки обрабатывать не нужно.

Исходный документ: tutorial/browser/events/script-load-src/index.html.

Решение, шаг 1
Решение
Решение, шаг 1

Добавляйте SCRIPT при помощи методов DOM:

var script = document.createElement('script');
script.src = src;

// в документе может не быть HEAD или BODY,
// но хотя бы один (текущий) SCRIPT в документе есть
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(script, s); // перед ним и вставим

На скрипт повесьте обработчики onload/onreadystatechange.

Решение, шаг 2
Решение, шаг 2

Решение: tutorial/browser/events/script-load/index.html.

Создайте функцию addScripts(scripts, callback), которая загружает скрипты из массива scripts, и после загрузки и выполнения их всех вызывает функцию callback.

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

Пример использования:

addScripts(["a.js", "b.js", "c.js"], function() { a() });
/* функция a() описана в a.js и использует b.js,c.js */

  • Ошибки загрузки обрабатывать не нужно.
  • Один скрипт не ждёт другого. Они все загружаются, а по окончании вызывается обработчик callback.
  • Исходный документ со скриптами a.js, b.js, c.js: tutorial/browser/events/scripts-load-src/index.html.

Решение, шаг 1
Решение
Решение, шаг 1

Создайте переменную-счетчик для подсчёта количества загруженных скриптов.

Чтобы один скрипт не учитывался два раза (например, onreadystatechange запустился при loaded и complete), учитывайте его состояние в объекте loaded. Свойство loaded[i] = true означает что i-й скрипт уже учтён.

Решение, шаг 2
Решение, шаг 2

Решение: tutorial/browser/events/scripts-load/index.html.


Комментарии

  1. Приветствуются комментарии, содержащие дополнения и вопросы по статье, и ответы на них.
  2. Если ваш комментарий касается задачи -- откройте её в отдельном окне и напишите там.
  3. Комментарии без смысла, с рекламой или не о статье вообще - удаляются.
Наверх

Содержание

Реклама

Нашли опечатку?

Нашли опечатку на сайте? Что-то кажется странным?
Выделите соответствующий текст и нажмите Ctrl+Enter!

Последние Комментарии

Помоги другим!

Помоги другим узнать о хорошей статье!