Обнаружить сбой загрузки содержимого iframe

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

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

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

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


person Daniel Cassidy    schedule 17.12.2008    source источник


Ответы (6)


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

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

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

person splattne    schedule 17.12.2008
comment
1) Зачем добавлять JavaScript на страницу iframe? Я мог бы так же легко использовать setTimeout в сочетании с событием загрузки. 2) Это не позволяет надежно определить, происходит ли сбой загрузки или почему, а только удается ли он в течение ограниченного времени. 3) Не обнаруживает случай, когда зависимости страницы не загружаются. - person Daniel Cassidy; 17.12.2008
comment
Разные ресурсы будут загружаться в разное время. Эта непредсказуемость убьет ваш подход к Дэниелу Кэссиди, так как вам придется позволить себе достаточно длительный тайм-аут. В то же время кратковременные ошибки загрузки будут просто висеть на экране, пока таймер не решит, что ресурс не удалось загрузить, и не обработает ошибку. - person seebiscuit; 23.10.2015

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

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

После заранее определенного времени и/или количества неудачных попыток обнаружить ожидаемый элемент (элементы) JavaScript изменяет атрибут SRC IFRAME (чтобы запросить у моего сервлета) удобную для пользователя страницу ошибки вместо отображения типичного сообщения HTTP ERROR. . 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");
            }
        }
    }
}

}

person Chris    schedule 22.12.2008

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

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

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

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

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

<сильный>3. script Я пытался загрузить 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> не имеет спецификаций для обратных вызовов, поэтому каждый браузер ведет себя по-разному.

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

Похоже, ничем не отличается от 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();
};

Но что делать с Сафари? Старый добрый 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 отстой

person Дмитрий Киселев    schedule 01.03.2019
comment
Это пустая трата времени, пробующая все доступные варианты, я должен сказать, что браузеры должны понимать, применяя какую-либо безопасность и запрещая любую функциональность, которую они должны предоставить альтернативу для выполнения этой работы безопасным образом. - person KumailR; 21.04.2021
comment
Это очень распространенная проблема, когда мы открываем ссылку внутри iframe, и эта страница не загружается из-за проблемы XFRAME=sameorigin, и мы бессильны справиться с этим случаем и показать какое-то правильное сообщение и создать одинаковый UX в каждом браузере. - person KumailR; 21.04.2021

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

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

person Neil    schedule 09.10.2009

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

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

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

person Kasturi    schedule 02.08.2013
comment
что, если у нас нет контроля над страницей, которую мы загружаем. тогда как быть в этой ситуации?? - person PHP_RIDER; 17.12.2015

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

Он не использует событие загрузки, но может определить, доступен ли веб-сайт и доступен ли он для вызова (если это так, то 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();

Редактировать:
Таким образом, этот метод работает нормально, за исключением того, что он имеет много ложных отрицательных результатов (собирает много материала, который будет отображаться в 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();
});
person immanis    schedule 22.08.2017