ok
This commit is contained in:
parent
aeb74092b6
commit
1f61c2ab1d
100 changed files with 4120 additions and 659 deletions
|
@ -0,0 +1,6 @@
|
|||
|
||||
The algorithm:
|
||||
1. Make `img` for every source.
|
||||
2. Add `onload/onerror` for every image.
|
||||
3. Increase the counter when either `onload` or `onerror` triggers.
|
||||
4. When the counter value equals to the sources count -- we're done: `callback()`.
|
|
@ -0,0 +1,54 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<script>
|
||||
function preloadImages(sources, callback) {
|
||||
let counter = 0;
|
||||
|
||||
function onLoad() {
|
||||
counter++;
|
||||
if (counter == sources.length) callback();
|
||||
}
|
||||
|
||||
for(let source of sources) {
|
||||
let img = document.createElement('img');
|
||||
img.onload = img.onerror = onLoad;
|
||||
img.src = source;
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- The test ----------
|
||||
|
||||
let sources = [
|
||||
"https://en.js.cx/images-load/1.jpg",
|
||||
"https://en.js.cx/images-load/2.jpg",
|
||||
"https://en.js.cx/images-load/3.jpg"
|
||||
];
|
||||
|
||||
// add random characters to prevent browser caching
|
||||
for (let i = 0; i < sources.length; i++) {
|
||||
sources[i] += '?' + Math.random();
|
||||
}
|
||||
|
||||
// for each image,
|
||||
// let's create another img with the same src and check that we have its width immediately
|
||||
function testLoaded() {
|
||||
let widthSum = 0;
|
||||
for (var i = 0; i < sources.length; i++) {
|
||||
let img = document.createElement('img');
|
||||
img.src = sources[i];
|
||||
widthSum += img.width;
|
||||
}
|
||||
alert(widthSum);
|
||||
}
|
||||
|
||||
// should output 300
|
||||
preloadImages(sources, testLoaded);
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,43 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<script>
|
||||
function preloadImages(sources, callback) {
|
||||
/* your code */
|
||||
}
|
||||
|
||||
// ---------- The test ----------
|
||||
|
||||
let sources = [
|
||||
"https://en.js.cx/images-load/1.jpg",
|
||||
"https://en.js.cx/images-load/2.jpg",
|
||||
"https://en.js.cx/images-load/3.jpg"
|
||||
];
|
||||
|
||||
// add random characters to prevent browser caching
|
||||
for (let i = 0; i < sources.length; i++) {
|
||||
sources[i] += '?' + Math.random();
|
||||
}
|
||||
|
||||
// for each image,
|
||||
// let's create another img with the same src and check that we have its width immediately
|
||||
function testLoaded() {
|
||||
let widthSum = 0;
|
||||
for (var i = 0; i < sources.length; i++) {
|
||||
let img = document.createElement('img');
|
||||
img.src = sources[i];
|
||||
widthSum += img.width;
|
||||
}
|
||||
alert(widthSum);
|
||||
}
|
||||
|
||||
// every image is 100x100, the total width should be 300
|
||||
preloadImages(sources, testLoaded);
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,36 @@
|
|||
importance: 4
|
||||
|
||||
---
|
||||
|
||||
# Load images with a callback
|
||||
|
||||
Normally, images are loaded when they are created. So i when we add `<img>` to the page, the user does not see the picture immediately. The browser needs to load it first.
|
||||
|
||||
To show an image immediately, we can create it "in advance", like this:
|
||||
|
||||
```js
|
||||
let img = document.createElement('img');
|
||||
img.src = 'my.jpg';
|
||||
```
|
||||
|
||||
The browser starts loading the image and remembers it in the cache. Later, when the same image appears in the document (no matter how), it shows up immediately.
|
||||
|
||||
**Create a function `preloadImages(sources, callback)` that loads all images from the array `sources` and, when ready, runs `callback`.**
|
||||
|
||||
For instance, this will show an `alert` after the images are loaded:
|
||||
|
||||
```js
|
||||
function loaded() {
|
||||
alert("Images loaded")
|
||||
}
|
||||
|
||||
preloadImages(["1.jpg", "2.jpg", "3.jpg"], loaded);
|
||||
```
|
||||
|
||||
In case of an error, the function should still assume the picture "loaded".
|
||||
|
||||
In other words, the `callback` is executed when all images are either loaded or errored out.
|
||||
|
||||
The function is useful, for instance, when we plan to show a gallery with many scrollable images, and want to be sure that all images are loaded.
|
||||
|
||||
In the source document you can find links to test images, and also the code to check whether they are loaded or not. It should output `300`.
|
|
@ -1,8 +0,0 @@
|
|||
# Подсказка
|
||||
|
||||
Текст на странице пусть будет изначально `DIV`, с классом `img-replace` и атрибутом `data-src` для картинки.
|
||||
|
||||
Функция `replaceImg()` должна искать такие `DIV` и загружать изображение с указанным `src`. По `onload` осуществляется замена `DIV` на картинку.
|
||||
|
||||
# Решение
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<style>
|
||||
.img-replace {
|
||||
float: left;
|
||||
border: 1px solid black;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<button onclick="window.location.reload(true)">Перезагрузить ифрейм</button>
|
||||
<hr>
|
||||
<div style="width:114px;height:40px;font-size:32px;letter-spacing:3px" data-src="https://js.cx/search/google.png" class="img-replace">
|
||||
<span style="color:#1A53F7">G</span><span style="color:#E42131">o</span><span style="color:#FEB819">o</span><span style="color:#164AF2">g</span><span style="color:#00a315">l</span><span style="color:#E42131">e</span>
|
||||
</div>
|
||||
|
||||
<div style="width:101px;height:40px;font-size:32px" data-src="https://js.cx/search/yandex.png" class="img-replace">
|
||||
<span style="color:#F00">Я</span>ндекс
|
||||
</div>
|
||||
|
||||
<div style="width:100;height:40px;font-size:32px;color:#006dd4;font-weight:bold;letter-spacing:3px;font-family:Arial" data-src="bing.png" class="img-replace">bing</div>
|
||||
|
||||
|
||||
<script>
|
||||
function replaceImg() {
|
||||
var divs = document.querySelectorAll('div.img-replace');
|
||||
for (var i = 0; i < divs.length; i++)(function(i) {
|
||||
var img = document.createElement('img');
|
||||
img.src = divs[i].getAttribute('data-src');
|
||||
img.className = 'img-replace';
|
||||
|
||||
img.onload = function() {
|
||||
divs[i].parentNode.replaceChild(img, divs[i]);
|
||||
}
|
||||
|
||||
}(i))
|
||||
}
|
||||
|
||||
setTimeout(replaceImg, 1000); // задержка на 1 сек для демонстрации
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -1,40 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<style>
|
||||
.img-replace {
|
||||
float: left;
|
||||
border: 1px solid black;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<!-- Google -->
|
||||
|
||||
<div style="width:114px;height:40px;font-size:32px;letter-spacing:3px" class="img-replace">
|
||||
<span style="color:#1A53F7">G</span><span style="color:#E42131">o</span><span style="color:#FEB819">o</span><span style="color:#164AF2">g</span><span style="color:#00a315">l</span><span style="color:#E42131">e</span>
|
||||
</div>
|
||||
|
||||
<!-- Яндекс -->
|
||||
<div style="width:101px;height:40px;font-size:32px" class="img-replace">
|
||||
<span style="color:#F00">Я</span>ндекс
|
||||
</div>
|
||||
|
||||
<!-- Bing -->
|
||||
<div style="width:100;height:40px;font-size:32px;color:#006dd4;font-weight:bold;letter-spacing: 3px; font-family:Arial">bing</div>
|
||||
|
||||
<hr>
|
||||
<!-- картинки (для bing картинки специально нет, чтобы протестировать случай "загрузка не удалась") -->
|
||||
|
||||
<img src="https://js.cx/search/yandex.png" width="114" height="40" alt="Яндекс">
|
||||
<img src="https://js.cx/search/google.png" width="101" height="40" alt="Google">
|
||||
<img src="https://js.cx/search/bing.png" width="101" height="40" alt="Файла нет (bing)">
|
||||
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -1,20 +0,0 @@
|
|||
importance: 5
|
||||
|
||||
---
|
||||
|
||||
# Красивый "ALT"
|
||||
|
||||
Обычно, до того как изображение загрузится (или при отключенных картинках), посетитель видит пустое место с текстом из "ALT". Но этот атрибут не допускает HTML-форматирования.
|
||||
|
||||
При мобильном доступе скорость небольшая, и хочется, чтобы посетитель сразу видел красивый текст.
|
||||
|
||||
**Реализуйте "красивый" (HTML) аналог `alt` при помощи CSS/JavaScript, который затем будет заменён картинкой сразу же как только она загрузится.** А если загрузка не состоится -- то не заменён.
|
||||
|
||||
Демо: (нажмите "перезагрузить", чтобы увидеть процесс загрузки и замены)
|
||||
|
||||
[iframe src="solution" height="100"]
|
||||
|
||||
Картинки для `bing` специально нет, так что текст остается "как есть".
|
||||
|
||||
Исходный документ содержит разметку текста и ссылки на изображения.
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
|
||||
Создайте переменную-счетчик для подсчёта количества загруженных картинок, и увеличивайте при каждом `onload/onerror`.
|
||||
|
||||
Когда счетчик станет равен количеству картинок -- вызывайте `callback`.
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<script>
|
||||
function preloadImages(sources, callback) {
|
||||
var counter = 0;
|
||||
|
||||
function onLoad() {
|
||||
counter++;
|
||||
if (counter == sources.length) callback();
|
||||
}
|
||||
|
||||
for (var i = 0; i < sources.length; i++) {
|
||||
var img = document.createElement('img');
|
||||
// сначала onload/onerror, затем src - важно для IE8-
|
||||
img.onload = img.onerror = onLoad;
|
||||
img.src = sources[i];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// ---------- Проверка ----------
|
||||
|
||||
var sources = [
|
||||
"https://js.cx/images-load/1.jpg",
|
||||
"https://js.cx/images-load/2.jpg",
|
||||
"https://js.cx/images-load/3.jpg"
|
||||
];
|
||||
for (var i = 0; i < sources.length; i++) {
|
||||
sources[i] += '?' + Math.random();
|
||||
}
|
||||
|
||||
function testLoaded() {
|
||||
var widthSum = 0;
|
||||
for (var i = 0; i < sources.length; i++) {
|
||||
var img = document.createElement('img');
|
||||
img.src = sources[i];
|
||||
widthSum += img.width;
|
||||
}
|
||||
alert(widthSum); // 300!
|
||||
}
|
||||
|
||||
// до загрузки - 0
|
||||
testLoaded();
|
||||
// после загрузки - 300
|
||||
preloadImages(sources, testLoaded);
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -1,50 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<script>
|
||||
function preloadImages(sources, callback) {
|
||||
/* ваш код */
|
||||
}
|
||||
|
||||
// ---------- Проверка ----------
|
||||
|
||||
/* файлы для загрузки */
|
||||
var sources = [
|
||||
"https://js.cx/images-load/1.jpg",
|
||||
"https://js.cx/images-load/2.jpg",
|
||||
"https://js.cx/images-load/3.jpg"
|
||||
];
|
||||
for (var i = 0; i < sources.length; i++) {
|
||||
sources[i] += '?' + Math.random(); // добавляем параметр, чтобы без кеша (для теста)
|
||||
}
|
||||
|
||||
/** если картинка загружена, то можно будет сразу получить её ширину
|
||||
* создадим все картинки и проверим, есть ли у них ширина
|
||||
*/
|
||||
function testLoaded() {
|
||||
var widthSum = 0;
|
||||
for (var i = 0; i < sources.length; i++) {
|
||||
var img = document.createElement('img');
|
||||
img.src = sources[i];
|
||||
widthSum += img.width;
|
||||
}
|
||||
// каждое изображение 100x100, общая ширина должна быть 300px
|
||||
alert(widthSum);
|
||||
}
|
||||
|
||||
// до загрузки - выведет 0
|
||||
testLoaded();
|
||||
|
||||
// после загрузки - выведет 300
|
||||
preloadImages(sources, testLoaded);
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -1,20 +0,0 @@
|
|||
importance: 4
|
||||
|
||||
---
|
||||
|
||||
# Загрузить изображения с коллбэком
|
||||
|
||||
Создайте функцию `preloadImages(sources, callback)`, которая предзагружает изображения из массива `sources`, и после загрузки вызывает функцию `callback`.
|
||||
|
||||
Пример использования:
|
||||
|
||||
```js
|
||||
preloadImages(["1.jpg", "2.jpg", "3.jpg"], callback);
|
||||
```
|
||||
|
||||
Если вдруг возникает ошибка при загрузке -- считаем такое изображение загруженным, чтобы не ломать поток выполнения.
|
||||
|
||||
Такая функция может полезна, например, для фоновой загрузки картинок в онлайн-галерею.
|
||||
|
||||
В исходном документе содержатся ссылки на картинки, а также код для проверки, действительно ли изображения загрузились. Он должен выводить "0", затем "300".
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
# Подсказка
|
||||
|
||||
Добавляйте `SCRIPT` при помощи методов `DOM`:
|
||||
|
||||
```js
|
||||
var script = document.createElement('script');
|
||||
script.src = src;
|
||||
|
||||
// в документе может не быть HEAD или BODY,
|
||||
// но хотя бы один (текущий) SCRIPT в документе есть
|
||||
var s = document.getElementsByTagName('script')[0];
|
||||
s.parentNode.insertBefore(script, s); // перед ним и вставим
|
||||
```
|
||||
|
||||
На скрипт повесьте обработчики `onload/onreadystatechange`.
|
||||
|
||||
# Решение
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
function go() {
|
||||
alert("ok");
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<script>
|
||||
function addScript(src, callback) {
|
||||
var script = document.createElement('script');
|
||||
script.src = src;
|
||||
var s = document.getElementsByTagName('script')[0]
|
||||
s.parentNode.insertBefore(script, s);
|
||||
|
||||
var loaded = false;
|
||||
|
||||
function onload() {
|
||||
if (loaded) return; // повторный вызов
|
||||
loaded = true;
|
||||
callback();
|
||||
}
|
||||
|
||||
script.onload = onload; // все браузеры, IE с версии 9
|
||||
|
||||
script.onreadystatechange = function() { // IE8-
|
||||
if (this.readyState == 'loaded' || this.readyState == 'complete') {
|
||||
setTimeout(onload, 0);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
addScript("go.js", function() {
|
||||
go();
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -1,3 +0,0 @@
|
|||
function go() {
|
||||
alert("ok");
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<script>
|
||||
function addScript(src, callback) {
|
||||
/* ваш код */
|
||||
}
|
||||
|
||||
addScript("go.js", function() {
|
||||
go();
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -1,21 +0,0 @@
|
|||
importance: 4
|
||||
|
||||
---
|
||||
|
||||
# Скрипт с коллбэком
|
||||
|
||||
Создайте функцию `addScript(src, callback)`, которая загружает скрипт с данным `src`, и после его загрузки и выполнения вызывает функцию `callback`.
|
||||
|
||||
Скрипт может быть любым, работа функции не должна зависеть от его содержимого.
|
||||
|
||||
Пример использования:
|
||||
|
||||
```js
|
||||
// go.js содержит функцию go()
|
||||
addScript("go.js", function() {
|
||||
go();
|
||||
});
|
||||
```
|
||||
|
||||
Ошибки загрузки обрабатывать не нужно.
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
# Подсказки
|
||||
|
||||
Создайте переменную-счетчик для подсчёта количества загруженных скриптов.
|
||||
|
||||
Чтобы один скрипт не учитывался два раза (например, `onreadystatechange` запустился при `loaded` и `complete`), учитывайте его состояние в объекте `loaded`. Свойство `loaded[i] = true` означает что `i`-й скрипт уже учтён.
|
||||
|
||||
# Решение
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
function a() {
|
||||
b();
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
function b() {
|
||||
c();
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
function c() {
|
||||
alert('ok');
|
||||
}
|
|
@ -1,54 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<script>
|
||||
function addScript(src) {
|
||||
var script = document.createElement('script');
|
||||
script.src = src;
|
||||
var s = document.getElementsByTagName('script')[0]
|
||||
s.parentNode.insertBefore(script, s);
|
||||
return script;
|
||||
}
|
||||
|
||||
function addScripts(scripts, callback) {
|
||||
var loaded = {}; // Для загруженных файлов loaded[i] = true
|
||||
var counter = 0;
|
||||
|
||||
function onload(i) {
|
||||
if (loaded[i]) return; // лишний вызов onload/onreadystatechange
|
||||
loaded[i] = true;
|
||||
counter++;
|
||||
if (counter == scripts.length) callback();
|
||||
}
|
||||
|
||||
for (var i = 0; i < scripts.length; i++)(function(i) {
|
||||
var script = addScript(scripts[i]);
|
||||
|
||||
script.onload = function() {
|
||||
onload(i);
|
||||
};
|
||||
|
||||
script.onreadystatechange = function() { // IE8-
|
||||
if (this.readyState == 'loaded' || this.readyState == 'complete') {
|
||||
setTimeout(this.onload, 0); // возможны повторные вызовы onload
|
||||
}
|
||||
};
|
||||
|
||||
}(i));
|
||||
|
||||
}
|
||||
|
||||
addScripts(["a.js", "b.js", "c.js"], function() {
|
||||
a()
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -1,3 +0,0 @@
|
|||
function a() {
|
||||
b();
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
function b() {
|
||||
c();
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
function c() {
|
||||
alert('ok');
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<script>
|
||||
/* ваш код */
|
||||
|
||||
// функция a() сработает только если загружены a.js, b.js, c.js
|
||||
addScripts(["a.js", "b.js", "c.js"], function() {
|
||||
a()
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -1,22 +0,0 @@
|
|||
importance: 5
|
||||
|
||||
---
|
||||
|
||||
# Скрипты с коллбэком
|
||||
|
||||
Создайте функцию `addScripts(scripts, callback)`, которая загружает скрипты из массива `scripts`, и *после загрузки и выполнения их всех* вызывает функцию `callback`.
|
||||
|
||||
Скрипт может быть любым, работа функции не должна зависеть от его содержимого.
|
||||
|
||||
Пример использования:
|
||||
|
||||
```js no-beautify
|
||||
addScripts(["a.js", "b.js", "c.js"], function() { a() });
|
||||
/* функция a() описана в a.js и использует b.js,c.js */
|
||||
```
|
||||
|
||||
- Ошибки загрузки обрабатывать не нужно.</li>
|
||||
- Один скрипт не ждёт другого. Они все загружаются, а по окончании вызывается обработчик `callback`.
|
||||
|
||||
Исходный код содержит скрипты `a.js`, `b.js`, `c.js`:
|
||||
|
|
@ -1,227 +1,89 @@
|
|||
# Resource loading: onload and onerror
|
||||
|
||||
Браузер позволяет отслеживать загрузку внешних ресурсов -- скриптов, ифреймов, картинок и других.
|
||||
The browser allows to track the loading of external resources -- scripts, iframes, pictures and so on.
|
||||
|
||||
Для этого есть два события:
|
||||
There are two events for it:
|
||||
|
||||
- `onload` -- если загрузка успешна.
|
||||
- `onerror` -- если при загрузке произошла ошибка.
|
||||
- `onload` -- successful load,
|
||||
- `onerror` -- an error occured.
|
||||
|
||||
## Загрузка SCRIPT
|
||||
## Loading a script
|
||||
|
||||
Рассмотрим следующую задачу.
|
||||
Let's say we need to call a function that resides in an external script.
|
||||
|
||||
В браузере работает сложный интерфейс и, чтобы создать очередной компонент, нужно загрузить скрипт с сервера.
|
||||
|
||||
Подгрузить внешний скрипт -- достаточно просто:
|
||||
We can load it dynamically, like this:
|
||||
|
||||
```js
|
||||
var script = document.createElement('script');
|
||||
let script = document.createElement('script');
|
||||
script.src = "my.js";
|
||||
|
||||
document.body.appendChild(script);
|
||||
document.head.append(script);
|
||||
```
|
||||
|
||||
...Но как после подгрузки выполнить функцию, которая объявлена в этом скрипте? Для этого нужно отловить момент окончания загрузки и выполнения тега `<script>`.
|
||||
...But how to run the function that is declared inside that script? We need to wait until the script loads, and only then we can call it.
|
||||
|
||||
### script.onload
|
||||
|
||||
Главным помощником станет событие `onload`. Оно сработает, когда скрипт загрузился и выполнился.
|
||||
The main helper is the `load` event. It triggers after the script was loaded and executed.
|
||||
|
||||
Например:
|
||||
For instance:
|
||||
|
||||
```js run
|
||||
var script = document.createElement('script');
|
||||
```js run untrusted
|
||||
let script = document.createElement('script');
|
||||
|
||||
// can load any script, from any domain
|
||||
script.src = "https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.3.0/lodash.js"
|
||||
document.body.appendChild(script);
|
||||
document.head.append(script);
|
||||
|
||||
*!*
|
||||
script.onload = function() {
|
||||
// после выполнения скрипта становится доступна функция _
|
||||
alert( _ ); // её код
|
||||
}
|
||||
// the script creates a helper function "_"
|
||||
alert(_); // the function is available
|
||||
};
|
||||
*/!*
|
||||
```
|
||||
|
||||
Это даёт возможность, как в примере выше, получить переменные из скрипта и выполнять объявленные в нём функции.
|
||||
So in `onload` we can use script variables, run functions etc.
|
||||
|
||||
...А что, если загрузка скрипта не удалась? Например, такого скрипта на сервере нет (ошибка 404) или сервер "упал" (ошибка 500).
|
||||
|
||||
Такую ситуацию тоже нужно как-то обрабатывать, хотя бы сообщить посетителю о возникшей проблеме.
|
||||
...And what if the loading failed? For instance, there's no such script (error 404) or the server or the server is down (unavailable).
|
||||
|
||||
### script.onerror
|
||||
|
||||
Любые ошибки загрузки (но не выполнения) скрипта отслеживаются обработчиком `onerror`.
|
||||
Errors that occur during the loading (but not execution) of the script can be tracked on `error` event.
|
||||
|
||||
Например, сделаем запрос заведомо отсутствующего скрипта:
|
||||
For instance, let's request a script that doesn't exist:
|
||||
|
||||
```js run
|
||||
var script = document.createElement('script');
|
||||
script.src = "https://example.com/404.js"
|
||||
document.body.appendChild(script);
|
||||
let script = document.createElement('script');
|
||||
script.src = "https://example.com/404.js"; // no such script
|
||||
document.head.append(script);
|
||||
|
||||
*!*
|
||||
script.onerror = function() {
|
||||
alert( "Ошибка: " + this.src );
|
||||
alert("Error loading " + this.src); // Error loading https://example.com/404.js
|
||||
};
|
||||
*/!*
|
||||
```
|
||||
|
||||
### IE8-: script.onreadystatechange [#onreadystatechange]
|
||||
Please note that we can't get error details here. We don't know was it error 404 or 500 or something else. Just that the loading failed.
|
||||
|
||||
Примеры выше работают во всех браузерах, кроме IE8-.
|
||||
## Other resources
|
||||
|
||||
В IE для отслеживания загрузки есть другое событие: `onreadystatechange`. Оно срабатывает многократно, при каждом обновлении состояния загрузки.
|
||||
The `load` and `error` events also work for other resources. There may be minor differences though.
|
||||
|
||||
Текущая стадия процесса находится в `script.readyState`:
|
||||
For instance:
|
||||
|
||||
`loading`
|
||||
: В процессе загрузки.
|
||||
|
||||
`loaded`
|
||||
: Получен ответ с сервера -- скрипт или ошибка. Скрипт на фазе `loaded` может быть ещё не выполнен.
|
||||
|
||||
`complete`
|
||||
: Скрипт выполнен.
|
||||
|
||||
Например, рабочий скрипт:
|
||||
|
||||
```js run no-beautify
|
||||
var script = document.createElement('script');
|
||||
script.src = "https://code.jquery.com/jquery.js";
|
||||
document.documentElement.appendChild(script);
|
||||
|
||||
*!*
|
||||
script.onreadystatechange = function() {
|
||||
alert(this.readyState); // loading -> loaded -> complete
|
||||
}
|
||||
*/!*
|
||||
```
|
||||
|
||||
Скрипт с ошибкой:
|
||||
|
||||
```js run no-beautify
|
||||
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`.
|
||||
|
||||
Итак, самое надёжное средство для IE8- поймать загрузку (или ошибку загрузки) -- это повесить обработчик на событие `onreadystatechange`, который будет срабатывать и на стадии `complete` и на стадии `loaded`. Так как скрипт может быть ещё не выполнен к этому моменту, то вызов функции лучше сделать через `setTimeout(.., 0)`.
|
||||
|
||||
Пример ниже вызывает `afterLoad` после загрузки скрипта и работает только в IE:
|
||||
|
||||
```js run no-beautify
|
||||
var script = document.createElement('script');
|
||||
script.src = "https://code.jquery.com/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` после загрузки скрипта *или* при ошибке.
|
||||
|
||||
Работает во всех браузерах:
|
||||
|
||||
```js run
|
||||
var script = document.createElement('script');
|
||||
script.src = "https://code.jquery.com/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
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Загрузка других ресурсов
|
||||
|
||||
Поддержка этих событий для других типов ресурсов различна:
|
||||
|
||||
`<img>`, `<link>` (стили)
|
||||
: Поддерживает `onload/onerror` во всех браузерах.
|
||||
`<img>`, `<link>` (external stylesheets)
|
||||
: Both `load` and `error` events work as expected.
|
||||
|
||||
`<iframe>`
|
||||
: Поддерживает `onload` во всех браузерах. Это событие срабатывает как при успешной загрузке, так и при ошибке.
|
||||
: Only `load` event when the iframe loading finished. It triggers both for successful load and in case of an error. That's for historical reasons.
|
||||
|
||||
Обратим внимание, что если `<iframe>` загружается с того же домена, то можно, используя `iframe.contentWindow.document` получить ссылку на документ и поставить обработчик `DOMContentLoaded`. А вот если `<iframe>` -- с другого домена, то так не получится, однако сработает `onload`.
|
||||
## Summary
|
||||
|
||||
## Итого
|
||||
Pictures `<img>`, external styles, scripts and other resources provide `load` and `error` events to track their loading:
|
||||
|
||||
В этой статье мы рассмотрели события `onload/onerror` для ресурсов.
|
||||
- `load` triggers on a successful load,
|
||||
- `error` triggers on a failed load.
|
||||
|
||||
Их можно обобщить, разделив на рецепты:
|
||||
|
||||
Отловить загрузку скрипта (включая ошибку)
|
||||
: Ставим обработчики на `onload` + `onerror` + (для IE8-) `onreadystatechange`, как указано в рецепте выше
|
||||
|
||||
Отловить загрузку картинки `<img>` или стиля `<link>`
|
||||
: Ставим обработчики на `onload` + `onerror`
|
||||
|
||||
```js no-beautify
|
||||
var img = document.createElement('img');
|
||||
img.onload = function() { alert("Успех "+this.src };
|
||||
img.onerror = function() { alert("Ошибка "+this.src };
|
||||
img.src = ...
|
||||
```
|
||||
|
||||
Изображения начинают загружаться сразу при создании, не нужно их для этого вставлять в HTML.
|
||||
|
||||
**Чтобы работало в IE8-, `src` нужно ставить *после* `onload/onerror`.**
|
||||
|
||||
Отловить загрузку `<iframe>`
|
||||
: Поддерживается только обработчик `onload`. Он сработает, когда `IFRAME` загрузится, со всеми подресурсами, а также в случае ошибки.
|
||||
The only exception is `<iframe>`: for historical reasons it always triggers `load`, for any load completion, even if the page is not found.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue