如何使用JavaScript/jQuery访问iframe的内容?

857

我想使用jQuery操纵iframe内部的HTML。

我以为可以通过将jQuery函数的上下文设置为iframe的文档来实现,类似于:

$(function(){ //document ready
    $('some selector', frames['nameOfMyIframe'].document).doStuff()
});

然而这似乎行不通。稍微检查一下,我发现要等一段时间iframe加载后frames['nameOfMyIframe']中的变量才会变成 undefined。但是,当iframe加载时,变量却无法访问(我会收到类似于permission denied的错误)。

有没有人知道解决这个问题的方法?


3
iFrame 包含什么内容 - 它的 src 是否设置为另一个域名? - James
如果是其他领域,是否仍有办法访问其内容或注册事件? - adardesign
38
不行,因为这将涉及跨站脚本攻击,出于安全原因被禁止。 我的解决方案是使用代理:将IFRAME中的HTML程序透过我的网站原封不动地传送,这样从浏览器的角度来看就不再是跨站了。 - reinierpost
1
最好使用 .contentWindow.document 而不是在 iframe 元素上使用 .document,这样可以更好地跨浏览器兼容。我建议以上更改。 - Alan H.
1
一种方法是使用Chrome扩展程序。 - Muhammad Umer
15个回答

1032

169
了解同源策略是很好的,但这个回答做到了OP想要的。为什么其他回答被选为正确答案? - Jason Swett
相关的 https://dev59.com/7XI-5IYBdhLWcg3wj5BG#1796621 - Nate Anderson

404

我认为你所做的事情受到了同源策略的限制。这可能是你收到权限被拒绝错误的原因。


12
@Tracker1:你能否建议一个框架/ API/设计模式来实现这个代理解决方案?有没有示例或教程的链接?我已经尝试搜索,但找不到任何相关内容。 (翻译):@Tracker1:Can you suggest any framework/api/design pattern for implementing this proxy solution. Any links to example or tutorial etc? I have tried to search but couldn't find any. - Umer Hayat

98

你可以使用 jQuery 的.contents()方法。

如果iframe和主页面在同一个域名下,.contents()方法也可以用于获取iframe的内容文档。

$(document).ready(function(){
    $('#frameID').load(function(){
        $('#frameID').contents().find('body').html('Hey, i`ve changed content of <body>! Yay!!!');
    });
});

48

如果 iframe 的 src 是来自另一个域名,你仍然可以实现它。需要将外部页面读取到 PHP 中,并从您的域中进行 echo。像这样:

iframe_page.php

<?php
    $URL = "http://external.com";

    $domain = file_get_contents($URL);

    echo $domain;
?>

然后就像这样:

display_page.html

<html>
<head>
  <title>Test</title>
 </head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.1/jquery.min.js"></script>

<script>

$(document).ready(function(){   
    cleanit = setInterval ( "cleaning()", 500 );
});

function cleaning(){
    if($('#frametest').contents().find('.selector').html() == "somthing"){
        clearInterval(cleanit);
        $('#selector').contents().find('.Link').html('ideate tech');
    }
}

</script>

<body>
<iframe name="frametest" id="frametest" src="http://yourdomain.com/iframe_page.php" ></iframe>
</body>
</html>
上面是一个通过iframe编辑外部页面且不会出现访问被拒绝等问题的示例。

13
当然,因为您的服务器获取远程页面而不是用户的浏览器,所以不会向远程页面发送任何cookie。可能因情况而异。 - Mark Fowler
1
@Mark:如果你使用curl扩展实现,你可以轻松地发送cookies、发布数据、HTTP头等等。http://php.net/manual/en/book.curl.php - geon
2
@geon:但是浏览器不会将外部域的cookie发送到您的PHP脚本。 - ysth
@Mark:就浏览器而言,它并不知道涉及到任何外部域名。浏览器会与您的服务器进行通信(包括cookies、已发布的数据和标头),然后服务器将再与外部域名进行通信。(除非它使用像通过图像设置/读取的cookies等技巧,这可以通过解析HTML并将它们通过PHP路由来修复。) - geon
@geon,Mark 的意思是浏览器不会将任何针对外部域的 cookies 发送到您的域名(例如它不会将 bank.com 的 cookies 发送到 mydomain.com),因此用户将看到没有cookies的页面(即未登录状态)。 - Justin

35

使用

iframe.contentWindow.document

而不是

iframe.contentDocument

1
很棒的答案。这适用于其他域中的iFrame。我在Safari和Firefox的控制台上尝试过这个。 - Aneil Mallavarapu
14
我相信只有在从控制台执行时,它才能适用于其他域。从脚本中,访问将不被允许。 - yincrash
1
这应该是被接受的答案。在 iframe 来自不同域时,它拯救了我的生命并且可以工作;尽管如上所述,只能在控制台中使用。 - silkfire
对我而言,iframe.contentWindow.document并不总是有效。当它无效时,我发现$(elem).contents().get(0)可以使用。 - elewinso
你可以在浏览器控制台中选择“根文档”。如果你选择“顶部”,它将无法工作,因为存在跨域访问。如果你选择代表iframe文档进行访问,当然它会给你访问权限。这是因为你不是浏览器,而是浏览器的所有者。 - Sergei Kovalenko

33

我认为以下方法更简洁:

var $iframe = $("#iframeID").contents();
$iframe.find('selector');

1
救了我的命 - 我快疯了,使用 "$("#iframe").contents().find()" 不知道为什么不起作用... 但是同样的代码分成两行就可以了。不知道为什么,但它有效。 - neminem

27

您需要将事件附加到iframe的onload处理程序,并在其中执行JS,以确保在访问它之前iframe已完成加载。

$().ready(function () {
    $("#iframeID").ready(function () { //The function below executes once the iframe has finished loading
        $('some selector', frames['nameOfMyIframe'].document).doStuff();
    });
};

上述方法可以解决“尚未加载”的问题,但是就权限而言,如果您在iframe中加载的页面来自不同的域,由于安全限制,您将无法访问它。


这是一个好主意,事实上我正尝试着做你回答的那样。然而,它无法解决权限被拒绝的问题(它确实解决了我必须等待才能开始访问iframe内容的问题)。 - rz.
实际上...算了,似乎即使这样做,仍然需要等待。 - rz.
准备函数可以工作。但是,似乎它不等待iframe的内容完成加载 - 仅对父文档进行调用,即使在iframe本身的内容上也是如此。我想这也是因为同源策略。 - rz.
3
关于这段代码的一些注释:你应该使用iframe.contentDocument而不是.document,并且应该使用.load()而不是.ready()以避免等待。(不完美,但更好) - Rodrigo Queiro

22
你可以使用 window.postMessage 在页面和它的 iframe 之间调用函数(跨域或不跨域)。 文档 page.html
<!DOCTYPE html>
<html>
<head>
    <title>Page with an iframe</title>
    <meta charset="UTF-8" />
    <script src="http://code.jquery.com/jquery-1.10.2.min.js"></script>
    <script>
    var Page = {
        id:'page',
        variable:'This is the page.'
    };

    $(window).on('message', function(e) {
        var event = e.originalEvent;
        if(window.console) {
            console.log(event);
        }
        alert(event.origin + '\n' + event.data);
    });
    function iframeReady(iframe) {
        if(iframe.contentWindow.postMessage) {
            iframe.contentWindow.postMessage('Hello ' + Page.id, '*');
        }
    }
    </script>
</head>
<body>
    <h1>Page with an iframe</h1>
    <iframe src="iframe.html" onload="iframeReady(this);"></iframe>
</body>
</html>

iframe.html

<!DOCTYPE html>
<html>
<head>
    <title>iframe</title>
    <meta charset="UTF-8" />
    <script src="http://code.jquery.com/jquery-1.10.2.min.js"></script>
    <script>
    var Page = {
        id:'iframe',
        variable:'The iframe.'
    };

    $(window).on('message', function(e) {
        var event = e.originalEvent;
        if(window.console) {
            console.log(event);
        }
        alert(event.origin + '\n' + event.data);
    });
    $(window).on('load', function() {
        if(window.parent.postMessage) {
            window.parent.postMessage('Hello ' + Page.id, '*');
        }
    });
    </script>
</head>
<body>
    <h1>iframe</h1>
    <p>It's the iframe.</p>
</body>
</html>

4

我更喜欢使用其他变量来进行访问。从父级中,你可以访问子 iframe 中的变量。 $ 也是一个变量,你可以通过调用 window.iframe_id.$ 来访问它。

例如,window.view.$('div').hide() - 隐藏 id 为 'view' 的 iframe 中所有的 div 元素。

但是,在 Firefox 浏览器中这种方法不起作用。为了更好的兼容性,你应该使用以下方式:

$('#iframe_id')[0].contentWindow.$


2
如果下面的代码不起作用。
$("#iFrame").contents().find("#someDiv").removeClass("hidden");

这里是使其正常工作的可靠方法:
$(document).ready(function(){ 
  setTimeout(
    function () {
      $("#iFrame").contents().find("#someDiv").removeClass("hidden");
    },
    300
  );
});

这样脚本将在300毫秒后运行,因此它将有足够的时间来加载,然后代码才会开始执行。有时iFrame没有加载完,脚本尝试在加载之前执行。300毫秒可以根据您的需求进行调整。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接