【问题标题】:I have multiple iframes, how to detect the first one that loads我有多个 iframe,如何检测第一个加载的 iframe
【发布时间】:2015-04-25 23:50:32
【问题描述】:

我正在动态创建 iframe 以显示通过 url 获得的文档。每个文档都在一个 iframe 中,而 iframe 又在一个 div 中。我有一个标签栏,它允许显示任何文档,同时隐藏其余文档,这是一个典型的标签页。

我想显示包含接收第一个响应的 iframe 的 div,即显示第一个加载并隐藏其余部分。无法预测哪个会先加载,所以我需要一种方法来检测第一个并显示它并隐藏所有其余部分。

我想我可以通过让 iframe onload 函数检查一个在第一个 onload 处理程序运行时设置为 true 的全局布尔值来做到这一点。

我不知道为什么,但这感觉很容易出错。有一个更好的方法吗?

var firstDocumentReceived = false ; 
function buildFileTabs(evt) {
    firstDocumentReceived = false ;
    var files = evt.target.files;       // files is a list of File objects. 

    for (var i = 0, f; f = files[i]; i++) {
        addTab ( files[i].name );
    }

// create a div to display the requested document
function addTab ( fileName ) {
    var newDiv = document.createElement("div");
    newDiv.id = fileName.replace(".","_") + newRandomNumber() ; 

    // create an iframe to place in the div
    var newIframe = document.createElement("iframe");
    newIframe.onload=iframeLoaded ;
    newIframe.setAttribute("seamless", true );
    newIframe.setAttribute("div_id" , newDiv.id) ;
    newIframe.src = url ; // the iframe will contain a web page returned by the FDS server

    // nest the iframe in the div element
    newDiv.appendChild(newIframe) ;

    // nest the div element in the parent div
    document.getElementById("documentDisplay").appendChild(newDiv) ;
    ...

function iframeLoaded ( event ) {
    if ( firstDocumentReceived === false ) {
        firstDocumentReceived = true ;
        // show the div associated with this event
    } else {
        // hide the div associated with this event
    }

【问题讨论】:

  • 你控制 iframe 的内容吗?
  • 乔,不知道你在问什么。 iframe 内容由服务器返回,但我不知道内容是什么。我只知道传递给服务器的文件名。
  • 如果你可以使用 ES6(或 Promise polyfill),这就是 Promise.race 设计的问题。
  • @joews,这是一个好方法。 @user903724,我修改了我的答案,包括如何使用 ES6 Promise.race()

标签: javascript html iframe


【解决方案1】:

下面,我描述了两种替代方法(A 和 B),您可以解析加载的第一个 iframe:

A) 加载第一个 iframe 时停止加载其他 iframe。

您需要设置一种方法来跟踪正在添加的新 iframe,例如下面显示的 newIframes 数组。您可以通过跟踪newIframes 或在iframeLoaded() 函数中运行以下逻辑之前从DOM 中检索它们来做到这一点。

下面是一个示例,说明如何通过循环其他 iframe、停止加载和隐藏它们来修改 iframeLoaded() 函数:

function iframeLoaded(evt) {
  newIframes.forEach(function (newIframe) {
    if (newIframe.getAttribute('div_id') !== evt.target.getAttribute('div_id')) {
      // stop loading and hide the other iframes
      if (navigator.appName === 'Microsoft Internet Explorer') {
        newIframe.document.execCommand('Stop');
      } else {
        newIframe.stop();
      }
      newIframe.setAttribute('hidden', 'true'); // you can also delete it or set the css display to none
    }
  });
}

B) 按照 joews 的建议使用 Promise.race()

可以使用 jQuery 创建 Promise,但 ES6 和谐包含一个用于 Promise 对象的 Promise.race() 方法。基本上,第一个完成的承诺是在承诺之间的竞争中解决的。

以下是如何修改代码以使用此 ES6 功能的示例:

1) 创建一个新函数 createIframePromise(),为 iframe 创建一个承诺。

function createIframePromise(newIframe) {
    var iframePromise = new Promise(function (resolve, reject) {
        newIframe.onload = function (evt) {
            // when the iframe finish loading, 
            // resolve with the iframe's identifier.
            resolve(evt.target.getAttribute('div_id'));
        }
    });
    return iframePromise;
}

2) 在外部范围内创建一个空数组来保存不同的 iframe 承诺。

// hold the iframe promises.
var iframePromises = []; 

3) 通过创建 iframe 承诺然后将其推送到 iframePromises 数组来修改您的 addTab() 函数。

确保还从该函数中删除行 newIframe.onload=iframeLoaded;,因为我们将其移至上面 #1 中新创建的 createIframePromise() 函数。

function addTab(fileName) {
    // after "newIframe" with properties is created,
    // create its promise and push it to the array.
    iframePromises.push(createIframePromise(newIframe));
    // ...
}

4) 修改您的 buildFileTabs() 函数以在创建 iframe 并将其承诺存储在 iframePromises 数组中之后设置比赛。

function buildFileTabs(evt) {
    // ...
    Promise.race(iframePromises).then(function (firstIframeId) {
        // resolve...
        // put logic to show the first loaded iframe here. 
        // (all other iframe promises lose the race)
    }, function () {
        // reject...
        // nothing b/c "createIframePromise()" doesn't reject anything.
    });
}

【讨论】:

  • Boombox,感谢您深思熟虑的回复。回复:更安全的代码,我应该考虑使用闭包来处理获取/设置 firstDocument 标志吗?
  • 如果您的意思是firstDocumentReceived,您可以将问题中显示的代码包装在一个 IIFE 中。这确保firstDocumentReceived 不会污染全局环境,并防止在IIFE 之外的某处的另一段代码无意中使用或设置同名firstDocumentReceived 变量。避免在全局命名空间中声明变量通常总是一个好主意。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-11-06
  • 2014-07-09
  • 1970-01-01
  • 2013-06-14
  • 1970-01-01
  • 2012-08-29
  • 2012-10-24
相关资源
最近更新 更多