854

I would like to manipulate the HTML inside an iframe using jQuery.

I thought I'd be able to do this by setting the context of the jQuery function to be the document of the iframe, something like:

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

However this doesn't seem to work. A bit of inspection shows me that the variables in frames['nameOfMyIframe'] are undefined unless I wait a while for the iframe to load. However, when the iframe loads the variables are not accessible (I get permission denied-type errors).

Does anyone know of a work-around to this?

isherwood
  • 58,414
  • 16
  • 114
  • 157
rz.
  • 19,861
  • 10
  • 54
  • 47
  • 3
    What does the iFrame contain - is its src set to another domain? – James Dec 14 '08 at 00:32
  • if it is a other domain,is there still a way to access its content or register an event – adardesign Oct 01 '09 at 15:59
  • 37
    No, because that would be cross-site scripting, which is prohibited for security reasons. My solution was to use a proxy: feed the HTML in the IFRAME verbatim through my own site so it's no longer cross-site fro mthe browser's perspective. – reinierpost Apr 06 '10 at 08:10
  • 1
    It’s more cross-browser to use `.contentWindow.document` than `.document` on the `iframe` element. I’ll suggest the change above. – Alan H. Jul 08 '11 at 20:04
  • 1
    one way is chrome extensions – Muhammad Umer Apr 16 '13 at 13:14

15 Answers15

1031

If the <iframe> is from the same domain, the elements are easily accessible as

$("#iFrame").contents().find("#someDiv").removeClass("hidden");

More on the jQuery .contents() method and "how to access an iframe in jQuery"

Nate Anderson
  • 18,334
  • 18
  • 100
  • 135
Yasir Laghari
  • 10,714
  • 4
  • 19
  • 17
404

I think what you are doing is subject to the same origin policy. This should be the reason why you are getting permission denied type errors.

Onur Bebin
  • 4,303
  • 1
  • 17
  • 5
  • 12
    @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 Jun 27 '12 at 12:26
97

You could use .contents() method of jQuery.

The .contents() method can also be used to get the content document of an iframe, if the iframe is on the same domain as the main page.

$(document).ready(function(){
    $('#frameID').load(function(){
        $('#frameID').contents().find('body').html('Hey, i`ve changed content of <body>! Yay!!!');
    });
});
Narendra Jadhav
  • 10,052
  • 15
  • 33
  • 44
davryusha
  • 971
  • 6
  • 2
49

If the iframe src is from another domain you can still do it. You need to read the external page into PHP and echo it from your domain. Like this:

iframe_page.php

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

    $domain = file_get_contents($URL);

    echo $domain;
?>

Then something like this:

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>

The above is an example of how to edit an external page through an iframe without the access denied etc...

brasofilo
  • 25,496
  • 15
  • 91
  • 179
basysmith
  • 499
  • 4
  • 2
  • 13
    Of course, because your server is getting the remote page not the user's browser, no cookies will be sent to the remote page. YMMV. – Mark Fowler Nov 03 '10 at 07:10
  • 1
    @Mark: You can easily send cookies, posted data, HTTP headers and whatnot if you implement it with the curl extension. http://php.net/manual/en/book.curl.php – geon Feb 16 '11 at 14:56
  • 2
    @geon: but the browser won't send cookies for the foreign domain to your PHP script – ysth Feb 20 '11 at 20:46
  • @Mark: As far as the browser knows, no foreign domain is involved. The browser will talk to *your* server (including cookies, posted data and headers), which will in turn talk to the foreign domain. (Unless it uses tricks like cookies set/read by images, etc, which could be fixed by parsing the HTML and routing them through PHP as well.) – geon Feb 22 '11 at 10:05
  • @geon What Mark means is that the browser won't send any cookies intended for the foreign domain to your domain, (for example it wouldn't send cookies from `bank.com` to `mydomain.com`), and so the user will see the page as it would be without cookies (ie. logged out) – Justin Feb 24 '11 at 02:07
35

Use

iframe.contentWindow.document

instead of

iframe.contentDocument
RevanthKrishnaKumar V.
  • 1,855
  • 1
  • 21
  • 34
user
  • 359
  • 3
  • 2
  • 1
    Great answer. This works for iFrames in other domains. I tried this in the console on Safari and Firefox. – Aneil Mallavarapu Dec 27 '10 at 00:04
  • 13
    I believe it will only work for other domains if you're doing it from the console. From a script, access will not be allowed. – yincrash May 31 '11 at 15:58
  • 1
    This should be the accepted answer. Saved my life and works when the iframe is from a different domain; although as mentioned, only in the console. – silkfire Feb 26 '14 at 08:47
  • for me iframe.contentWindow.document does not always work. and when it doesn't I found that $(elem).contents().get(0) does – elewinso Nov 22 '18 at 13:02
  • you can choose the "root document" in the browser console. When you select "top" it will not work, as a cross-origin access. If you choose to access on behalf of iframe document, of course it will give you access. Just because you're not a browser, but the browser owner. – Sergei Kovalenko Sep 03 '19 at 07:34
33

I find this way cleaner:

var $iframe = $("#iframeID").contents();
$iframe.find('selector');
zupa
  • 12,809
  • 5
  • 40
  • 39
  • 1
    Saved my butt - I was going crazy, using "$("#iframe").contents().find()" wasn't working for some reason... same thing over two lines did. Dunno why, but it works. – neminem Jun 12 '19 at 21:26
27

You need to attach an event to an iframe's onload handler, and execute the js in there, so that you make sure the iframe has finished loading before accessing it.

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

The above will solve the 'not-yet-loaded' problem, but as regards the permissions, if you are loading a page in the iframe that is from a different domain, you won't be able to access it due to security restrictions.

Andreas Grech
  • 105,982
  • 98
  • 297
  • 360
  • this is a good idea, in fact I was trying it just as you answered. However, it doesn't work around the permission denied (it does address my having to wait before starting to access the iframe stuff) – rz. Dec 13 '08 at 08:16
  • actually... nevermind, it seems that the wait is still required even when doing this. – rz. Dec 13 '08 at 08:20
  • The ready function works. However, it seems that it doesn't wait for the contents of the iframe to finish loading -- only for the parent document even when invoked on the contents of the iframe itself. I imagine it is also because of the same origin policy. – rz. Dec 14 '08 at 23:15
  • 3
    A couple of notes on this code: you should use iframe.contentDocument instead of .document, and you should use .load() instead of .ready() to avoid the wait. (Not perfect, but better) – Rodrigo Queiro Jul 27 '09 at 22:55
22

You can use window.postMessage to call a function between page and his iframe (cross domain or not).

Documentation

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>
Sevle
  • 3,109
  • 2
  • 19
  • 31
B.Asselin
  • 978
  • 10
  • 19
4

I prefer to use other variant for accessing. From parent you can have a access to variable in child iframe. $ is a variable too and you can receive access to its just call window.iframe_id.$

For example, window.view.$('div').hide() - hide all divs in iframe with id 'view'

But, it doesn't work in FF. For better compatibility you should use

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

Evgeny Karpov
  • 2,386
  • 26
  • 16
2

I create a sample code . Now you can easily understand from different domain you can't access content of iframe .. Same domain we can access iframe content

I share you my code , Please run this code check the console . I print image src at console. There are four iframe , two iframe coming from same domain & other two from other domain(third party) .You can see two image src( https://www.google.com/logos/doodles/2015/googles-new-logo-5078286822539264.3-hp2x.gif

and

https://www.google.com/logos/doodles/2015/arbor-day-2015-brazil-5154560611975168-hp2x.gif ) at console and also can see two permission error( 2 Error: Permission denied to access property 'document'

...irstChild)},contents:function(a){return m.nodeName(a,"iframe")?a.contentDocument...

) which is coming from third party iframe.

<body id="page-top" data-spy="scroll" data-target=".navbar-fixed-top">
<p>iframe from same domain</p>
  <iframe frameborder="0" scrolling="no" width="500" height="500"
   src="iframe.html" name="imgbox" class="iView">

</iframe>
<p>iframe from same domain</p>
<iframe frameborder="0" scrolling="no" width="500" height="500"
   src="iframe2.html" name="imgbox" class="iView1">

</iframe>
<p>iframe from different  domain</p>
 <iframe frameborder="0" scrolling="no" width="500" height="500"
   src="https://www.google.com/logos/doodles/2015/googles-new-logo-5078286822539264.3-hp2x.gif" name="imgbox" class="iView2">

</iframe>

<p>iframe from different  domain</p>
 <iframe frameborder="0" scrolling="no" width="500" height="500"
   src="http://d1rmo5dfr7fx8e.cloudfront.net/" name="imgbox" class="iView3">

</iframe>

<script type='text/javascript'>


$(document).ready(function(){
    setTimeout(function(){


        var src = $('.iView').contents().find(".shrinkToFit").attr('src');
    console.log(src);
         }, 2000);


    setTimeout(function(){


        var src = $('.iView1').contents().find(".shrinkToFit").attr('src');
    console.log(src);
         }, 3000);


    setTimeout(function(){


        var src = $('.iView2').contents().find(".shrinkToFit").attr('src');
    console.log(src);
         }, 3000);

         setTimeout(function(){


        var src = $('.iView3').contents().find("img").attr('src');
    console.log(src);
         }, 3000);


    })


</script>
</body>
Zisu
  • 497
  • 2
  • 6
  • 25
2

Have you tried the classic, waiting for the load to complete using jQuery's builtin ready function?

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

K

Khb
  • 1,423
  • 9
  • 9
  • 2
    Yes. The ready function starts executing when the main frame is loaded -- not when the iframe is loaded. The wait seems to be a small part of the problem, though. I think it has to do with cross-domain security. – rz. Dec 13 '08 at 08:07
2

If the code below doesn't work

$("#iFrame").contents().find("#someDiv").removeClass("hidden");

Here is the reliable way to make it work:

$(document).ready(function(){ 
  setTimeout(
    function () {
      $("#iFrame").contents().find("#someDiv").removeClass("hidden");
    },
    300
  );
});

This way the script will run after 300 miliseconds, so it'll get enough time for iFrame to be loaded and then the code will come into action. At times the iFrame doesn't load and script tries to execute before it. 300ms can be tweaked to anything else as per your needs.

Imran Zahoor
  • 2,521
  • 1
  • 28
  • 38
1

For even more robustness:

function getIframeWindow(iframe_object) {
  var doc;

  if (iframe_object.contentWindow) {
    return iframe_object.contentWindow;
  }

  if (iframe_object.window) {
    return iframe_object.window;
  } 

  if (!doc && iframe_object.contentDocument) {
    doc = iframe_object.contentDocument;
  } 

  if (!doc && iframe_object.document) {
    doc = iframe_object.document;
  }

  if (doc && doc.defaultView) {
   return doc.defaultView;
  }

  if (doc && doc.parentWindow) {
    return doc.parentWindow;
  }

  return undefined;
}

and

...
var frame_win = getIframeWindow( frames['nameOfMyIframe'] );

if (frame_win) {
  $(frame_win.contentDocument || frame_win.document).find('some selector').doStuff();
  ...
}
...
Dominique Fortin
  • 2,212
  • 15
  • 20
1

I ended up here looking for getting the content of an iframe without jquery, so for anyone else looking for that, it is just this:

document.querySelector('iframe[name=iframename]').contentDocument
Jeff Davis
  • 4,736
  • 4
  • 38
  • 44
1

This solution works same as iFrame. I have created a PHP script that can get all the contents from the other website, and most important part is you can easily apply your custom jQuery to that external content. Please refer to the following script that can get all the contents from the other website and then you can apply your cusom jQuery/JS as well. This content can be used anywhere, inside any element or any page.

<div id='myframe'>

  <?php 
   /* 
    Use below function to display final HTML inside this div
   */

   //Display Frame
   echo displayFrame(); 
  ?>

</div>

<?php

/* 
  Function to display frame from another domain 
*/

function displayFrame()
{
  $webUrl = 'http://[external-web-domain.com]/';

  //Get HTML from the URL
  $content = file_get_contents($webUrl);

  //Add custom JS to returned HTML content
  $customJS = "
  <script>

      /* Here I am writing a sample jQuery to hide the navigation menu
         You can write your own jQuery for this content
      */
    //Hide Navigation bar
    jQuery(\".navbar.navbar-default\").hide();

  </script>";

  //Append Custom JS with HTML
  $html = $content . $customJS;

  //Return customized HTML
  return $html;
}
Ahsan Horani
  • 239
  • 2
  • 13
  • How would this work? If you're appending the $customJS string to $content, aren't you going to have the script element AFTER the closing html tag (of the external domain)? – ultrageek Aug 09 '20 at 06:12