Обнаружить сбой загрузки содержимого iframe - PullRequest
53 голосов
/ 17 декабря 2008

Я могу определить, когда содержимое iframe загружено, используя событие load . К сожалению, для моих целей есть две проблемы с этим:

  • Если при загрузке страницы произошла ошибка (404/500 и т. Д.), Событие загрузки никогда не запускается.
  • Если не удалось загрузить некоторые изображения или другие зависимости, событие загрузки запускается как обычно.

Есть ли способ надежно определить, произошла ли какая-либо из перечисленных ошибок?

Я пишу полупериодическое полуполученное приложение на основе Mozilla / XULRunner, поэтому приветствуются решения, которые работают только в Mozilla.

Ответы [ 6 ]

14 голосов
/ 17 декабря 2008

Если у вас есть контроль над страницей iframe (и страницы находятся на одном доменном имени), стратегия может быть следующей:

  • В родительском документе инициализируйте переменную var iFrameLoaded = false;
  • Когда документ iframe загружен, установите эту переменную в родительском элементе на true, вызывая, например, из документа iframe родительскую функцию (setIFrameLoaded();).
  • проверьте флаг iFrameLoaded, используя объект timer (установите таймер на желаемый предел тайм-аута) - если флаг все еще равен false, вы можете сказать, что iframe загружался не регулярно.

Надеюсь, это поможет.

8 голосов
/ 22 декабря 2008

У меня недавно возникла эта проблема, и мне пришлось прибегнуть к настройке действия опроса Javascript на родительской странице (которая содержит тег IFRAME). Эта функция JavaScript проверяет содержимое IFRAME на наличие явных элементов, которые должны существовать только в ХОРОШЕМ ответе. Это предполагает, конечно, что вам не нужно иметь дело с нарушением «политики одного и того же происхождения».

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

После заранее определенного времени и / или количества неудачных попыток обнаружить ожидаемый элемент (ы) JavaScript изменяет атрибут SRC IFRAME (чтобы запросить у моего сервлета) страницу ошибок, дружественную пользователю, в отличие от отображения типичного Сообщение об ошибке HTTP. JavaScript также может легко изменить атрибут SRC, чтобы сделать совершенно другой запрос.

function checkForContents(){
var contents=document.getElementById('myiframe').contentWindow.document
if(contents){
    alert('found contents of myiframe:' + contents);
    if(contents.documentElement){
        if(contents.documentElement.innerHTML){
            alert("Found contents: " +contents.documentElement.innerHTML);
            if(contents.documentElement.innerHTML.indexOf("FIND_ME") > -1){
                openMediumWindow("woot.html", "mypopup");
            }
        }
    }
}

}

1 голос
/ 02 марта 2019

Это очень поздний ответ, но я оставлю его тому, кто в нем нуждается.

Задача: загрузить содержимое iframe из разных источников, выдать onLoaded в случае успеха и onError при ошибке загрузки .

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

1. iframe Это был небольшой шок для меня, что iframe имеет только событие onload, и оно вызывается при нагрузке и при ошибке, нет способа узнать, является ли это ошибкой или нет.

2. performance.getEntriesByType ( 'Ресурс') . Этот метод возвращает загруженные ресурсы. Похоже, что нам нужно. Но какой позор, Firefox всегда добавляет Resource в массив ресурсов, независимо от того, загружен он или нет. К Resource экземпляру не было никакого способа узнать, был ли он успешным. По-прежнему. Кстати, этот метод не работает в IOS <11. </p>

3. скрипт Я пытался загрузить html, используя тег <script>. Выдает onload и onerror правильно, к сожалению, только в Chrome.

И когда я был готов сдаться, мой старший коллега рассказал мне о теге html4 <object>. Это похоже на тег <iframe>, за исключением того, что у него есть запасные варианты, когда контент не загружен. Это звучит как то, что нам нужно! К сожалению, это не так просто, как кажется.

РАЗДЕЛ КОДА

var obj = document.createElement('object');

// we need to specify a callback (i will mention why later)
obj.innerHTML = '<div style="height:5px"><div/>'; // fallback

obj.style.display = 'block'; // so height=5px will work
obj.style.visibility = 'hidden'; // to hide before loaded

obj.data = src;

После этого мы можем установить некоторые атрибуты на <object>, как мы хотели бы сделать с iframe. Единственное отличие, мы должны использовать <params>, а не атрибуты, но их имена и значения идентичны.

for (var prop in params) {
    if (params.hasOwnProperty(prop)) {
        var param = document.createElement('param');
        param.name = prop;
        param.value = params[prop];

        obj.appendChild(param);
    }
}

Теперь сложная часть. Как и у многих похожих элементов, <object> не имеет спецификаций для обратных вызовов, поэтому каждый браузер ведет себя по-разному.

  • Chrome . При ошибке и при загрузке генерируется событие load.
  • Firefox . Выдает load и error правильно.
  • Safari . Ничего не излучает ....

Похоже, ничем не отличается от iframe, getEntriesByType, script .... Но у нас есть запасной вариант браузера! Таким образом, поскольку мы устанавливаем резервный (innerHtml) напрямую, мы можем сказать, загружен <object> или нет

function isReallyLoaded(obj) {
    return obj.offsetHeight !== 5; // fallback height
}
/**
 * Chrome calls always, Firefox on load
 */
obj.onload = function() {
    isReallyLoaded(obj) ? onLoaded() : onError();
};

/**
 * Firefox on error
 */
obj.onerror = function() {
    onError();
};

Но что делать с Safari? Старый добрый setTimeout.

var interval = function() {
    if (isLoaded) { // some flag
        return;
    }

    if (hasResult(obj)) {
        if (isReallyLoaded(obj)) {
            onLoaded();
        } else {
            onError();
        }
    }

    setTimeout(interval, 100);
};

function hasResult(obj) {
    return obj.offsetHeight > 0;
}

Да .... не так быстро. Дело в том, что <object> при сбое не упомянул в поведении спецификаций:

  1. Пытается загрузить (размер = 0)
  2. Сбой (размер = любой) действительно
  3. Откат (размер = как в innnerHtml)

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

var interval = function() {
    if (isLoaded) { // some flag
        return;
    }

    if (hasResult(obj)) {
        if (isReallyLoaded(obj)) {
            interval.count++;
            // needs less then 400ms to fallback
            interval.count > 4 && onLoadedResult(obj, onLoaded);
        } else {
            onErrorResult(obj, onError);
        }
    }

    setTimeout(interval, 100);
};
interval.count = 0;

setTimeout(interval, 100);

Ну и для начала загрузки

document.body.appendChild(obj);

Вот и все . Я пытался объяснить код в каждой детали, поэтому он может выглядеть не так глупо.

P.S. WebDev отстой

1 голос
/ 10 октября 2009

Я думаю, что событие pagehow вызывается для страниц с ошибками. Или, если вы делаете это из Chrome, проверьте запрос слушателя прогресса, чтобы узнать, является ли это каналом HTTP, и в этом случае вы можете получить код состояния.

Что касается зависимости страниц, я думаю, что вы можете сделать это только из chrome, добавив перехватчик событий onerror, и даже тогда он будет обнаруживать ошибки только в элементах, а не в фонах CSS или других изображениях.

0 голосов
/ 22 августа 2017

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

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

Сначала я думал сделать AJAX-вызов, как и все остальные, за исключением того, что изначально он не работал для меня, так как я использовал jQuery. Это прекрасно работает, если вы делаете XMLHttpRequest:

var url = http://url_to_test.com/
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
    if (this.readyState == 4 && this.status != 200) {
        console.log("iframe failed to load");
    }
};
xhttp.open("GET", url, true);
xhttp.send();

Edit:
Таким образом, этот метод работает нормально, за исключением того, что он имеет много ложных негативов (собирает много вещей, которые будут отображаться в iframe) из-за малярии перекрестного происхождения. Я смог обойти это, выполнив запрос CURL / Web на сервере, а затем проверил заголовки ответа: а) существует ли веб-сайт и б) установлен ли заголовок x-frame-options.

Это не проблема, если вы запускаете свой собственный веб-сервер, так как вы можете сделать свой собственный вызов API для него.

Моя реализация в node.js:

app.get('/iframetest',function(req,res){ //Call using /iframetest?url=url - needs to be stripped of http:// or https://
   var url = req.query.url; 
    var request = require('https').request({host: url}, function(response){ //This does an https request - require('http') if you want to do a http request
        var headers = response.headers;
        if (typeof headers["x-frame-options"] != 'undefined') {
            res.send(false); //Headers don't allow iframe
        } else {
            res.send(true); //Headers don't disallow iframe
        }
    });
    request.on('error',function(e){
       res.send(false); //website unavailable
    });
    request.end();
});
0 голосов
/ 02 августа 2013

Иметь идентификатор самого верхнего элемента (body) на странице, загружаемой в ваш iframe.

в обработчике загрузки вашего iframe, проверьте, возвращает ли getElementById () ненулевое значение. Если это так, iframe загрузился успешно. иначе это не удалось.

в этом случае поместите frame.src = "about: blank". Обязательно удалите обработчик нагрузки перед этим.

...