Pers.narod.ru. Javascript. Предзагрузка, смена картинок через Javascript и проблемы "большой тройки" браузеров

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

Формально поводом для заметки стало то, что мне понадобился несложный скрипт для случайной смены картинок, продолжающейся указанное время. Всё, что попадалось в инете - или чересчур сложно, или не работает в каком-либо из основных браузеров (Опере, ИЕ, Файрфоксе) или тому подобные вещи. Поэтому главное, на что я обращу здесь внимание - специфические баги и особенности браузеров, из-за которых даже совсем простой скрипт бывает проблематично подключить или адаптировать.

Сам по себе код для решения нашей (или любой похожей) задачи несложен. Ниже приводится закомментированный скрипт, делающий предзагрузку набора картинок из указанной папки и затем способный "прокрутить" картинки случайным выбором в течение указанного времени.

var imgFolder="./cubeimg/"; //Путь от скрипта или URL к папке с картинками
var imgExt=".gif"; //Общий тип всех картинок
var imgNames=new Array('1','2','3','4','5','6'); //Список имён картинок
var timeStep=100; //Шаг между сменой картинок в миллисекундах
 
//Ниже - служебные переменные
var div6=null; //Невидимый раздел с картинками, который создает скрипт
var width=0; 
var height=0;
var loaded=false;
var TimeoutID=null;
var current=0;
var count=1;
var result=1;
var item_cube;
var item_result;
 
function $(id) { return document.getElementById(id); } //Чтение элемента по id
 
function preload(images) { 
 //Функция предзагрузки картинок. Кроссбраузерна (?)
 if (typeof(document.body) == "undefined") return;
 try {
  //Создаём новый раздел DIV с именем div6
  div6 = document.createElement("div"); 
  //Позиционируем его в левом верхнему углу окна и "прячем"
  div6.style.position = 'absolute';
  div6.style.top = div6.style.left = 0;
  div6.style.visibility = 'hidden';
  //Добавляем новый элемент к телу документа
  document.body.appendChild(div6);
  //Просматриваем массив картинок
  for (var i = 0; i < images.length; i++) { 
   //Формируем имя картинки
   img = imgFolder+images[i]+imgExt;
   //Добавляем её к элементу div6 и даём id, соответствующий имени
   div6.innerHTML += "<img src='"+img+"' alt='' id=\""+images[i]+"\">";
  }
  loaded=true;
 } 
 catch(e) { //Сообщение, если не удалось сделть предзагрузку
  window.alert ("Error! Can't preload the images for cube from folder "+
   imgFolder);
 }
}
 
function rand6 (n) { //Случайное целое число в интервале [0,n-1]
 return (Math.floor(Math.random()*n));
}
 
function initImg () { 
 //Загрузка картинок - можно сначала вызывать отдельно
 if (!loaded) preload(imgNames);
}
 
function showcube () { //Смена картинки
 var n = rand6(imgNames.length); //Получить случайное число
 var s = imgFolder+imgNames[n]+imgExt; //Сформировать путь к картинке
 result=n+1; //Результат - номер картинки в списке с 1
 item_cube.style.backgroundImage = "url("+s+")"; 
  //Поставить картинку фоном элемента item_cube
 current++; //Увеличить счётчик шагов
 if (current>count) { //Уже сделаны все шаги?
  window.clearTimeout (TimeoutID); //Очистить метку времени
  TimeoutID=null;
  item_result.innerHTML=result; //Вывести результат в элемент item_result
 }
}
 
function show6 (item1,itemresult1,timeexe) {
 //Вызывается по нажатию кнопки из внешнего документа
 //Показ вращения кубика (или другая смена картинок)
 //item1 - id элемента, куда выводятся картинки
 //itemresult1 - id элемента, куда выводится номер итоговой картинки
 //timeexe - общее время смены картинок в миллисекундах
 if (!loaded) initImg (); //Ещё не загружены - загрузить
 if (loaded) {
  if (TimeoutID) return; //Сейчас идёт время смены картинок - ничего не делать
  else {
   item_cube=$(item1);          //Получить в глобальные переменные
   item_result=$(itemresult1);  //ссылки на оба элемента
   item_result.innerHTML='';    //Очистить содержимое элемента с результатом
   //Лучше пройдем картинки яваскриптом повторно, когда они
   //все уже загружены
   for (var i = 0; i < imgNames.length; i++) { 
    //Получаем id картинки
    id_pic=$(imgNames[i]);
    //Вычисляем будущую ширину и высоту раздела div6 по размерам картинок
    w=id_pic.width;
    if (w>width) width=w;
    h=id_pic.height;
    if (h>height) height=h;
   }
   item_cube.style.width=width;    //Установить ширину и высоту элемента 
   item_cube.style.height=height;  //с картинками, полученные при загрузке
   var d = new Date();
   var start = d.getTime();     //Вычислить время начала и конца
   var end = start + timeexe;   //смены картинок
   count = (end - start) / timeStep; //Получить число шагов для смены картинок
   if (count<1) count=1;
   current = 0; //Номер текущего шага
   TimeoutID=window.setInterval("showcube()", timeStep);
    //Установить метку времени TimeoutID и обработчик showcube(),
    //повторяемый через timeStep миллисекунд
  }
 }
}

Предзагрузка делается через объектную модель документа DOM, а не через работающий в меньшинстве современных браузеров код вроде image.src=..., так что скрипт задуман кроссплатформенным.

Все нужные настройки - в первых 4 строках. Имена картинок из массива imgNames (на всякий случай) станут их идентификаторами id. Если размеры картинок разные, при загрузке будет взят максимальный из них. Вызвать скрипт можно из документа HTML или серверного скрипта PHP кодом следующего вида:

<div align="center">
 <form name="cubeform1">
  <input type=button value="Бросить" onClick="show6('cube','cuberesult',1000);">
 </form>
 <div id="cube"></div>
 <div id="cuberesult"></div>
</div>
<script type="text/javascript">initImg ();</script>

Здесь функция show6 выполняется в течение секунды, показывает картинки в разделе с id="cube" а результат "бросания кубика" (номер картинки, начиная с 1) выводит в элемент с id="cuberesult". Типовые проблемы с такими несложными скриптами (создать элемент, загрузить в него содержимое, поменять это содержимое) обычно следующие.

1. Internet Explorer 6-8 версий часто отказывается исполнять метод appendChild (добавление документа в элемент), если создающая элемент функция расположена внутри любого вложенного в тег <body> элемента. Грубо говоря, если у нас есть тег T (это может быть head, body и вообще любой тег) и в любом из его потомков есть скрипт, который добавляет методом appendChild новых потомков T, то этот appendChild скорее всего вызовет ошибку. Если скрипт находится непосредственно в T, то ошибки не будет. То есть, показанный выше код подключения сработает в IE нормально, в отличие от

<div align="center">
 <form name="cubeform1">
  <input type=button value="Бросить" onClick="show6('cube','cuberesult',1000);">
 </form>
 <div id="cube"></div>
 <div id="cuberesult"></div>
 <script type="text/javascript">initImg ();</script>
</div>

Сообщение об ошибке может иметь вид
HTML Parsing Error: Unable to modify the parent container element before the child element is closed
или что-то похожее. Может появиться окно с сообщением "Не удалось открыть узел http://... Операция будет прервана" с последующим "Невозможно загрузить страницу", может понадобиться и перезагрузка.

В качестве решения можно посоветовать добавлять потомков методом insertBefore перед любым существующим элементом (не всегда удобно при быстром написании-вставке-переделке скрипта) или переместить скрипт в другое место. Лучше всего - вне всех тегов кроме <body> и ниже того места документа, где прописаны элементы, имена которых передаются функциям Jabascript, как и сделано.

2. У Mozilla FireFox и основанных на нём браузеров свои причуды. Так, проверенный в других браузерах и работающий без замечаний код может привести просто к глухому отказу скрипта работать в FF, и лишь в консоли ошибок (Инструменты, Консоль ошибок), включив режим "Все", можно увидеть сообщение типа

"имя элемента" is not defined

При этом в других браузерах всё работает отлично. Причина чаще всего в коде вроде

<div id=programs>

вместо

<div id="programs">

или

<div id=item_name></div>
<input type=button value="OK" onClick="function_name(item_name);">

вместо

<div id="item_name"></div>
<input type=button value="OK" onClick="function_name('item_name');">

В общем, следите, в первую очередь, за кавычками и строковым контекстом.

Разобравшись с кавычками, часто получаем по-прежнему неработающий скрипт и предупреждения в консоли вроде

Предупреждение: Ошибка при анализе значения "width".  Потерянное объявление. Источник: ...

Помогает обычно максимальное "упрощение" заголовка выходного HTML-документа. Не создавайте доки с заголовками вида

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang='ru-RU'>

Потому что простой заголовок

<html>

увы, по-прежнему куда надёжнее :)

3. Opera создаёт проблемы, главным образом, слабой поддержкой Javascript. В ней просто нет событий onunload, scrollIntoView и многого другого. Так что проверьте, прежде всего, вызывается ли вообще ваше событие. Включите консоль ошибок Javascript (Инструменты, Настройки, вкладка Дополнительно, слева элемент списка Содержимое, кнопка Настроить Javascript, флажок Открывать консоль при ошибке), с ней найти проблему бывает намного проще.

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

Вывод - даже несложные и работающие со стандартным DOM html/xhtml-документы нужно проверять, как минимум, в Internet Explorer, Opera и Mozilla Firefox. Конкретный скрипт может не работать в любом сочетании этих браузеров :)

 Этот пример в работе

Рейтинг@Mail.ru

вверх гостевая; E-mail
Hosted by uCoz