检测 iframe 内容加载失败

发布于 2024-07-11 06:20:32 字数 274 浏览 6 评论 0原文

我可以使用 load 事件检测 iframe 的内容何时加载。 不幸的是,就我的目的而言,这有两个问题:

  • 如果加载页面时出现错误(404/500 等),则永远不会触发加载事件。
  • 如果某些图像或其他依赖项加载失败,则会照常触发加载事件。

有什么方法可以可靠地确定是否发生了上述错误?

我正在编写一个基于 Mozilla/XULRunner 的半网络半桌面应用程序,因此欢迎仅适用于 Mozilla 的解决方案。

I can detect when the content of an iframe has loaded using the load event. Unfortunately, for my purposes, there are two problems with this:

  • If there is an error loading the page (404/500, etc), the load event is never fired.
  • If some images or other dependencies failed to load, the load event is fired as usual.

Is there some way I can reliably determine if either of the above errors occurred?

I'm writing a semi-web semi-desktop application based on Mozilla/XULRunner, so solutions that only work in Mozilla are welcome.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

天涯沦落人 2024-07-18 06:20:33

我认为 pageshow 事件是针对错误页面触发的。 或者,如果您从 chrome 执行此操作,则检查进度侦听器的请求以查看它是否是 HTTP 通道,在这种情况下您可以检索状态代码。

至于页面依赖关系,我认为你只能通过添加捕获 onerror 事件侦听器从 chrome 中做到这一点,即使如此,它也只会发现元素中的错误,而不是 CSS 背景或其他图像。

I think that the pageshow event is fired for error pages. Or if you're doing this from chrome, then your check your progress listener's request to see if it's an HTTP channel in which case you can retrieve the status code.

As for page dependencies, I think you can only do this from chrome by adding a capturing onerror event listener, and even then it will only find errors in elements, not CSS backgrounds or other images.

你列表最软的妹 2024-07-18 06:20:33

为 iframe 中加载的页面中最顶部(主体)元素提供一个 id。

在 iframe 的 Load 处理程序上,检查 getElementById() 是否返回非 null 值。
如果是,则 iframe 已成功加载。 否则它就失败了。

在这种情况下,请输入frame.src="about:blank"。 确保在执行此操作之前删除负载处理程序。

Have a id for the top most (body) element in the page that is being loaded in your iframe.

on the Load handler of your iframe, check to see if getElementById() returns a non null value.
If it is, iframe has loaded successfully. else it has failed.

in that case, put frame.src="about:blank". Make sure to remove the loadhandler before doing that.

稚气少女 2024-07-18 06:20:33

如果 iframe 加载在与父页面相同的源上,那么您可以这样做:

iframeEl.addEventListener('load', function() {
    // NOTE: contentDocument is null if a connection error occurs or if
    //       X-Frame-Options is not SAMESITE (which could happen with
    //       4xx or 5xx error pages if the corresponding error handlers
    //       do not specify SAMESITE). If error handlers do not specify
    //       SAMESITE, then networkErrorOccurred will incorrectly be set
    //       to true.
    const networkErrorOccurred = !iframeEl.contentDocument;
    const serverErrorOccurred = (
        !networkErrorOccurred &&
        !iframeEl.contentDocument.querySelector('#well-known-element')
    );
    if (networkErrorOccurred || serverErrorOccurred) {
        let errorMessage;
        if (networkErrorOccurred) {
            errorMessage = 'Error: Network error';
        } else if (serverErrorOccurred) {
            errorMessage = 'Error: Server error';
        } else {
            // Assert that the above code is correct.
            throw new Error('networkErrorOccurred and serverErrorOccurred are both false');
        }
        
        alert(errorMessage);
    }
});

If the iframe is loaded on the same origin as the parent page, then you can do this:

iframeEl.addEventListener('load', function() {
    // NOTE: contentDocument is null if a connection error occurs or if
    //       X-Frame-Options is not SAMESITE (which could happen with
    //       4xx or 5xx error pages if the corresponding error handlers
    //       do not specify SAMESITE). If error handlers do not specify
    //       SAMESITE, then networkErrorOccurred will incorrectly be set
    //       to true.
    const networkErrorOccurred = !iframeEl.contentDocument;
    const serverErrorOccurred = (
        !networkErrorOccurred &&
        !iframeEl.contentDocument.querySelector('#well-known-element')
    );
    if (networkErrorOccurred || serverErrorOccurred) {
        let errorMessage;
        if (networkErrorOccurred) {
            errorMessage = 'Error: Network error';
        } else if (serverErrorOccurred) {
            errorMessage = 'Error: Server error';
        } else {
            // Assert that the above code is correct.
            throw new Error('networkErrorOccurred and serverErrorOccurred are both false');
        }
        
        alert(errorMessage);
    }
});
江湖彼岸 2024-07-18 06:20:32

如果您可以控制 iframe 页面(并且页面位于同一域名上),则策略可以如下:

  • 在父文档中,初始化变量 var iFrameLoaded = false;
  • 当 iframe加载文档后,将父级中的此变量设置为 true 从 iframe 文档调用父级的函数 (例如 setIFrameLoaded();)。
  • 使用 timer 对象检查 iFrameLoaded 标志(将计时器设置为您首选的超时限制) - 如果该标志仍然为 false,您可以判断 iframe 没有定期加载。

我希望这会有所帮助。

If you have control over the iframe page (and the pages are on the same domain name), a strategy could be as follows:

  • In the parent document, initialize a variable var iFrameLoaded = false;
  • When the iframe document is loaded, set this variable in the parent to true calling from the iframe document a parent's function (setIFrameLoaded(); for example).
  • check the iFrameLoaded flag using the timer object (set the timer to your preferred timeout limit) - if the flag is still false you can tell that the iframe was not regularly loaded.

I hope this helps.

流年里的时光 2024-07-18 06:20:32

这是一个很晚的答案,但我会把它留给需要它的人。

任务: 加载 iframe 跨源内容,成功时发出 onLoaded ,加载错误时发出 onError

这是我可以开发的最跨浏览器源独立的解决方案。 但首先我将简要介绍我所采用的其他方法以及它们为何不好。

1. iframe 这对我来说有点震惊,iframe 只有 onload 事件,它在加载和错误时调用,没有办法知道它是否错误。

2. Performance.getEntriesByType('resource')。 该方法返回加载的资源。 听起来像我们需要的。 但遗憾的是,无论加载还是失败,firefox总是将Resource添加到资源数组中。 无法通过 Resource 实例知道它是否成功。 照常。 顺便说一句,这个方法在ios<11下不起作用。

3. script 我尝试使用

当我准备放弃时,我的老同事告诉我关于 html4 标签 的事情。 它类似于 标记,只不过它在未加载内容时有回退。 这听起来正是我们所需要的! 遗憾的是,这并不像听起来那么容易。

代码部分

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 所做的那样。 唯一的区别是,我们应该使用 ,而不是属性,但它们的名称和值是相同的。

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

        obj.appendChild(param);
    }
}

现在,困难的部分。 与许多类似的元素一样,没有回调规范,因此每个浏览器的行为都不同。

  • Chrome。 发生错误和加载时会发出 load 事件。
  • 火狐。 正确发出 loaderror
  • Safari。 不发出任何东西......

看起来与 iframegetEntriesByTypescript 没有什么不同......
但是,我们有本机浏览器后备! 所以,因为我们直接设置了fallback(innerHtml),所以我们可以判断是否已加载

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

是啊……没那么快。 问题是,失败时在规范行为中未提及:

  1. 尝试加载(size=0)
  2. 失败(size=any)确实
  3. 回退(size=如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);

仅此而已。 我试图详细解释代码,所以它看起来可能不那么愚蠢。

PS WebDev 很糟糕

This is a very late answer, but I will leave it to someone who needs it.

Task: load iframe cross-origin content, emit onLoaded on success and onError on load error.

This is the most cross browsers origin independent solution I could develop. But first of all I will briefly tell about other approaches I had and why they are bad.

1. iframe That was a little shock for me, that iframe only has onload event and it is called on load and on error, no way to know it is error or not.

2. performance.getEntriesByType('resource'). This method returns loaded resources. Sounds like what we need. But what a shame, firefox always adds Resource in resources array no matter it is loaded or failed. No way to know by Resource instance was it success. As usual. By the way, this method does not work in ios<11.

3. script I tried to load html using <script> tag. Emits onload and onerror correctly, sadly, only in Chrome.

And when I was ready to give up, my elder collegue told me about html4 tag <object>. It is like <iframe> tag except it has fallbacks when content is not loaded. That sounds like what we are need! Sadly it is not as easy as it sounds.

CODE SECTION

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;

After this we can set some attributes to <object> like we'd wanted to do with iframe. The only difference, we should use <params>, not attributes, but their names and values are identical.

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

        obj.appendChild(param);
    }
}

Now, the hard part. Like many same-like elements, <object> doesn't have specs for callbacks, so each browser behaves differently.

  • Chrome. On error and on load emits load event.
  • Firefox. Emits load and error correctly.
  • Safari. Emits nothing....

Seems like no different from iframe, getEntriesByType, script....
But, we have native browser fallback! So, because we set fallback (innerHtml) directly, we can tell if <object> is loaded or not

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

But what to do with Safari? Good old 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;
}

Yeah.... not so fast. The thing is, <object> when fails has unmentioned in specs behaviour:

  1. Trying to load (size=0)
  2. Fails (size = any) really
  3. Fallback (size = as in innnerHtml)

So, code needs a little enhancement

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

Well, and to start loading

document.body.appendChild(obj);

That is all. I tried to explain code in every detail, so it may look not so foolish.

P.S. WebDev sucks

风向决定发型 2024-07-18 06:20:32

我最近遇到了这个问题,不得不在父页面(包含 IFRAME 标记)上设置 Javascript 轮询操作。 此 JavaScript 函数检查 IFRAME 内容中是否存在仅应存在于 GOOD 响应中的显式元素。 当然,这是假设您不必处理违反“同源政策”的问题。

我没有检查可能由许多不同的网络资源生成的所有可能的错误。我只是检查了我知道应该处于良好响应中的一个恒定的积极元素。

在预定时间和/或尝试检测预期元素失败的次数后,JavaScript 会修改 IFRAME 的 SRC 属性(以从我的 Servlet 请求)用户友好的错误页面,而不是显示典型的 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");
            }
        }
    }
}

}

I had this problem recently and had to resort to setting up a Javascript Polling action on the Parent Page (that contains the IFRAME tag). This JavaScript function checks the IFRAME's contents for explicit elements that should only exist in a GOOD response. This assumes of course that you don't have to deal with violating the "same origin policy."

Instead of checking for all possible errors which might be generated from the many different network resources.. I simply checked for the one constant positive Element(s) that I know should be in a good response.

After a pre-determined time and/or # of failed attempts to detect the expected Element(s), the JavaScript modifies the IFRAME's SRC attribute (to request from my Servlet) a User Friendly Error Page as opposed to displaying the typical HTTP ERROR message. The JavaScript could also just as easily modify the SRC attribute to make an entirely different request.

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

}

简美 2024-07-18 06:20:32

如果你想检查iframe抓取的url是否出错,可以先进行抓取,如果抓取没有失败,则设置iframe的src。

  fetch(src)
    .then((response) => {
      if (response.status >= 200 && response.status < 300) {
        iframe.src = src;
      }
    })

If you want to check if the url iframe will fetch is faulty, you can first make a fetch, then set the iframe's src if fetch did not fail.

  fetch(src)
    .then((response) => {
      if (response.status >= 200 && response.status < 300) {
        iframe.src = src;
      }
    })
妄想挽回 2024-07-18 06:20:32

没有完全回答你的问题,但我对答案的搜索把我带到了这里,所以我发帖以防万一其他人有类似的查询。

它并不完全使用加载事件,但它可以检测网站是否可访问和可调用(如果是,则理论上应该加载 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 请求,然后检查响应标头 a) 网站是否存在,b) 标头是否设置了 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();
});

Doesn't answer your question exactly, but my search for an answer brought me here, so I'm posting just in case anyone else had a similar query to me.

It doesn't quite use a load event, but it can detect whether a website is accessible and callable (if it is, then the iFrame, in theory, should load).

At first, I thought to do an AJAX call like everyone else, except that it didn't work for me initially, as I had used jQuery. It works perfectly if you do a 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:
So this method works ok, except that it has a lot of false negatives (picks up a lot of stuff that would display in an iframe) due to cross-origin malarky. The way that I got around this was to do a CURL/Web request on a server, and then check the response headers for a) if the website exists, and b) if the headers had set x-frame-options.

This isn't a problem if you run your own webserver, as you can make your own api call for it.

My implementation in 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();
});
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文