jQuery / JavaScript: dostęp do zawartości elementu iframe

791

Chciałbym manipulować HTML wewnątrz elementu iframe za pomocą jQuery.

Myślałem, że będę w stanie to zrobić, ustawiając kontekst funkcji jQuery jako dokument iframe, coś w stylu:

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

Jednak to nie działa. Trochę inspekcji pokazuje mi, że zmienne frames['nameOfMyIframe']są, undefinedchyba że poczekam chwilę na załadowanie iframe. Jednak gdy ładuje się element iframe, zmienne nie są dostępne (dostaję permission deniedbłędy typu).

Czy ktoś wie o obejściu tego?

rz.
źródło
3
Co zawiera iFrame - czy src jest ustawiony na inną domenę?
James
jeśli jest to inna domena, czy istnieje jeszcze sposób na uzyskanie dostępu do jej treści lub zarejestrowanie wydarzenia
adardesign
34
Nie, ponieważ byłoby to skrypty między witrynami, co jest zabronione ze względów bezpieczeństwa. Moim rozwiązaniem było użycie proxy: podaj HTML w IFRAME dosłownie przez moją własną stronę, aby nie była już przekrojowa z perspektywy przeglądarki.
reinierpost
Jest bardziej do użytku w różnych przeglądarkach .contentWindow.documentniż .documentna iframeelemencie. Zasugeruję powyższą zmianę.
Alan H.,
1
jednym ze sposobów są rozszerzenia chromowane
Muhammad Umer

Odpowiedzi:

384

Myślę, że to, co robisz, podlega tej samej polityce pochodzenia . To powinien być powód błędów typu „odmowa uprawnień” .

Onur Bebin
źródło
6
@Pacerier Najlepszym rozwiązaniem jest przesłanie zawartości iframe na twoją stronę, jeśli możesz ...
Tracker1
10
@ Tracker1: Czy możesz zasugerować dowolny wzorzec framework / api / design dla wdrożenia tego rozwiązania proxy? Wszelkie linki do przykładu lub samouczka itp.? Próbowałem wyszukać, ale nie znalazłem.
Umer Hayat
3
W tym przypadku ma na myśli użycie serwera http, który obsługuje stronę Twojej domeny jako serwera proxy - zażądaj treści z witryny innej firmy i przekaż ją dalej w odpowiedzi http do klienta. Jak zapewne można się domyślić, szybko wpływa to na szybkość reakcji witryny, ponieważ wcześniej równoległe żądania są wykonywane szeregowo z serwerem jako potencjalne wąskie gardło.
rutherford
1
@Pacerier Potencjalnie dowolna z metod w przyjętej odpowiedzi tutaj: stackoverflow.com/questions/3076414/... lub inne (np. Używanie własnej domeny jako serwera proxy), w zależności od tego, co chcesz osiągnąć.
Mark Amery
4
Dla przypomnienia, właśnie skonfigurowałem coś podobnego: jeśli nie chcesz, aby wynik był niesamowicie wolny, po prostu skonfiguruj serwer WWW, aby bezpośrednio przekierowywał żądania zasobów (CSS / JS / images) do oryginalnej witryny, więc Twój serwer proxy zarządza tylko żądaniami HTML. W tej chwili przeglądam odległą stronę pod localhost i jest ona absolutnie przezroczysta :)
neemzy
985

Jeśli <iframe>jest z tej samej domeny, elementy są łatwo dostępne jako

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

Odniesienie

Yasir Laghari
źródło
157
Wiedza na temat tej samej polityki pochodzenia jest świetna, ale jest to odpowiedź, która robi to, czego chciał PO. Dlaczego drugą odpowiedź wybrano jako właściwą?
Jason Swett
2
Musisz użyć proxy, aby uzyskać HTML w swoim pochodzeniu. Na przykład johnchapman.name/…
mhenry1384
7
@JasonSwett, zgaduję, że problemem OP jest rzeczywiście problem same origin policy, w którym to przypadku nie jest dla niego rozwiązaniem.
ANeves
3
@fizzbuzz Następnie otrzymujesz IFrame w taki sam sposób, jak każdą inną zawartość bez identyfikatora za pomocą jQuery: wybierasz odpowiedni znacznik <IFrame> za pomocą CSS, używając nazw klas i wartości atrybutów, aby zawęzić go w razie potrzeby.
Auspex
2
Dla iframe nie ma metody zwanej zawartością .
Renan
88

Możesz użyć .contents()metody jQuery.

.contents()Metoda może być również używana do pobierania zawartości dokumentu z iframe, gdy iframe jest w tej samej domenie co na stronie głównej.

$(document).ready(function(){
    $('#frameID').load(function(){
        $('#frameID').contents().find('body').html('Hey, i`ve changed content of <body>! Yay!!!');
    });
});
davryusha
źródło
2
muszę uzyskać zawartość ramki i, aby nie ustawiać ciała ..... Po wykonaniu powyższej czynności nie działa zawsze zwróć puste Ciało
George Hanna
3
@DarkoZ Um, właściwie ta odpowiedź była pierwsza, więc jeśli już, drugą odpowiedzią była kopia
michaelpri
@DarkoZ: pozostawienie oryginalnego komentarza nie sprawiło, żebym cię zawstydził, ale zmarnowałem około 30 sekund, próbując zrozumieć problem, który nie jest problemem :) Więc usunięcie całej dyskusji na temat plagiującej odpowiedzi może być najlepsze.
Dan Dascalescu
komentarze zostały usunięte, aby uniknąć pomyłek. przeprosiny.
Darko Z
43

Jeśli element iframe src pochodzi z innej domeny, nadal możesz to zrobić. Musisz przeczytać zewnętrzną stronę w PHP i powtórzyć ją ze swojej domeny. Lubię to:

iframe_page.php

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

    $domain = file_get_contents($URL);

    echo $domain;
?>

Następnie coś takiego:

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>

Powyższy przykład jest przykładem edycji strony zewnętrznej za pomocą elementu iframe bez odmowy dostępu itp.

basysmith
źródło
12
Oczywiście, ponieważ twój serwer otrzymuje zdalną stronę, a nie przeglądarkę użytkownika, żadne ciasteczka nie będą wysyłane na zdalną stronę. YMMV.
Mark Fowler
1
@Mark: Możesz łatwo wysyłać pliki cookie, opublikowane dane, nagłówki HTTP i co więcej, jeśli zaimplementujesz je z rozszerzeniem curl. php.net/manual/en/book.curl.php
Geon
2
@geon: ale przeglądarka nie wysyła plików cookie dla obcej domeny do skryptu PHP
ysth
6
@basysmith Uważaj: istnieje powód, dla którego same origin policyistnieje, a propozycja tej odpowiedzi nie jest na to odporna.
ANeves
1
czy jest to w jakikolwiek sposób nielegalne?
Tallboy
32

Uważam, że ten sposób jest czystszy:

var $iframe = $("#iframeID").contents();
$iframe.find('selector');
zupa
źródło
Zapisałem tyłek - zwariowałem, używając „$ („ # iframe ”). Content (). Find ()” z jakiegoś powodu nie działało… to samo w dwóch liniach. Nie wiem dlaczego, ale działa.
neminem
30

Posługiwać się

iframe.contentWindow.document

zamiast

iframe.contentDocument
użytkownik
źródło
Świetna odpowiedź. Działa to w przypadku ramek iFrame w innych domenach. Próbowałem tego w konsoli na Safari i Firefox.
Aneil Mallavarapu
12
Wierzę, że będzie działać tylko w przypadku innych domen, jeśli robisz to z konsoli. Ze skryptu dostęp nie będzie dozwolony.
yincrash
To powinna być zaakceptowana odpowiedź. Uratowałem moje życie i działa, gdy iframe pochodzi z innej domeny; chociaż jak wspomniano, tylko w konsoli.
Silkfire
dla mnie iframe.contentWindow.document nie zawsze działa. a kiedy nie, to okazało się, że $ (elem) .contents (). get (0) robi
elewinso
możesz wybrać „dokument główny” w konsoli przeglądarki. Po wybraniu opcji „góra” nie będzie działać, jako dostęp do wielu źródeł. Jeśli zdecydujesz się na dostęp w imieniu dokumentu iframe, oczywiście da ci on dostęp. Tylko dlatego, że nie jesteś przeglądarką, ale właścicielem przeglądarki.
Sergei Kovalenko
26

Musisz dołączyć zdarzenie do modułu obsługi obciążenia iframe i wykonać tam plik js, aby upewnić się, że ramka iframe zakończyła ładowanie przed uzyskaniem do niej dostępu.

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

Powyższe rozwiąże problem „jeszcze nie załadowano”, ale jeśli chodzi o uprawnienia, jeśli ładujesz stronę w ramce iframe z innej domeny, nie będziesz w stanie uzyskać do niej dostępu z powodu ograniczeń bezpieczeństwa.

Andreas Grech
źródło
to dobry pomysł, w rzeczywistości próbowałem go tak, jak odpowiedziałeś. Nie działa to jednak wokół odmowy dostępu (rozwiązuje problem konieczności oczekiwania przed rozpoczęciem dostępu do elementów iframe)
rz.
właściwie ... nieważne, wydaje się, że czekanie jest nadal wymagane, nawet jeśli to robisz.
rz.
Funkcja gotowości działa. Wydaje się jednak, że nie czeka on na zakończenie ładowania zawartości iframe - tylko dla dokumentu nadrzędnego, nawet jeśli jest wywoływany na zawartości samego iframe. Wyobrażam sobie, że dzieje się tak również z powodu tej samej polityki pochodzenia.
rz.
3
Kilka uwag na temat tego kodu: powinieneś użyć iframe.contentDocument zamiast .document i powinieneś użyć .load () zamiast .ready (), aby uniknąć oczekiwania. (Nie idealnie, ale lepiej)
Rodrigo Queiro
21

Za pomocą window.postMessage można wywołać funkcję między stroną a jego ramką iframe (między domenami lub nie).

Dokumentacja

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>
B.Asselin
źródło
4

Wolę użyć innego wariantu dostępu. Od rodzica możesz mieć dostęp do zmiennej w potomnym iframe. $jest również zmienną i możesz uzyskać dostęp do jej zwykłego połączenia window.iframe_id.$

Na przykład window.view.$('div').hide()- ukryj wszystkie elementy div w ramce iframe o identyfikatorze „view”

Ale to nie działa w FF. Dla lepszej kompatybilności powinieneś użyć

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

Jewgienij Karpow
źródło
2

Czy wypróbowałeś już wersję klasyczną, czekając na zakończenie ładowania za pomocą wbudowanej funkcji gotowości jQuery?

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

K.

Khb
źródło
2
Tak. Funkcja gotowości zaczyna działać po załadowaniu głównej ramki - a nie po załadowaniu ramki iframe. Oczekiwanie wydaje się jednak niewielką częścią problemu. Myślę, że ma to związek z bezpieczeństwem między domenami.
rz.
2

Tworzę przykładowy kod. Teraz możesz łatwo zrozumieć z innej domeny, że nie masz dostępu do treści iframe. Z tej samej domeny możemy uzyskać dostęp do treści iframe

Udostępniam ci mój kod. Uruchom ten kod, sprawdź konsolę. Drukuję obraz src na konsoli. Istnieją cztery elementy iframe, dwa elementy iframe pochodzące z tej samej domeny i pozostałe dwa elementy z innej domeny (podmiot zewnętrzny). Możesz zobaczyć dwa obrazy src ( https://www.google.com/logos/doodles/2015/googles-new-logo -5078286822539264.3-hp2x.gif

i

https://www.google.com/logos/doodles/2015/arbor-day-2015-brazil-5154560611975168-hp2x.gif ) na konsoli, a także można zobaczyć błąd dwóch uprawnień (2 Błąd: odmowa dostępu do dokumentu właściwości „

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

), który pochodzi z zewnętrznego elementu 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
źródło
1

Skończyłem tutaj, szukając zawartości iframe bez jquery, więc dla każdego, kto tego szuka, jest to po prostu:

document.querySelector('iframe[name=iframename]').contentDocument
Jeff Davis
źródło
1

To rozwiązanie działa tak samo jak iFrame. Stworzyłem skrypt PHP, który może pobrać całą zawartość z drugiej strony, a najważniejsze jest to, że możesz łatwo zastosować niestandardowe jQuery do tej zawartości zewnętrznej. Zapoznaj się z poniższym skryptem, który może pobrać całą zawartość z innej witryny, a następnie możesz zastosować również swój jusquery / JS. Tej zawartości można używać w dowolnym miejscu, w dowolnym elemencie lub na dowolnej stronie.

<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
źródło
0

Dla jeszcze większej niezawodności:

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

i

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

if (frame_win) {
  $(frame_win.contentDocument || frame_win.document).find('some selector').doStuff();
  ...
}
...
Dominique Fortin
źródło