web-dev-qa-db-fra.com

Détecter un échec de chargement du contenu d'un iframe

Je peux détecter le chargement du contenu d'un iframe à l'aide de l'événement load. Malheureusement, pour mes besoins, cela pose deux problèmes:

  • En cas d'erreur lors du chargement de la page (404/500, etc.), l'événement de chargement n'est jamais déclenché.
  • Si le chargement de certaines images ou d'autres dépendances a échoué, l'événement de chargement est déclenché comme d'habitude.

Existe-t-il un moyen de déterminer de manière fiable si l'une des erreurs ci-dessus s'est produite?

J'écris une application semi-web semi-bureau basée sur Mozilla/XULRunner. Les solutions qui fonctionnent uniquement dans Mozilla sont donc les bienvenues.

52
Daniel Cassidy

Si vous avez le contrôle sur la page iframe (et que les pages se trouvent sur le même nom de domaine), une stratégie pourrait être la suivante:

  • Dans le document parent, initialisez une variable var iFrameLoaded = false;
  • Lorsque le document iframe est chargé, définissez cette variable dans le parent sur true appelant à partir du document iframe d'une fonction (setIFrameLoaded(); du parent par exemple).
  • vérifiez l'indicateur iFrameLoaded à l'aide de l'objet timer (définissez le délai sur votre délai d'expiration préféré). Si l'indicateur est toujours faux, vous pouvez savoir que l'iframe n'a pas été chargé régulièrement.

J'espère que ça aide.

14
splattne

J'ai eu ce problème récemment et j'ai dû mettre en place une action de sondage Javascript sur la page parent (qui contient la balise IFRAME). Cette fonction JavaScript vérifie le contenu de l'IFRAME pour les éléments explicites qui ne devraient exister que dans une réponse GOOD. Cela suppose bien entendu que vous n’aurez pas à violer la "même règle d’origine".

Au lieu de vérifier toutes les erreurs possibles qui pourraient être générées à partir des nombreuses ressources réseau différentes…, j'ai simplement vérifié si le ou les éléments positifs constants que je sais devraient recevoir de bonnes réponses.

Après un délai et/ou un nombre prédéterminé de tentatives de détection du ou des éléments attendus, le code JavaScript modifie l'attribut SRC de l'IFRAME (pour demander à mon servlet) une page d'erreur conviviale par opposition à l'affichage du message HTTP ERROR typique . Le code JavaScript pourrait également tout aussi facilement modifier l'attribut SRC pour faire une demande totalement différente.

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");
            }
        }
    }
}

}

8
Chris

Je pense que l'événement pageshow est déclenché pour les pages d'erreur. Ou bien, si vous faites cela à partir de chrome, alors vérifiez votre demande d'écouteur de progression pour voir s'il s'agit d'un canal HTTP, auquel cas vous pouvez récupérer le code d'état.

En ce qui concerne les dépendances de page, je pense que vous ne pouvez le faire qu'à partir de chrome en ajoutant un écouteur d'événement onerror de capture. Même dans ce cas, il ne trouvera que des erreurs dans les éléments, pas dans les arrière-plans CSS ni dans d'autres images.

1
Neil

C'est une réponse très tardive, mais je laisserai cela à quelqu'un qui en a besoin.

Tâche: load iframe cross-Origin content, émettez onLoaded en cas de succès et onError en cas d'erreur de chargement.

C'est la solution indépendante la plus multi-navigateurs que j'ai pu développer. Mais tout d’abord, je vais parler brièvement des autres approches que j’ai eues et de la raison pour laquelle elles sont mauvaises.

1. iframe Ce fut un petit choc pour moi, si iframe a seulement un événement onload et qu'il est appelé au chargement et en cas d'erreur, aucun moyen de savoir qu'il s'agit d'une erreur ou non.

2. performance.getEntriesByType ('ressource') . Cette méthode renvoie les ressources chargées. Cela ressemble à ce dont nous avons besoin. Mais quelle honte, firefox ajoute toujours des ressources dans son tableau de ressources, qu’il soit chargé ou en panne. Pas moyen de savoir par exemple Resource si c'était un succès. Comme d'habitude. À propos, cette méthode ne fonctionne pas dans ios <11.

3. script J'ai essayé de charger du HTML avec la balise <script>. Émet onload et onerror correctement, malheureusement, uniquement dans Chrome.

Et quand j'étais prêt à abandonner, mon collègue aîné m'a parlé du tag html4 <object>. C'est comme la balise <iframe> sauf qu'il y a des replis quand le contenu n'est pas chargé. Cela ressemble à ce dont nous avons besoin! Malheureusement, ce n'est pas aussi facile qu'il y paraît. 

SECTION CODE

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;

Après cela, nous pouvons définir certains attributs sur <object> comme nous l’avions souhaité avec iframe. La seule différence, nous devrions utiliser <params>, pas les attributs, mais leurs noms et leurs valeurs sont identiques.

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

        obj.appendChild(param);
    }
}

Maintenant, la partie difficile. Comme beaucoup d'éléments identiques, <object> n'a pas de spécifications pour les rappels, donc chaque navigateur se comporte différemment.

  • Chrome. En cas d'erreur et de charge, émet l'événement load.
  • Firefox. Emet load et error correctement.
  • Safari. N'émet rien ....

Il semble que ce ne soit pas différent de iframe, getEntriesByType, script.... Mais nous avons un repli du navigateur natif! Donc, comme nous définissons directement fallback (innerHtml), nous pouvons dire si <object> est chargé ou non

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();
};

Mais que faire avec Safari? Bon vieux 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;
}

Ouais .... pas si vite. Le problème est que <object> quand échoue n’a pas été mentionné dans le comportement de specs:

  1. Essayer de charger (taille = 0)
  2. Échoue (taille = aucun) vraiment
  3. Fallback (taille = comme dans innnerHtml)

Donc, le code a besoin d'un peu d'amélioration

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);

Eh bien, et pour commencer à charger

document.body.appendChild(obj);

C'est tout . J'ai essayé d'expliquer le code dans les moindres détails afin qu'il puisse sembler moins idiot.

P.S. WebDev est nul

Ne répond pas à votre question avec précision, mais ma recherche d’une réponse m’a amené ici. Je publie donc juste au cas où quelqu'un d’autre aurait eu une question similaire à moi. 

Il n'utilise pas tout à fait un événement de chargement, mais il peut détecter si un site Web est accessible et appelable (le cas échéant, l'iFrame devrait, en théorie, se charger).

Au début, je pensais passer un appel AJAX comme tout le monde, sauf que cela ne fonctionnait pas pour moi au départ, car jQuery était utilisé. Cela fonctionne parfaitement si vous faites un 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();

Modifier:
Donc, cette méthode fonctionne bien, sauf qu’elle contient beaucoup de faux négatifs (ramasse beaucoup de choses qui s’affichent dans un iframe) en raison de la malarky croisée. La façon dont j'ai réussi à éviter cela consistait à faire une requête CURL/Web sur un serveur, puis à vérifier les en-têtes de réponse pour a) si le site Web existe et b) si les en-têtes avaient défini x-frame-options.

Ce n'est pas un problème si vous utilisez votre propre serveur Web, car vous pouvez faire votre propre appel d'api pour cela.

Mon implémentation dans 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
lels

Ayez un identifiant pour l'élément le plus haut (corps) de la page en cours de chargement dans votre iframe.

dans le gestionnaire de chargement de votre iframe, vérifiez si getElementById () renvoie une valeur non NULL . Si c'est le cas, iframe s'est chargé avec succès. sinon il a échoué.

dans ce cas, mettez frame.src = "about: blank". Assurez-vous de retirer le gestionnaire de chargement avant de le faire.

0
Kasturi