AJAX : readyState, load и readystatechange
Как мы все знаем работа с технологией AJAX требует использования события readystatechange. Теоретически мы так же можем использовать событие load, но как мы увидим далее IE до версии 9 его не поддерживает в данном контексте. Событие readystatechange и свойство readyState, имеют несколько особенностей при использовании.
Как правило эти особенности не попадаются нам на глаза, но если мы решим работать с AJAX без использования, какого либо уровня абстракции типа JQuery и ей подобных, то нам не помешает дополнительная информация о readyState и его состояниях отличных от 4, а так же других возможных подводных камнях.
В статье использованы следующие версии браузеров:
- Chrom: 20.0.1132.47 m
- Opera: 12.00
- IE: 7,8,9
- FireFox: 13.0.1
- Safari: 5.1.7 (7534.57.2)
AJAX : readyState
Событие readystatechange возникает, когда изменяется свойство AJAX запроса - readyState. Это свойство может иметь четыре состояния, которые теоретически работают так, как показано ниже. Практически же, браузеры, увы не всегда следуют этому стандарту. Итак, значения readyState:
- 0 Uninitialized - метод open() ещё не был вызван.
- 1 Loading - метод send() ещё не был вызван.
- 2 Loaded - метод send() был вызван,заголовки и статус - доступны.
- 3 Interactive - Загрузка данных, responseText уже содержит какую то часть данных.
- 4 Completed - Завершение всех операций.
Значение 4 свойства readyState является эквивалентом события load, а так же является стандартной частью любого AJAX сценария. Значение 3 свойства readyStates хорошо бы подошло для разработки прогресс бара, кабы не было проблем с совместимостью. Адекватных подсчётов уже загруженного контента удалось добиться только в Chrome и Safari, FireFox Opera предоставляли уже конечные значения, а от IE я вообще не добился никакой реакции.
Тем не менее, давайте проверим следующий кусок кода. Вы скорее всего ожидали бы увидеть такой прядок состояний: 1, 2, 3 и 4.
// Так как GET - запросы могут кэшироваться мы применим приём с добавлением временной метки // таким образом url - всегда будет разным: xmlhttp.open("GET","somepage.xml?t=" + (new Date()).getTime(),true); xmlhttp.onreadystatechange = checkData; xmlhttp.send(null); function checkData() { console.log(xmlhttp.readyState); }
- Chrom : 2-3-4 (ОЙ!)
- Opera: 2-3-4 (ОЙ!)
- Explorer 7: 1-2-4-3 (Нормально)
- Explorer 8: 1-2-4-3 (Нормально)
- Explorer 9: 1-2-3-4 (Нормально)
- Mozilla: 1-2-4-3 (Нормально)
- Safari: 2-3-4 (ОЙ!)
Результат:
А теперь давайте перенесём подключение обработчика события в другую точку кода и посмотрим как изменится поведение браузеров:
xmlhttp.onreadystatechange = checkData; xmlhttp.open("GET","somepage.xml?t=" + (new Date()).getTime(),true); xmlhttp.send(null); function checkData() { console.log(xmlhttp.readyState); }
- Chrom : 1-2-3-4 (Нормально)
- Opera: 1-2-3-4 (Нормально)
- Explorer 7: 1-1-2-3-4 (ОЙ!)
- Explorer 8: 1-1-2-3-4 (ОЙ!)
- Explorer 9: 1-1-2-3-4 (ОЙ!)
- Mozilla: 1-1-2-3-4 (ОЙ!)
- Safari: 1-2-3-4 (Нормально)
Результат:
Все браузеры теперь добавили в начало последовательности дополнительное состояние readyState - 1. Explorer и Mozilla, правда сделали это по-своему... дважды, и это странно, потому, что переход состояния от 1 к 1 это вовсе не "change" и не должно вызывать обработчик onreadystatechange. Но зато при таком раскладе мы теперь наблюдаем все состояния в правильном порядке. Кстати этот вариант установки обработчика до вызова метода open, считается правильным.
Давайте снова перенесём точку подключения обработчика события в коде:
xmlhttp.open("GET","somepage.xml?t=" + (new Date()).getTime(),true); xmlhttp.send(null); xmlhttp.onreadystatechange = checkData; function checkData() { console.log(xmlhttp.readyState); }
- Chrom : 2-3-4 (ОЙ!)
- Opera: 2-3-4 (ОЙ!)
- Explorer 7: 2-4-3 (ОЙ!)
- Explorer 8: 2-4-3 (ОЙ!)
- Explorer 9: 2-3-4 (ОЙ!)
- Mozilla: 2-3-4 (ОЙ!)
- Safari: 2-3-4 (ОЙ!)
Результат:
Итог: все испытуемые лопухнулись, все потеряли состояние 1... но зато сделали это коллективно и единогласно! В общем вывод один: все браузеры имеют глюки в отношении readyState, одно только радует, что хотя бы ключевое значение - 4 присутствует во всех случаях. А так же устанавливайте обработчик до вызова метода open()
readystatechange
Рассмотрим само событие readystatechange:
xmlhttp.open("GET","somepage.xml?t=" + (new Date()).getTime(),true); xmlhttp.onreadystatechange = checkData; xmlhttp.send(null); function checkData(e) { var evt = e || window.event, rs = xmlhttp.readyState || "None"; alert(evt.type + ' ' + rs); }
- Chrom : evt.type = readystatechange 2, ... 3, ... 4 (ОЙ!)
- Opera: evt.type = readystatechange 2, ... 3, ... 4 (ОЙ!)
- Explorer 7: evt.type = 1, ... 2, ... 3, ... 4 (ОЙ!)
- Explorer 8: evt.type = 1, ... 2, ... 3, ... 4 (ОЙ!)
- Explorer 9: evt.type = readystatechange 1, ... 2, ... 3, ... 4 (Нормально)
- Mozilla: evt.type = readystatechange 1, ... 2, ... 3, ... 4 (Нормально)
- Safari: evt.type = readystatechange 2, ... 3, ... 4 (ОЙ!)
Результат:
Увы для IE 7-8 свойство type - является пустой строкой... ну ладно хоть ошибку не вызывает
load
IE до версии 9 не имеет такого события как load у объекта xmlhttprequests, в остальных же случаях его можно рассматривать как подмножество событий readystatechange. load вызывается, когда страницы загружена полностью, что эквивалентно: readyState 4 completed.
Давайте попробуем следующий кусок кода:
xmlhttp.open("GET","somepage.xml?t=" + (new Date()).getTime(),true); xmlhttp.onload = checkData; xmlhttp.send(null); function checkData(e){ var evt = e || window.event; var rs = xmlhttp.readyState || "None"; alert(evt.type + ' ' + rs); }
- Chrom : load 4
- Opera : load 4
- Explorer 7: Ничего!
- Explorer 8: Ничего!
- Explorer 9: load 4
- Mozilla: load 4
- Safari: load 4
Результат:
Как видим все современные браузеры поддерживают событие load, кроме IE до версии 9. В целом на сегодняшний день с поддержкой технологии AJAX, на мой взгляд дела обстоят значительно лучше, чем на момент написания прообраза этой статьи: XMLHTTP notes Изначально я просто хотел сделать перевод статьи указанной по ссылке, но потом потестив примеры на современных браузерах и используя в тестах не alert(), а console.log() и получив иные результаты - я решил провести своё мини исследованиеНадеюсь мои изыскания сэкономят кому то из вас время при разработке вэб приложений.
Информация копипастерам
Внимание! Копирование контента с сайта, возможно только с разрешения администратора. Т.е. Меня! Я скорее всего разрешу Вам это сделать, в обмен на живую ссылку, на статью оригинал.