Jak sprawdzić, czy iframe jest załadowany lub ma zawartość?

84

Mam iframe z id = "myIframe" i tutaj mój kod do załadowania jego zawartości:

$('#myIframe').attr("src", "my_url");

Problem polega na tym, że czasami ładowanie trwa zbyt długo, a czasami ładuje się bardzo szybko. Muszę więc użyć funkcji „setTimeout”:

setTimeout(function(){
   if (//something shows iframe is loaded or has content)
   {
       //my code
   }
   else
   {
       $('#myIframe').attr("src",""); //stop loading content
   }
},5000);

Chcę tylko wiedzieć, jak sprawdzić, czy element iFrame jest załadowany lub zawiera zawartość. Używanie iframe.contents().find()nie zadziała. Nie mogę użyć iframe.load(function(){}).

nowicjusz29
źródło
Nie chcesz więc, aby element iframe cokolwiek ładował, jeśli ładowanie trwa dłużej niż 5 sekund?
skimberk1
Dzięki za pomoc :) . Tak, nie chcę, aby element iframe wczytywał cokolwiek, jeśli ładowanie trwa dłużej niż 5 sekund. Używanie „.ready ()”, jak pokazano poniżej, nie działa.
nowicjusz 29
Zobacz moją odpowiedź tutaj stackoverflow.com/questions/17158932/…
koleś
Sprawdź tę odpowiedź - forums.tumult.com/t/detect-iframe-loaded/7588
Varshaan

Odpowiedzi:

89

Spróbuj tego.

<script>
function checkIframeLoaded() {
    // Get a handle to the iframe element
    var iframe = document.getElementById('i_frame');
    var iframeDoc = iframe.contentDocument || iframe.contentWindow.document;

    // Check if loading is complete
    if (  iframeDoc.readyState  == 'complete' ) {
        //iframe.contentWindow.alert("Hello");
        iframe.contentWindow.onload = function(){
            alert("I am loaded");
        };
        // The loading is complete, call the function we want executed once the iframe is loaded
        afterLoading();
        return;
    } 

    // If we are here, it is not loaded. Set things up so we check   the status again in 100 milliseconds
    window.setTimeout(checkIframeLoaded, 100);
}

function afterLoading(){
    alert("I am here");
}
</script>

<body onload="checkIframeLoaded();"> 
Biranchi
źródło
21
Wady za przekazanie ciągu do setTimeout (). Czystsze jest przekazanie odwołania do funkcji:setTimeout(checkIframeLoaded, 100);
Jesse Hallett,
1
ten prowadzi do „Niebezpiecznej próby dostępu do ramki z adresem URL url1 z ramki z adresem url2 przez niebezpieczny kod JavaScript. Domeny, protokoły i porty muszą być zgodne”.
mohamed-ibrahim,
To było najlepsze rozwiązanie tego, co próbowałem osiągnąć. Używam AngularJS wewnątrz iFrame i używam ng-include, co opóźnia ładowanie dołączonych plików. Próbowałem wydrukować zawartość iframe po zdarzeniu onload, ale uruchamiał się zbyt szybko. Sprawdzanie readyState === "complete"zadziałało, ponieważ nie zmienia się, dopóki wszystko nie zostanie załadowane.
Jeffrey A. Gochin
3
Muszę powiedzieć, że było to jedyne rozwiązanie, które również mi się sprawdziło. Po prostu nie możesz ufać zdarzeniu ładowania w elemencie iframe, ponieważ w większości przypadków jest ono uruchamiane natychmiastowo w przeglądarkach IE11, Chrome i Edge z iframe.contentDocument.location.hrefwyświetlaniem jako about:blank. Wydaje się, że jest to prawdą, nawet jeśli srcfor the iframejest określone bezpośrednio w kodzie HTML.
rmalayter
24
To nie zadziała, jeśli ramka iframe pochodzi z innej domeny:VM29320:1 Uncaught DOMException: Blocked a frame with origin "http://localhost:9002" from accessing a cross-origin frame.
Augustin Riedinger
46

uprzejmie użyj:

$('#myIframe').on('load', function(){
    //your code (will be called once iframe is done loading)
});

Zaktualizowałem moją odpowiedź, gdy zmieniły się standardy.

pratikabu
źródło
4
To podejście w ogóle nie działa, ponieważ zdarzenie jest uruchamiane natychmiast, ponieważ iframezazwyczaj zaczyna się od src="about:blank", nawet jeśli srcjest on określony bezpośrednio w kodzie HTML lub przed dodaniem iframewęzła do DOM. Przetestowałem to, sprawdzając, czy iframecontentDocument.location.hrefw ciasnej pętli w IE11, Chrome i Edge. Nie działa również, jeśli zawartość w ramce przekierowuje za pomocą metatagów lub kodu JavaScript.
rmalayter
17

Miałem ten sam problem i dodałem do tego, musiałem sprawdzić, czy ramka iframe jest ładowana niezależnie od zasad międzydomenowych. Pracowałem nad rozszerzeniem do Chrome, które wstawia pewien skrypt na stronę internetową i wyświetla część treści ze strony nadrzędnej w ramce iframe. Próbowałem zastosować podejście i to zadziałało idealnie dla mnie.
PS: W moim przypadku mam kontrolę nad zawartością elementu iframe, ale nie w witrynie nadrzędnej. (Element iframe jest hostowany na moim własnym serwerze)

Po pierwsze:
Utwórz element iframe z data-atrybutem w nim takim jak (ta część była w skrypcie wstrzykniętym w moim przypadku)
<iframe id="myiframe" src="http://anyurl.com" data-isloaded="0"></iframe>

Teraz w kodzie iframe użyj:

var sourceURL = document.referrer;
window.parent.postMessage('1',sourceURL);



Wróćmy teraz do wstrzykniętego skryptu, jak w moim przypadku:

setTimeout(function(){
  var myIframe = document.getElementById('myiframe');
  var isLoaded = myIframe.prop('data-isloaded');
  if(isLoaded != '1')
  {
    console.log('iframe failed to load');
  } else {
    console.log('iframe loaded');
  }
},3000);


i,

window.addEventListener("message", receiveMessage, false);
function receiveMessage(event)
{
    if(event.origin !== 'https://someWebsite.com') //check origin of message for security reasons
    {
        console.log('URL issues');
        return;
    }
    else {
        var myMsg = event.data;
        if(myMsg == '1'){
            //8-12-18 changed from 'data-isload' to 'data-isloaded
            $("#myiframe").prop('data-isloaded', '1');
        }
    }           
}



Może nie do końca odpowiada na to pytanie, ale rzeczywiście jest to możliwy przypadek tego pytania, które rozwiązałem tą metodą.

Tushar Shukla
źródło
W twoim kodzie jest kilka błędów, takich jak document.getElement-> s <-ById, ale cały pomysł jest przydatny. To działa. Dzięki)
JohnK
@JohnK Dzięki za wskazanie tego, naprawiłem to. Cieszę się, że jest to przydatne dla Ciebie.
Tushar Shukla
12

Najłatwiejsza opcja:

<script type="text/javascript">
  function frameload(){
   alert("iframe loaded")
  }
</script>

<iframe onload="frameload()" src=...>
Orane
źródło
10
nie jest to solidne rozwiązanie, ponieważ daje alert, nawet jeśli adres URL jest uszkodzony.
bboy,
7

Możesz użyć loadzdarzenia iframe, aby odpowiedzieć na załadowanie elementu iframe.

document.querySelector('iframe').onload = function(){
    console.log('iframe loaded');
};

Nie powie Ci, czy załadowano poprawną zawartość: Aby to sprawdzić, możesz sprawdzić plik contentDocument.

document.querySelector('iframe').onload = function(){
    var iframeBody = this.contentDocument.body;
    console.log('iframe loaded, body is: ', body);
};

Zaznaczenie opcji contentDocumentnie zadziała, jeśli element iframe srcwskazuje inną domenę, z której działa Twój kod.

Max Heiber
źródło
2
Rozważ edycję swojego posta, aby dodać więcej wyjaśnień na temat tego, co robi Twój kod i dlaczego rozwiąże problem. Odpowiedź, która w większości zawiera tylko kod (nawet jeśli działa), zazwyczaj nie pomoże OP w zrozumieniu problemu.
SuperBiasedMan
5

Nie jestem pewien, czy możesz wykryć, czy jest załadowany, czy nie, ale możesz uruchomić zdarzenie po zakończeniu ładowania:

$(function(){
    $('#myIframe').ready(function(){
        //your code (will be called once iframe is done loading)
    });
});

EDYCJA: Jak zauważył Jesse Hallett, będzie to zawsze uruchamiane po iframezaładowaniu, nawet jeśli już zostało. Zasadniczo więc, jeśli plik iframezostał już załadowany, wywołanie zwrotne zostanie wykonane natychmiast.

skimberk1
źródło
2
Zaletą tej metody jest to, że zdarzenie „ready” jQuery zostanie uruchomione, nawet jeśli zostanie ono powiązane po całkowitym załadowaniu elementu iframe. Jest to więc dobry sposób, aby sprawdzić, czy element iframe ma już treść, lub powiadomić Cię, gdy otrzyma zawartość.
Jesse Hallett
1
Wygląda na to, że uruchamia się tylko raz dla elementu iframe. Jeśli jednak element iframe zawiera linki, które mogą spowodować ponowne załadowanie zawartości elementu iframe, to zdarzenie nie zostanie uruchomione ponownie. Jeśli tak jest w Twoim przypadku, użyj odpowiedzi od @pratikabu -$('#myIframe').load(function(){ })
ragamufin
@ skimberk1 Cześć, zgodnie z tą odpowiedzią Twoje rozwiązanie nie zadziała, ponieważ jquery przechowuje zmienną do zapamiętania, czy DOM został załadowany i tylko sprawdza bieżący DOM bieżącej ramki, a nie DOM elementu iframe.
Marco Medrano,
tak, możesz odpalać wydarzenia, pokazywać wideo, odtwarzać muzykę, gdy będzie gotowa, bardzo pouczająca ...
Giridhar Karnik
Zgodnie z tym biletem: dev.jquery.com/ticket/5511 użycie w ramceready iframe zawsze spowoduje natychmiastowe wykonanie funkcji, ponieważ nie jest ona obsługiwana w innych dokumentach niż ramka, w której działa jQuery.
koleś
5

w moim przypadku była to ramka cross-origin i czasami się nie ładowała. rozwiązanie, które zadziałało dla mnie, to: jeśli zostało pomyślnie załadowane, jeśli spróbujesz tego kodu:

var iframe = document.getElementsByTagName('iframe')[0];
console.log(iframe.contentDocument);

nie pozwoli ci uzyskać dostępu contentDocumenti zgłosić błędu z różnych źródeł, jednak jeśli ramka nie zostanie załadowana pomyślnie contentDocument, zwróci #documentobiekt

Raza Ahmed
źródło
2

Kiedy element iFrame jest ładowany, początkowo zawiera #document, więc sprawdzenie stanu obciążenia może działać najlepiej, sprawdzając, co jest teraz.

if ($('iframe').contents().find('body').children().length > 0) {
    // is loaded
} else {
    // is not loaded
}
obimod
źródło
6
To się nie powiedzie, jeśli znajduje się w innej domenie
Funkodebat
Jest to niepoprawne, ponieważ element iframe może być W TRAKCIE ładowania (nie w pełni załadowany).
Mike Shiyan
1

Oto sztuczka działająca w następujący sposób: [nie testowałem różnych przeglądarek!]

Zdefiniuj procedurę obsługi zdarzeń onload elementu iframe zdefiniowaną jako

$('#myIframe').on('load', function() {
    setTimeout(function() {
        try {
            console.log($('#myIframe')[0].contentWindow.document);
        } catch (e) {
            console.log(e);
            if (e.message.indexOf('Blocked a frame with origin') > -1 || e.message.indexOf('from accessing a cross-origin frame.') > -1) {
                alert('Same origin Iframe error found!!!');
                //Do fallback handling if you want here
            }
        }
    }, 1000);

});

Zastrzeżenie: działa tylko w przypadku dokumentów IFRAME SAMEGO POCHODZENIA.

Pratap Singh
źródło
0

Naprawdę świetną metodą jest użycie jQuery AJAX. Ramka nadrzędna wyglądałaby następująco:

<iframe src="iframe_load.php" style="width: 100%; height: 100%;"></iframe>

Plik iframe_load.php załadowałby bibliotekę jQuery i JavaScript, który próbuje załadować docelowy adres URL w AJAX GET:

var the_url_to_load = "http://www.your-website.com" ;
$.ajax({
            type: "GET",
            url: the_url_to_load,
            data: "",
            success: function(data){
                // if can load inside iframe, load the URL
                location.href = the_url_to_load ;
            },
            statusCode: {
                500: function() {
                    alert( 'site has errors' ) ;
                }
            },
            error:function (xhr, ajaxOptions, thrownError){
                // if x-frame-options, site is down or web server is down
                alert( 'URL did not load due to x-frame-options' ) ;
            } });

WAŻNE Miejsce docelowe musi zawierać nagłówek „Access-Control-Allow-Origin”. Przykład w PHP:

HEADER( "Access-Control-Allow-Origin: *" ) ;
Nate Skate
źródło
0

Jeśli chcesz wiedzieć, kiedy element iframe jest gotowy do manipulacji, użyj interwału. W tym przypadku „pinguję” zawartość co 250 ms i jeśli w docelowym elemencie iframe jest jakaś zawartość, zatrzymuję „ping” i coś zrób.

var checkIframeLoadedInterval = setInterval( checkIframeLoaded, 250 );

function checkIframeLoaded() {
    var iframe_content = $('iframe').contents();

    if (iframe_content.length > 0) {
        clearInterval(checkIframeLoadedInterval);

        //Apply styles to the button
        setTimeout(function () {
            //Do something inside the iframe 
            iframe_content.find("body .whatever").css("background-color", "red");
        }, 100); //100 ms of grace time
    }
}
Jimmy Adaro
źródło
0

Jeśli hostujesz stronę i element iframe w tej samej domenie , możesz nasłuchiwać Window.DOMContentLoadedzdarzenia elementu iframe . Musisz DOMContentLoadednajpierw poczekać na uruchomienie oryginalnej strony , a następnie dołączyć DOMContentLoadeddetektor zdarzeń do elementu iframe Window.

Biorąc pod uwagę, że masz następujący element iframe,

<iframe id="iframe-id" name="iframe-name" src="..."></iframe>

następny fragment pozwoli Ci podłączyć się do DOMContentLoadedzdarzenia elementu iframe :

document.addEventListener('DOMContentLoaded', function () {
    var iframeWindow = frames['iframe-name'];
    // var iframeWindow = document.querySelector('#iframe-id').contentWindow
    // var iframeWindow = document.getElementById('iframe-id').contentWindow

    iframeWindow.addEventListener('DOMContentLoaded', function () {
        console.log('iframe DOM is loaded!');
    });
});
Daniel
źródło