web-dev-qa-db-ja.com

iframeのコンテンツのロードの失敗を検出する

loadイベントを使用して、iframeのコンテンツがロードされたことを検出できます。残念ながら、私の目的のために、これには2つの問題があります。

  • ページの読み込みエラー(404/500など)がある場合、loadイベントは発生しません。
  • 一部のイメージまたは他の依存関係がロードに失敗した場合、通常どおり、ロードイベントが発生します。

上記のエラーのいずれかが発生したかどうかを確実に判断できる方法はありますか?

私はMozilla/XULRunnerに基づいたセミウェブセミデスクトップアプリケーションを書いているので、Mozillaでのみ機能するソリューションを歓迎します。

53
Daniel Cassidy

Iframeページを制御できる場合(およびページが同じドメイン名にある場合)、戦略は次のようになります。

  • 親ドキュメントで、変数var iFrameLoaded = false;を初期化します
  • Iframeドキュメントがロードされたら、親のこの変数をtrueに設定し、iframeドキュメントから親の関数(setIFrameLoaded();などを呼び出します。
  • iFrameLoadedオブジェクトを使用してtimerフラグを確認します(タイマーをお好みのタイムアウト制限に設定します)-フラグがまだfalseの場合、iframeが定期的にロードされていないことがわかります。

これが役立つことを願っています。

14
splattne

最近この問題が発生し、親ページ(IFRAMEタグを含む)でJavaScriptポーリングアクションを設定することに頼らなければなりませんでした。このJavaScript関数は、IFRAMEのコンテンツをチェックして、GOOD応答にのみ存在する明示的な要素を探します。もちろん、これは「同じオリジンポリシー」に違反することに対処する必要がないことを前提としています。

多くの異なるネットワークリソースから生成される可能性のあるすべてのエラーをチェックする代わりに、応答が良好であることがわかっている1つの一定の肯定的な要素をチェックしました。

所定の時間および/または予想される要素の検出に失敗した回数の後に、JavaScriptはIFRAMEのSRC属性を変更します(私のサーブレットからリクエストするため)、一般的な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");
            }
        }
    }
}

}

8
Chris

これは非常に遅い答えですが、必要な人に任せます。

タスク:iframeクロスオリジンコンテンツをロードし、成功時にonLoadedを発行し、ロードエラー時にonErrorを発行します

これは、私が開発できる最もクロスブラウザなOrigin独立ソリューションです。しかし、まず最初に、私が持っていた他のアプローチとそれらが悪い理由について簡単に説明します。

1。iframe iframeにはonloadイベントしかなく、ロード時およびエラー時に呼び出され、エラーであるかどうかを知る方法はありません。

2。performance.getEntriesByType( 'resource')。このメソッドは、ロードされたリソースを返します。必要なもののように聞こえます。しかし、残念なことに、firefoxは、リソースが読み込まれても失敗しても、常にリソース配列にリソースを追加します。 Resourceインスタンスで知る方法は成功しませんでした。いつものように。ところで、このメソッドはios <11では機能しません。

。script<script>タグを使用してhtmlをロードしようとしました。 onloadonerrorを正しく、残念ながら、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;

この後、iframeでやりたいように、いくつかの属性を<object>に設定できます。唯一の違いは、属性ではなく<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イベントを発行します。
  • Firefoxloadおよびerrorを正しく発行します。
  • サファリ。何も出さない...

iframegetEntriesByTypescript。と何の違いもないようです...しかし、ネイティブブラウザのフォールバックがあります!したがって、フォールバック(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がひどい

エラーページに対してpageshowイベントが発生すると思います。または、これをchromeから実行している場合、進行状況リスナーのリクエストをチェックして、HTTPチャンネルであるかどうかを確認します。この場合、ステータスコードを取得できます。

ページの依存関係については、キャプチャするonerrorイベントリスナーを追加することでchromeからのみこれを行うことができ、CSSの背景やその他の画像ではなく要素のエラーのみを検出します。

1
Neil

あなたの質問に正確には答えませんが、答えを探してここに来たので、他の誰かが私に似た質問をした場合に備えて投稿しています。

ロードイベントを使用することはあまりありませんが、Webサイトがアクセス可能かつ呼び出し可能かどうかを検出できます(アクセス可能な場合は、理論的にはiFrameをロードする必要があります)。

最初は、AJAXの呼び出しを他のすべての人と同じように行うことを考えていましたが、jQueryを使用していたため、最初は動作しませんでした。

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要求を実行し、a)Webサイトが存在する場合、およびb)ヘッダーがx-frame-optionsを設定した場合、応答ヘッダーを確認することでした。

独自のWebサーバーを実行する場合は、独自の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
lels

Iframeに読み込まれているページの一番上の(body)要素のIDを持っています。

iframeのLoadハンドラで、getElementById()がnull以外の値を返すかどうかを確認します。そうであれば、iframeは正常にロードされています。それ以外の場合は失敗しました。

その場合、frame.src = "about:blank"を配置します。それを行う前に、必ずロードハンドラーを削除してください。

0
Kasturi