当前位置: 首页 > 知识库问答 >
问题:

如何访问iFrame中的DOM元素

陶沛
2023-03-14

我正在编写一个jQuery插件,它需要能够在iframe中针对DOM元素运行。我现在只是在本地测试这个(即url是file://.../example.html),在Chrome中,我一直按“安全错误:未能从”htmliframeElement“中读取”内容文档“属性:阻止一个来源为”null“的框架访问跨来源的框架。”在Safari中,我只得到一个空文档。

如果父文件和iframe文件都从我的本地磁盘(开发中)出来,并且将从同一个服务器(生产中)出来,我会认为我不会受到跨源问题的影响。

更新

根据下面的建议,我启动了一个简单的本地Web服务器来测试这一点,现在没有得到跨源错误--是的--但也没有得到任何内容。

我的Javascript看起来像

$(document).ready(function(){
  var myFrame = $("iframe"),
      myDocument = $(myFrame.get(0).contentDocument),
      myElements;
  myDocument.ready(function(){
    myElements = myDocument.find("ul, ol");
    console.debug("success - iFrame", myFrame, "document", myDocument, "elements", myElements);
  });
});
$($("iframe").get(0).contentDocument).find("ol, ul")

共有1个答案

田骁
2023-03-14

Chrome有默认的安全限制,不允许您从硬盘访问其他窗口,即使它们在技术上是相同的来源。Chrome中有一个标志可以放松这种安全限制(我记得Windows上的命令行参数是这样的),尽管我不建议在快速测试之外使用该标志运行。有关命令行参数的信息,请参阅本文或这篇文章。

如果您从web服务器(即使是本地web服务器)而不是硬盘上运行文件,则不会出现此问题。

或者,您可以在其他没有限制的浏览器中进行测试

$(document).ready(function() {
    // get the iframe in my documnet
    var iframe = document.getElementById("testFrame");
    // get the window associated with that iframe
    var iWindow = iframe.contentWindow;

    // wait for the window to load before accessing the content
    iWindow.addEventListener("load", function() {
        // get the document from the window
        var doc = iframe.contentDocument || iframe.contentWindow.document;

        // find the target in the iframe content
        var target = doc.getElementById("target");
        target.innerHTML = "Found It!";
    });
});
$(document).ready(function() {
    $("#testFrame").load(function() {
        var doc = this.contentDocument || this.contentWindow.document;
        var target = doc.getElementById("target");
        target.innerHTML = "Found It!";
    });
});

在查看jQuery实现时,它真正要做的就是在iFrame本身上设置一个load事件侦听器。

如果您想了解调试/解决上面第一种方法时的细节:

在我试图解决这个问题时,我发现了一些关于iFrames的非常奇怪的东西(在Chrome中)。当您第一次在iframe的窗口中查看时,有一个文档,它说它的readystate===“complete”,所以您认为它已经完成了加载,但它在撒谎。通过URL加载到iframe中的文档中的实际文档和实际标记实际上还不存在。我通过在上放置一个自定义属性并检查该自定义属性来证明这一点。瞧吧。即使document.readystate===“complete”,该自定义属性也不在标记中。因此,我得出结论(至少在Chrome中),iFrame最初有一个虚设的空文档和正文,而这些文档和正文并不是将URL加载到iFrame中后的实际文档和正文。这使得检测它何时准备好的整个html" target="_blank">过程相当混乱(我花了几个小时来解决这个问题)。事实上,如果我设置了一个间隔计时器并轮询iWindow.document.body.getAttribute(“data-test”),我将看到它反复显示为undefined,最后它将显示正确的值,所有这些都与document.readystate===“complete”有关,这意味着它完全是在撒谎。

我想发生的事情是,iFrame从一个虚拟的空文档和正文开始,然后在内容开始加载后被替换。另一方面,iFrame窗口才是真正的窗口。因此,我找到的真正等待内容加载的唯一方法是监视iFrame窗口上的load事件,因为它似乎并不存在。如果您知道有一些特定的内容您正在等待,您也可以轮询,直到该内容可用。但是,即使这样,您也必须小心,因为不能太快地获取iframe.contentWindow.document,因为如果太快地获取它将是错误的文档。整件事都坏了。我无法从iFrame文档本身之外找到使用DomContentLoad的任何方法,因为您无法知道实际的document对象是否就位,可以将事件处理程序附加到它。所以...我接受了iFrame窗口上的load事件,它似乎确实有效。

如果您实际控制了iFrame中的代码,那么您可以更容易地从iFrame本身触发事件,方法是在iFrame代码中使用带有$(document).ready()的jQuery以及它自己的jQuery版本,或者从位于目标元素之后的脚本调用父窗口中的函数(从而确保目标元素已加载并就绪)。

进一步编辑

// This function ONLY works for iFrames of the same origin as their parent
function iFrameReady(iFrame, fn) {
    var timer;
    var fired = false;

    function ready() {
        if (!fired) {
            fired = true;
            clearTimeout(timer);
            fn.call(this);
        }
    }

    function readyState() {
        if (this.readyState === "complete") {
            ready.call(this);
        }
    }

    // cross platform event handler for compatibility with older IE versions
    function addEvent(elem, event, fn) {
        if (elem.addEventListener) {
            return elem.addEventListener(event, fn);
        } else {
            return elem.attachEvent("on" + event, function () {
                return fn.call(elem, window.event);
            });
        }
    }

    // use iFrame load as a backup - though the other events should occur first
    addEvent(iFrame, "load", function () {
        ready.call(iFrame.contentDocument || iFrame.contentWindow.document);
    });

    function checkLoaded() {
        var doc = iFrame.contentDocument || iFrame.contentWindow.document;
        // We can tell if there is a dummy document installed because the dummy document
        // will have an URL that starts with "about:".  The real document will not have that URL
        if (doc.URL.indexOf("about:") !== 0) {
            if (doc.readyState === "complete") {
                ready.call(doc);
            } else {
                // set event listener for DOMContentLoaded on the new document
                addEvent(doc, "DOMContentLoaded", ready);
                addEvent(doc, "readystatechange", readyState);
            }
        } else {
            // still same old original document, so keep looking for content or new document
            timer = setTimeout(checkLoaded, 1);
        }
    }
    checkLoaded();
}

这简单地叫成这样:

// call this when you know the iFrame has been loaded
// in jQuery, you would put this in a $(document).ready()
iFrameReady(document.getElementById("testFrame"), function() {
    var target = this.getElementById("target");
    target.innerHTML = "Found It!";
});
 类似资料:
  • 问题内容: 我有一个网页,其中iframe内有一个texarea。我需要从其子页面javascript读取此textarea的值。目前,通过在javascript中使用,我能够提取父页面中除iframe中的textarea之外的所有控件的值。 父页面中的框架ID和框架名称在运行时会更改,因此我们无法使用框架ID /框架名称作为参考。 问题答案: 如果您有HTML 和JavaScript 现在是对文

  • 问题内容: 我有一个网页,其中iframe内有一个文本区域。我需要使用JavaScript从其子页面读取此textarea的值。 目前,通过在JavaScript中使用,我能够在iframe中获取父页面中除textarea之外的所有控件的值。 任何人都可以给我任何指示以解决此问题吗? 问题答案: 如果您的iframe与父页面位于同一域中,则可以使用collection 访问元素。 如果您的ifra

  • 问题内容: 我试图在我的Angular项目中将keyup事件附加到指令。这是指令: 这是模板中的html: 我想在链接函数中访问并将keyup事件附加到它。我知道我可以用jQuery来获得它,但是那不是Angular的方法。从dom中获取该元素的正确方法是什么? 问题答案: 您可以使用Angular’ ‘ 方法轻松完成此操作: 实时示例:http://jsfiddle.net/cherniv/S7

  • 有没有可能用蟒蛇硒在阴影DOM中找到元素? 示例用例: 我用输入了这个

  • JAVASCRIPT文件 HTML文件 每当我试图执行上面的代码时,它会给我一个类似这样的错误 谁能告诉我怎样才能做到不出错。

  • 我可以通过以下方式访问dev中的用户名: 有没有其他方法可以通过webdriver访问ShadowDOM元素?