Этот транспорт - пожалуй, самый универсальный и мощный, но и тонкостей в нем - больше всех
Для общения с сервером создается невидимый IFrame. Простая смена URL этого iframe - запрос к серверу за данными. Кроме того, в iframe можно отправлять post-запросы
поставив его имя в атрибут form.target.
Как правило, iframe - один, и запросы в него по очереди отправляются. Можно сделать и больше ифреймов, чтобы отправлять несколько запросов одновременно.
Однако, если их больше двух - придется выносить на разные поддомены. Об этом - в секции Обмен данными для документов с разных доменов.
Изменения в history браузера, влияющие на историю посещенных страниц и/или кнопки back-forward. Переходы по служебным URL не должны отражаться на history.
Полоса загрузки или курсор-часики при запросе в iframe. Запросы должны быть по возможности прозрачны, невидимы для посетителя.
А теперь - к реализации и ее особенностям, включая преодоление описанных проблем!
Что такое iframe? На этот вопрос у браузера два ответа
HTML-тэг: <iframe> со стандартным набором свойств
Тэг можно создавать в javascript
У тега есть стили, можно менять style.height, style.width и т.п.
К тегу можно обратиться через document.getElementsByName(name)[0] или document.getElementById(id)
Окно браузера, window
Такое же по функционалу окно браузера, как и основное, с адресом и т.п.
Основное окно и ифрейм могут общаться через javascript, если находятся на одном домене, или на разных поддоменах одного домена 2 уровня (same origin policy).
Можно получить через window.frames['имя фрейма']
Когда мы говорим о переводе iframe на новый URL - подразумеваем "окно". Когда собираемся создавать его и запихивать в DOM - конечно, "тег".
В теге iframe хранится ссылка на окно. В зависимости от браузера, это либо iframe.contentDocument, либо iframe.contentWindow.document, либо iframe.document.
// получить окно по тегу
function getIframeDocument(iframeNode) {
if (iframeNode.contentDocument) return iframeNode.contentDocument
if (iframeNode.contentWindow) return iframeNode.contentWindow.document
return iframeNode.document
}
Из страницы внутри окна iframe можно пройти к родительскому окну через window.parent, и, если разрешает same origin policy, даже вызвать функцию/получить тег iframe.
GET-запрос - всего лишь перевод iframe на новый URL. Его лучше всего осуществлять вызовом iframeDocument.location.src.replace(newURL).
Такой синтаксис в отличие от сабмита GET-формы в iframe или iframeDocument.location.src=..., оставляет history чистой в ряде браузеров. Так что переходы между адресами внутри iframe не отразятся на
кнопках back-forward и списке посещенных страниц..
function setIframeSrc(iframeNode, src) {
getIframeDocument(iframeNode).location.replace(src)
}
Для поста - достаточно задать форме form атрибут form.target='имя ифрейма' и вызвать form.submit(). Таким способом можно отправлять на сервер файлы, и вообще,
все что может содержать форма HTML.
// функция постит объект-хэш content в виде формы с нужным action, target
// напр. postToIframe({a:5,b:6}, '/count.php', 'frame1')
function postToIframe(content, action, target){
if(typeof phonyForm == 'undefined'){
// временную форму создаем, если нет
phonyForm = document.createElement("form")
phonyForm.style.display = "none"
phonyForm.enctype = "application/x-www-form-urlencoded"
phonyForm.method = "POST"
document.body.appendChild(phonyForm)
}
phonyForm.action = action
phonyForm.target = target
phonyForm.setAttribute("target", target);
// убить все содержание из временной формы
while(phonyForm.firstChild){
phonyForm.removeChild(phonyForm.firstChild);
}
// заполнить форму данными из объекта
for(var x in content){
var tn;
if(browser.isIE){
tn = document.createElement("<input type='hidden' name='"+x+"' value='"+content[x]+"'>")
phonyForm.appendChild(tn)
}else{
tn = document.createElement("input");
phonyForm.appendChild(tn);
tn.type = "hidden";
tn.name = x;
tn.value = content[x]
}
}
phonyForm.submit();
}
Сервер получает запрос, и генерирует в ответ страницу, которая, как правило, передает результат в основное окно через специальную функцию-каллбэк.
В примерах такая функция появится чуть позже, при рассмотрении невидимых ифреймов.
Для браузера iframe - такое же окно, как и основное. Соответственно, переходы в нем на разные URL должны попадать в историю, браузить туда-сюда можно через back/forward.
Например, вот:
Клик на Туда соответствует переходу по адресу there.html, а клик на Сюда - по адресу here.html (он же - начальный адрес).
Вы можете скопировать ссылку на этот пример и позаходить на нее в разных браузерах. Клики на кнопки туда-сюда никак не отражаются на истории, хотя
меняют страницу внутри фрейма. Пока что остается звук клика в IE. Запросы к серверу должны быть, вроде как, незаметны, так что придется от него избавляться.
Для начала проверим - загрязняется ли history. Для этого в новой вкладке(или окне) откроем урл примера. Затем, нажимая на кнопки - смотрим, не заработала ли
кнопка back. Если заработала - в history появился новый, лишний элемент. Лишний - потому, что автоматические, служебные переходы по специальным адресам
попадать в history не должны, они там лишние.
Кроме того, генерирует ли IE звук клика при переходе по служебному адресу? Как правило, при стандартной звуковой схеме Windows такой звук есть.
Результаты тестов зависят от браузера, ОС и т.п. Обычно получиться так, что при POST history загрязняется, а при GET - нет.
Создать ифрейм - так же просто, как и любой другой элемент. Пожалуй, единственная подстава - в IE свойство name должно обязательно задаваться при создании элемента.
Т.е, нельзя сначала сделать iframe, а потом присвоить ему name (то же самое и для input, и т.п.) - будут проблемы. Поэтому приходится делать отдельную проверку на isIE.
Приведенная ниже функция создает ифрейм с именем fname, если его еще нет, и при создании задает исходный адрес src. Если ифрейм с таким именем уже есть - он просто возвращается без дополнительных действий.
Если не задан параметр debug, то ифрейм после создания делается невидимым.
Динамическое создание ифрейма преследует две цели:
Делаем HTML чище
Динамический ифрейм - еще одна мера против загрязнения history
// браузер хранится в объекте browser
function createIFrame(fname, src, debug){
var ifrstr = browser.isIE ? '<iframe name="'+fname+'" src="'+src+'">' : 'iframe'
var cframe = document.createElement(ifrstr)
with(cframe){
name = fname // это не для IE
setAttribute("name", fname) // и это тоже, но вреда не будет
id = fname // а это везде ок
}
// можно добавлять сразу к document.body
document.getElementById('iframe_container').appendChild(cframe);
if (!debug) {
hideIframe(cframe)
}
if(!browser.isIE){
setIframeSrc(cframe, src);
}
return cframe
}
// прячем фрейм
function hideIframe(iframeNode) {
with(iframeNode.style) {
if(!browser.isSafari){
position = "absolute";
}
left = top = "0px";
height = width = "1px";
visibility = "hidden";
}
}
Вы можете самостоятельно протестировать влияние динамической генерации на звук клика в IE и кнопки back-forward.
Следующий пример работает только в IE. Он создает невидимый iframe, при запросе через который нет ни клика ни лишней хистори, ни индикатора загрузки.
Если Вы находитесь в IE, то можете заметить, что переходы по этому ифрейму абсолютно незаметны для пользователя. Идеал, да и только . Он создается при помощи
слабо документированной, но безопасной возможности ActiveX. Никаких специальных опций браузера для нее включать не надо.
function handleMessage(txt) { // функция-обработчик сообщения с сервера, живет в основном окне
alert(">> "+txt) // для примера
}
// создать IE-only транспорт
function createIEFrame(fname, src) {
// создаем объект htmlfile, примерно аналогичный по функционалу window.document
var rcvNode = new ActiveXObject("htmlfile");
// заполним исходным HTML
rcvNode.open();
rcvNode.write("<html><head><title>ActiveX</title></head><body></body></html>");
rcvNode.close();
// связь htmlfile с родительским окном (см объяснения ниже)
rcvNode.parentWindow.deliver = handleMessage
// добавим внутрь div с нужным ифреймом
var ifrDiv = rcvNode.createElement("div");
rcvNode.appendChild(ifrDiv);
ifrDiv.innerHTML = "<iframe name='"+fname+"' src='"+src+"'></iframe>"
// глобальные переменные, для примера
// главное - чтобы объект ActiveX и фрейм были доступны из исходного окна
IEFrameNode = ifrDiv.firstChild
IEFrameDocument = rcvNode
}
Как видно из примера, для изоляции ифрейма создается промежуточный документ ActiveX. Поэтому операции над ифреймом и не видны из основного окна.Здесь получается, что объектов window не два, как обычно (основное окно + ифрейм), а три:
Основное окно window
Окно документа htmlfile - доступно как IEFrameDocument.parentWindow
Окно ифрейма - доступно как IEFrameNode.contentWindow, или с использованием getIframeDocument
Глобальные переменные IEFrameNode, IEFrameDocument дают нам прямой доступ из основного окна в окна 2 и 3. Так что можно легко отправить запрос на сервер вызовом
.. Но что дальше? Документ с сервера, наверное, захочет обратиться к основному окну. Например, вызвать его функцию handleMessage() с некоторым сообщением.
И здесь есть некоторые сложности. Чтобы вызвать функцию основного окна, javascript-код изнутри ифрейма должен пробиться через промежуточный htmlfile.
Но htmlfile - это отдельный HTML-документ. Политика безопасности same origin policy запрещает взаимный javascript-доступ между документами
с сайта и новосозданным htmlfile.
Чтобы такой доступ получить, нужно заранее, из основного окна, протянуть ссылку из окна htmlfile в основное окно:
// rcvNode: htmlfile, объект, сходный с document
// rcvNode.parentWindow - окно, соответствующее rcvNode
// делаем ссылку из этого окна на функцию из основного окна
rcvNode.parentWindow.deliver = handleMessage
Если ссылка между окнами уже существует, то same origin policy не проверяется, так что документ с сервера может быть, например, таким:
Если Вы решите использовать этот подход - позвольте мне сэкономить возможные часы копания в отладчике. До этого обсуждался метод GET.
В нем достаточно ссылки на iframe.
С другой стороны, в методе POST нужно присвоить form.target не сам иферейм, а имя ифрейма, причем этот ифрейм должен быть виден из текущего окна.
Легко проверить, что простой пост в фрейм "frame3" (в примере) не даст результата, т.к этот фрейм не виден.
// не пашет, откроет /server_push/parentHere.html в новом окне, т.к не увидит frame3
postToIframe({prop:'val'}, '/server_push/parentHere.html', 'frame3')
Для того, чтобы форма увидела ифрейм, ее нужно создать в том же окне. Это можно сделать массой способов. Например, добавить нужные скрипты в htmlfile:
Все основное, надеюсь, в тексте есть. Если есть более глубокий интерес - приятного копания в отладчике .
Описанный способ предлагает идеальную реализацию iframe-транспорта, которая, к сожалению, работает только в IE. А для остальных браузеров можно использовать либо обычный iframe-транспорт, либо другие транспорты.
Базовый пример использования iframe для COMET c использованием GET вы можете скачать.
Вот такая проблема. Нигде не могу найти ответ. Если в айфрейм используешь ссылку на чужой сайт. То в айфрейм страница подгружается с левого верхнего угла. А иногда необходимо чтоб окно подгружалось из середины или после отступа 100 пикселей сверху, Или что самое правельное вставить в айфрем поиск который по образцу найдет нужный участок на странице и подгрузит начиная с него. Задача мне показалась сначала элементарной вствить бы скрипт поиска внутрь окна и вбить вместо переменной постоянную, однако почему то не вышло ничего.
В этом ключе сделал прогу, но... работает наполовину. Кто подскажет- почему и как лучше сделать?
Имеется:
html, в нем десяток узлов javascript и <div id=a1 style='hiden'> разное &lgt;/div>
Делается:
1) Первый script через document.getElementById('a1').innerHTML забирает "разное" и рисует формы
<form id=a2> <textarea id=textar>разное-2</textarea></form>
и вторую <iframe name=fout1 id=a3> </iframe>
где "разное-2"=F("разное"+KB) - всё делается ОК
2) Второй script через document.getElementById('textar').innerHTML забирает "разное-2" и <post ...php... target=fout1> - всё делается ОК -> на php попадает "разное-2", и через секунду fout1 заполняется ответом "bla-bla-bla"
3) Третий script пробует:
var str1=document.getElementById('a3').innerHTML
var str1=document.getElementById('a3').value (глупость, но пробует)
var str1=document.getElementById('fout1').innerHTML
и др...
- пытается выковырнуть из из fout1 "bla-bla-bla" через 10 секунд после post , но ничего не получается. В чем ошибка?
Как ещё можно реализовать функцию var str1= ...(ответ от php на post-запрос), либо выбить из php в java-функцию результат на запрос "разное-2" по протоколу post (или иным образом (каким?). Есть рабочий пример?
У меня в опере (версия 10.10) оба примера - с динамическим и статическим iframe [#history2 и #history1]- загрязняют хистори при использовании метода POST =(
Динамически созданный iframe меняет строку статуса IE, а это заметно пользователю. Если сделать его невидимым то еще и выдает ошибку "Отказано в доступе"м(при настройках браузера по умолчанию).
Если ссылка между окнами уже существует, то same origin policy не проверяется, так что документ с сервера может быть, например, таким:
window.parent.deliver("here")
Пробовал:
parentWindow.deliver
parent.parent.deliver
parentWindow.parentWindow.deliver
window.parent.deliver
и много чего прочего
Сколько не пытался пробиться из ифрейма, ничего не получается. Если открывать серверный скрипт в браузере, при просмотре исходного кода страницы вижу ответы сервера. Но ни в какую не вызывается функция главного окна указанная как rcvNode.parentWindow.deliver = handleMessagе
Кто нибудь пробовал этот пример в деле, в чем может быть проблема?
частенько замечал в ИЕ нестандартное поведение - когда firstChild=null, а lastChild=[object], т.е при наличии у родителя 1 CHILD`а атрибута firstChild - нет, lastChild - есть!
поэтому этот код с БАГОМ
// убить все содержание из временной формы
19 while(phonyForm.firstChild){
20 phonyForm.removeChild(phonyForm.firstChild);
Не подскажите есть плеер на ява скрипте, когда вставляешь обычный текст все нормально, а когда вставляешь ссылку, появляется отступ в вниз. Негде не могу найти в коде как убрать
В абзаце про #GET: "GET-запрос - всего лишь перевод iframe на новый URL. Его лучше всего осуществлять вызовом iframeDocument.location.src.replace(newURL)." - здесь должно быть iframeDocument.location.replace(newURL) (src лишний).
Подскажите пожалуйста, а как реализовать что б в iframe открывался сайт но с условием если xmlhttp.status == 200, в противном случае браузер повторял бы попытку? Т.е. если сервер сильно нагружен что б не приходилось вручную долбить F5 а просто автоматизировать этот процесс
Надо понимать, хотя бы в общих, что такое формальные языки, формальные грамматики, контекстно-свободные грамматики, лексический анализ и т.д. Можно почитать в википедии, например: Super Mario Bros
Iframe History победить легко : только POST, а после на сервере header location
Функция для создания IFRAME:
Можно поподробнее про POST, GET побеждаю, благодаря динамично создаваемому IFRAME.
также добавлю уплоад на jQuery:
Вот такая проблема. Нигде не могу найти ответ. Если в айфрейм используешь ссылку на чужой сайт. То в айфрейм страница подгружается с левого верхнего угла. А иногда необходимо чтоб окно подгружалось из середины или после отступа 100 пикселей сверху, Или что самое правельное вставить в айфрем поиск который по образцу найдет нужный участок на странице и подгрузит начиная с него. Задача мне показалась сначала элементарной вствить бы скрипт поиска внутрь окна и вбить вместо переменной постоянную, однако почему то не вышло ничего.
В этом ключе сделал прогу, но... работает наполовину. Кто подскажет- почему и как лучше сделать?
Имеется:
html, в нем десяток узлов javascript и <div id=a1 style='hiden'> разное &lgt;/div>
Делается:
1) Первый script через document.getElementById('a1').innerHTML забирает "разное" и рисует формы
<form id=a2> <textarea id=textar>разное-2</textarea></form>
и вторую <iframe name=fout1 id=a3> </iframe>
где "разное-2"=F("разное"+KB) - всё делается ОК
2) Второй script через document.getElementById('textar').innerHTML забирает "разное-2" и <post ...php... target=fout1> - всё делается ОК -> на php попадает "разное-2", и через секунду fout1 заполняется ответом "bla-bla-bla"
3) Третий script пробует:
var str1=document.getElementById('a3').innerHTML
var str1=document.getElementById('a3').value (глупость, но пробует)
var str1=document.getElementById('fout1').innerHTML
и др...
- пытается выковырнуть из из fout1 "bla-bla-bla" через 10 секунд после post , но ничего не получается. В чем ошибка?
Как ещё можно реализовать функцию var str1= ...(ответ от php на post-запрос), либо выбить из php в java-функцию результат на запрос "разное-2" по протоколу post (или иным образом (каким?). Есть рабочий пример?
Хех, приятноу видеть эту статью, когда-то делал чат(вернее онлайн систему сообщений) на ajax - принцип очень похож на этот
У меня в опере (версия 10.10) оба примера - с динамическим и статическим iframe [#history2 и #history1]- загрязняют хистори при использовании метода POST =(
Спасибо огромное, красавчик!
Все нубы никто, нормально объяснить не может, даже пухлые книги!
А ты еще и кучу проблем сразу решил.
Звук "клика" при переходе запросе в iframe. Наверное, .. при переходе запросА в iframe
input value в ie не экранирован
Динамически созданный iframe меняет строку статуса IE, а это заметно пользователю. Если сделать его невидимым то еще и выдает ошибку "Отказано в доступе"м(при настройках браузера по умолчанию).
Если ссылка между окнами уже существует, то same origin policy не проверяется, так что документ с сервера может быть, например, таким:
window.parent.deliver("here")
Пробовал:
parentWindow.deliver
parent.parent.deliver
parentWindow.parentWindow.deliver
window.parent.deliver
и много чего прочего
Сколько не пытался пробиться из ифрейма, ничего не получается. Если открывать серверный скрипт в браузере, при просмотре исходного кода страницы вижу ответы сервера. Но ни в какую не вызывается функция главного окна указанная как rcvNode.parentWindow.deliver = handleMessagе
Кто нибудь пробовал этот пример в деле, в чем может быть проблема?
В IE(6,7) теряются куки, как побороть кто знает?
>> rcvNode.parentWindow.deliver = handleMessage;
не получается "протянуть такую ссылку". Подскажите пожалуйста, как это правильно называется, чтобы можно было нагуглить?
Спасибо
Все хорошо расписано!
Автор, Вы достойны уважения!
Оживленно написано, заставляет задуматься! спасибо
Спасибо
Все вроде работает.... тока вопрос - как сделать чтобы значек закрузки постоянно не висел на вкладке браузера7 бесит((( его реально убрать как-нить7
частенько замечал в ИЕ нестандартное поведение - когда firstChild=null, а lastChild=[object], т.е при наличии у родителя 1 CHILD`а атрибута firstChild - нет, lastChild - есть!
поэтому этот код с БАГОМ
// убить все содержание из временной формы
19 while(phonyForm.firstChild){
20 phonyForm.removeChild(phonyForm.firstChild);
Как все таки победить проблему "отказано в доступе"
Индикатор загрузки есть в ff7, chrome, дазнт верк короче..
Не подскажите есть плеер на ява скрипте, когда вставляешь обычный текст все нормально, а когда вставляешь ссылку, появляется отступ в вниз. Негде не могу найти в коде как убрать
Отлично все расписано. Благодарен!
Вопрос - есть ли библиотеки js для реализации поддержки (в т.ч. кросс-браузерной) описанного в этой статье функционала?
В абзаце про #GET: "GET-запрос - всего лишь перевод iframe на новый URL. Его лучше всего осуществлять вызовом iframeDocument.location.src.replace(newURL)." - здесь должно быть iframeDocument.location.replace(newURL) (src лишний).
Подскажите пожалуйста, а как реализовать что б в iframe открывался сайт но с условием если xmlhttp.status == 200, в противном случае браузер повторял бы попытку? Т.е. если сервер сильно нагружен что б не приходилось вручную долбить F5 а просто автоматизировать этот процесс
хороший ресурс
И кому нужен этот IE? Как предотвратить изменение истории в Firefox, Chrome и Opera (на звук щелчка и индикатор загрузки наплевать)?
Скажите, откуда отправлять POST, из главного окна или фрейма? Как при этом оставлять открытым COMET - соединение? или нужно переподключаться?
Надо понимать, хотя бы в общих, что такое формальные языки, формальные грамматики, контекстно-свободные грамматики, лексический анализ и т.д. Можно почитать в википедии, например: Super Mario Bros