*.JS, когда грузиться думаешь?

Абстракт


В статье рассматривается проблема очередности загрузки внутренних и внешних JS-скриптов в динамически создаваемых HTML-страницах. Все, о чем идет речь, касается только Internet Explorer v5 и выше (IE4 не пробовал).

Интродюкшн


Как-то однажды делал я такую вот штучку: на открытой в браузере HTML-странице в поле TEXTAREA ввожу команды, нажимаю кнопку - JS-скрипт обрабатывает эти команды, формирует по ним код и создает новое окно, в которое этот код записывает. В новый документ в обязательном порядке записывалась строка, подключающая внешний скрипт - <script src="tools.js"> - для доступа к сервисным функциям.

Когда я стал это дело отлаживать, обнаружилась одна неожиданная неприятность: что-то было не так со скриптом, загружаемым из файла tools.js. Как оказалось впоследствии, дело было не в коде скрипта, а в том, что он являлся внешним.

Суть проблемы


Итак, вот что я обнаружил. Пусть имеется код:

<html>

<
head>

<
script id="S1">

---
script code ---

</script>

<script id="S2" src="tools.js"></script>

<script id="S3">

--- script code ---

</script>

</head>

<body>

<script id="S4">

--- script code ---

</script>

</body>

</html>

Если этот HTML-код загружается из файла, то порядок загрузки скриптов естественный: S1 - S2 - S3 - S4. Если же его записать с помощью newWin.document.write в созданное окно newWin, то порядок загрузки скриптов уже другой: S1 - S3 - S4 - S2(!) Т.е. порядок сохранился, если забыть о внешнем скрипте. В данном примере этот скрипт помещен внутрь <head>, но это не существенно, т.к. в <body> ничего не меняется.

Следующий пример призван продемонстрировать описанное изменение порядка загрузки скриптов.

Внешний скрипт example.js:

function demo() {

alert("example.js:demo() - Я доступна");

}

Тестовая страница:

<
html>

<
head>

<
meta http-equiv="Content-Type" content="text/html;charset=windows-1251">

<
title>Пример 1.</title>

<
script language='JavaScript' src='example.js' charset='windows-1251'></script>

<script language="JavaScript">

code = "<html>\n\

<head>\n\

<meta http-equiv=\"Content-Type\" content=\"text/html;charset=windows-1251\">\n\

<title>Пример 1. Динамически созданный документ.</title>\n\

<script language='JavaScript' src='example.js' charset='windows-1251'><"+"/script>\n\

<script language='JavaScript'>\n\

window.onerror = myerror;\n\

\n\

function myerror(msg,url,line) {\n\

alert('Генерится ошибка \\n\"'+msg+'\"\\nв строке '+line+'\\nЛегко убедиться,\\n\

что в этой строке стоит вызов demo()');\n\

return true;\n\

}\n\

<"+"/script>\n\

</head>\n\

<body>\n\

<span id=\"ilnb\">Проверяем доступность</span><br>\n\

<script language=\"JavaScript\">\n\

demo();\n\

<"+"/script>\n\

<script language=\"JavaScript\">\n\

document.all.ilnb.innerHTML += ' - выполнено';\n\

<"+"/script>\n\

Нажмите кнопку \"Обновить\", и ошибка не появится, т.к. документ уже статичен\n\

</body>\n\

</html>";

function test() {

w = window.open();

w.document.write(code);

}

</script>

</head>

<body>

<span id="ilnb">Проверяем доступность</span><br>

<script language="JavaScript">

demo();

document.all.ilnb.innerHTML += ' - выполнено';

</script>

<a href="#" onclick="test(); return false;">Создать динамический документ</a>

</body>

</html>


Метод устранения


Все делается предельно просто. Пусть нужно подключить файл tools.js. Добавим в его конец инструкцию is_load = 1. В динамически создаваемую страницу выше тега <script src="tools.js"> запишем скрипт, в котором также объявим переменную is_load = 0. Тогда is_load будет равна 0, пока не загрузится tools.js, когда она станет равной 1. Когда загрузка tools.js подтвердится указанным образом, можно будет вызвать код, использующий содержимое этого файла. Этот код указан под собирательным именем tool(). Осталось только отловить момент окончания загрузки. Для этого используем таймер.

Запишем то, что проговорено словами, в виде кода:

<script language='JavaScript'>

is_load = 0;

time_ = 100;

function
if_load() {

if (
is_load==0 )

setTimeout('if_load();',time_);

else

tool();

}

setTimeout('if_load();',time_);

</script>

<script language='JavaScript' src='tool.js'></script>

Обращаю особое внимание на размещение вспомогательного скрипта перед внешним. Порядок следования оказывается существенным, если динамически созданная страница обновляется. Тогда она уже является статической, и скрипты грузятся в порядке появления в коде. Перестановка вспомогательного и внешнего скриптов привела бы к тому, что сначала загрузился бы внешний скрипт - при этом было бы is_load = 1 - после чего загрузилась бы инструкция is_load = 0, что привело бы к непрекращающимся вызовам функции if_load().

Пример реализации.

Внешний скрипт tools.js:

function tool() {

alert("tools.js:tool() - Я уже загрузилась");

}

is_load = 1;

Тестовая страница:

<html>

<
head>

<
meta http-equiv="Content-Type" content="text/html;charset=windows-1251">

<
title>Пример 2.</title>

<
script language='JavaScript'>

is_load = 0;

time_ = 100;

function
if_load() {

if (
is_load==0 )

setTimeout('if_load();',time_);

else {

tool();

document.all.ilnb.innerHTML += ' - выполнено';

}

}

setTimeout('if_load();',time_);

</script>

<script language='JavaScript' src='tools.js' charset='windows-1251'></script>

<script language="JavaScript">

code = "<html>\n\

<head>\n\

<meta http-equiv=\"Content-Type\" content=\"text/html;charset=windows-1251\">\n\

<title>Пример 2. Динамически созданный документ</title>\n\

<script language='JavaScript'>\n\

is_load = 0;\n\

time_ = 100;\n\

\n\

function if_load() {\n\

if ( is_load==0 )\n\

setTimeout('if_load();',time_);\n\

else {\n\

tool();\n\

document.all.ilnb.innerHTML += ' - выполнено';\n\

}\n\

}\n\

\n\

setTimeout('if_load();',time_);\n\

<"+"/script>\n\

<script language='JavaScript' src='tools.js' charset='windows-1251'><"+"/script>\n\

</head>\n\

<body>\n\

<span id=\"ilnb\">Проверяем доступность</span><br>\n\

Нажмите кнопку \"Обновить\", и загрузка определится правильно, т.к. порядок \n\

следования вспомогательного и внешнего скриптов - правильный\n\

</body>\n\

</html>";

function test() {

w = window.open();

w.document.write(code);

}

</script>

</head>

<body>

<span id="ilnb">Проверяем доступность</span><br>

<a href="#" onclick="test(); return false;">Создать динамический документ</a>

</body>

</html>

Как видим, все улажено, и в динамически создаваемой странице функция tool() вызывается только тогда, когда она становится доступной.

Благодарю всех за внимание и желаю творческих успехов!

Автор: Александр Рябов