Wywoływanie kodu JavaScript w elemencie iframe ze strony nadrzędnej

615

Zasadniczo mam iframeosadzoną na stronie i iframema kilka procedur JavaScript, które muszę wywołać ze strony nadrzędnej.

Teraz przeciwieństwo jest dość proste, wystarczy tylko zadzwonić parent.functionName(), ale niestety potrzebuję dokładnie tego przeciwnie.

Należy pamiętać, że mój problem nie zmienia źródłowy adres URL z następujących iframe, ale wywoływanie funkcji zdefiniowanej w iframe.

Tamas Czinege
źródło
42
Twoja uwaga na temat parent.fuctioName () rozwiązała mój problem z wywoływaniem funkcji w nadrzędnym pliku HTML iframe. Dzięki!
CyberFonic
1
Uwaga: podczas debugowania możesz wykonać takie czynności, jak window.location.href lub parent.location.href, aby wyświetlić adres URL iframe, jeśli chcesz sprawdzić, czy masz odniesienie do szukanego iframe.
AaronLS
Zobacz odpowiedź @le dorfier ... działało dla mnie idealnie.
ErickBest

Odpowiedzi:

542

Załóżmy, że identyfikator iFrame to „targetFrame”, a funkcja, którą chcesz wywołać, to targetFunction():

document.getElementById('targetFrame').contentWindow.targetFunction();

Możesz również uzyskać dostęp do ramki za pomocą window.frameszamiast document.getElementById.

// this option does not work in most of latest versions of chrome and Firefox
window.frames[0].frameElement.contentWindow.targetFunction(); 
Joel Anair
źródło
3
działa w FF 7, Chrome 12, IE 8/9 i Safari (nie jestem pewien co do tej wersji)
Darcy
3
To rozwiązanie nie działa dla mojego gadżetu, oto mój kod document.getElementById('remote_iframe_0').contentWindow.my.create_element_gadg‌​et('verify_user');"remote_iframe_0 jest tworzony programowo przez serwer Apache Shindig, ale window.parent.document.getElementById('remote_iframe_0').contentWindow.my.create_element_gadg‌​et('verify_user');"działa
J Bourne
3
@ Brudny Henryku, co rozumiesz przez „funkcję jQuery”? jQuery to biblioteka JavaScript.
Joel Anair
8
@JellicleCat Dzieje się tak, ponieważ strona nadrzędna i element iframe mają różne hosty, co czyni je ramką krzyżową, jak informuje komunikat. Dopóki protokół, domena i port strony nadrzędnej i elementu iframe są zgodne, wszystko będzie działać poprawnie.
Mark Amery
1
Dodałem przykład użycia wspomnianej metody window.frames.
Elijah Lynn
145

Jest kilka dziwactw, o których należy pamiętać.

  1. HTMLIFrameElement.contentWindowjest prawdopodobnie łatwiejszym sposobem, ale nie jest to dość standardowa właściwość, a niektóre przeglądarki go nie obsługują, głównie starsze. Jest tak, ponieważ standard HTML poziomu 1 DOM nie ma nic do powiedzenia na temat windowobiektu.

  2. Możesz także spróbować HTMLIFrameElement.contentDocument.defaultView, na co zezwala kilka starszych przeglądarek, ale IE tego nie robi. Mimo to standard nie mówi wprost, że odzyskujesz windowobiekt z tego samego powodu, co (1), ale możesz wybrać tutaj kilka dodatkowych wersji przeglądarki.

  3. window.frames['name']powrót okna jest najstarszym, a zatem najbardziej niezawodnym interfejsem. Ale musisz użyć name="..."atrybutu, aby uzyskać ramkę według nazwy, która jest nieco brzydka / przestarzała / przejściowa. ( id="..."byłoby lepiej, ale IE tego nie lubi).

  4. window.frames[number]jest również bardzo niezawodny, ale znajomość właściwego indeksu to sztuczka. Możesz uciec od tego np. jeśli wiesz, że na stronie jest tylko jeden element iframe.

  5. Jest całkiem możliwe, że iframe potomne jeszcze się nie załadowało lub coś innego poszło nie tak, że stało się niedostępne. Łatwiejsze może być odwrócenie przepływu komunikacji: to znaczy, że iframe potomne powiadomi swój window.parentskrypt, gdy zakończy ładowanie i będzie gotowe do oddzwonienia. Przekazując jeden ze swoich obiektów (np. Funkcję wywołania zwrotnego) do skryptu nadrzędnego, ten rodzic może następnie komunikować się bezpośrednio ze skryptem w ramce iframe, nie martwiąc się o to, z jakim elementem HTMLIFrameElement jest skojarzony.

Bobin
źródło
13
Świetne rzeczy! Szczególnie twoja sugestia 5. okazała się niezwykle cenna. Rozwiązało mi to wiele problemów podczas próby uzyskania dostępu do funkcji iFrame od rodzica w Chrome. Przekazanie obiektu funkcji z iFrame sprawiło, że działa on jak urok w Chrome, FF, a nawet IE od 9 do 7, a także bardzo łatwo sprawdzić, czy funkcja już się załadowała. Dzięki!
Jpsy 24.11.11
Numer 3 działa, ale lepiej, jeśli zrobisz to tak, jak @le dorfier umieścił go w swoim komentarzu. Zwróć uwagę na methode()część.
ErickBest
Należy również pamiętać, że ze względu na ograniczenia bezpieczeństwa w nowoczesnych przeglądarkach (testowane FF29 i Chrome34) bardzo ważne jest, gdzie umieścisz wywołanie funkcji. Samo wywołanie funkcji ze znacznika skryptu lub $ (document) .ready () nie będzie działać. Zamiast tego umieść wywołanie w tagu onload ciała lub w kotwicy <a href="javascript:doit();"> zrób to </a>, jak sugerowano w innym miejscu (patrz stackoverflow.com/questions/921387/... ). Przekazanie wywołania funkcji do skryptu jako wywołanie zwrotne również zwykle działa.
tytuł
@chovy: Nie, zobacz wyjaśnienie w odpowiedzi Viveka na ten temat.
bobince
66

Wywołanie nadrzędnej funkcji JS z iframejest możliwe, ale tylko wtedy, gdy zarówno rodzic, jak i strona załadowana do iframesą z tej samej domeny, tj. Abc.com, i oba używają tego samego protokołu, tzn. Oba są włączone http://lub https://.

Połączenie nie powiedzie się w poniższych przypadkach:

  1. Strona nadrzędna i strona iframe pochodzą z innej domeny.
  2. Używają różnych protokołów, jeden znajduje się na http: // a drugi na https: //.

Wszelkie obejście tego ograniczenia byłoby wyjątkowo niepewne.

Wyobraź sobie na przykład, że zarejestrowałem domenę superwinningcontest.com i wysłałem linki do e-maili użytkowników. Gdy załadowali stronę główną, mogłem tam ukryć kilka iframesekund i przeczytać ich kanał na Facebooku, sprawdzić ostatnie transakcje Amazon lub PayPal lub - jeśli korzystali z usługi, która nie zapewniała wystarczającego bezpieczeństwa - przelać pieniądze z ich konta Dlatego JavaScript jest ograniczony do tej samej domeny i tego samego protokołu.

Vivek Singh CHAUHAN
źródło
6
Obejściem tego problemu jest użycie pięknego i niebezpiecznego en.wikipedia.org/wiki/Cross-document_messaging
Laramie
Czy ktoś wie, czy inna subdomena spowoduje awarię? Mam problem z debugowaniem problemu, który moim zdaniem jest z tym związany - iframe wywołuje funkcję okna nadrzędnego, ale połączenie się nie dzieje.
Jake,
1
Uwaga, to również nie powiedzie się dla różnych numerów portów. tj abc.com:80 = abc.com:8080!
prasanthv
31

W ramce IFRAME upublicznij swoją funkcję dla obiektu window:

window.myFunction = function(args) {
   doStuff();
}

Aby uzyskać dostęp ze strony nadrzędnej, użyj tego:

var iframe = document.getElementById("iframeId");
iframe.contentWindow.myFunction(args);
Tomalak
źródło
Świetna odpowiedź. Nie jestem pewien konwencji tutaj. Mam pytanie uzupełniające. Jeśli mam zacząć nowe pytanie, zrobię to. Mam jeden problem. Element iframe mojej strony jest zapełniany dynamicznie. Zawiera przycisk, którego onclick()zdarzenie po prostu wywołuje window.print(). Powoduje wydrukowanie treści z następujących iframedoskonale. Ale gdy wywoływana jest funkcja publiczna strony iframe window.print()i jeśli strona nadrzędna wywołuje funkcję publiczną, treść rodzica jest drukowana. Jak mam to kodować, aby wywołanie window.print()funkcji publicznej zachowywało się tak, jak w onclickprzypadku zdarzenia?
Karl
1
@Karl Nie chcesz dzwonić onclick. Chcesz zadzwonić iframe.contentWindow.print().
Tomalak
Niestety to nie działa. Może powinienem zacząć nowe pytanie i udokumentować swój kod? Tak czy inaczej, treść strony nadrzędnej jest również drukowana. document.getElementById('myFrame').contentWindow.print();' and the pubic function printContent () `(nie program obsługi zdarzeń oncick), który wywołuje wywołano w window.print()ten sposób:document.getElementById('myFrame').contentWindow.printContent();
Karl
1
@Karl - Tak, najlepiej zacząć nowe pytanie. Chyba że okno nadrzędne znajduje się w innej domenie niż element iframe. Wtedy nic, czego spróbujesz, nigdy nie zadziała .
Tomalak,
To, co tu zgłosiłem, wydaje się być problemem IE (testowanie w IE8). W bieżącej wersji Chrome nie ma problemu. Jednak jeszcze nie przetestowałem innych przeglądarek. Dla zainteresowanych, zobacz ten jsFiddle (myślę, że jest to dobry przykład dynamicznego ładowania iframe i wywoływania funkcji publicznej od rodzica).
Karl
20

Jeśli cel elementu iFrame i dokument zawierający są w innej domenie, wcześniej opublikowane metody mogą nie działać, ale istnieje rozwiązanie:

Na przykład, jeśli dokument A zawiera element iframe, który zawiera dokument B, a skrypt w dokumencie A wywołuje metodę postMessage () na obiekcie okna dokumentu B, wówczas zostanie wywołane zdarzenie komunikatu na tym obiekcie, oznaczone jako pochodzące z okna dokument A. Skrypt w dokumencie A może wyglądać następująco:

var o = document.getElementsByTagName('iframe')[0];
o.contentWindow.postMessage('Hello world', 'http://b.example.org/');

Aby zarejestrować moduł obsługi zdarzeń dla zdarzeń przychodzących, skrypt użyłby metody addEventListener () (lub podobnych mechanizmów). Na przykład skrypt w dokumencie B może wyglądać następująco:

window.addEventListener('message', receiver, false);
function receiver(e) {
  if (e.origin == 'http://example.com') {
    if (e.data == 'Hello world') {
      e.source.postMessage('Hello', e.origin);
    } else {
      alert(e.data);
    }
  }
}

Skrypt ten najpierw sprawdza, czy domena jest domeną oczekiwaną, a następnie sprawdza wiadomość, którą albo wyświetla użytkownikowi, albo odpowiada, wysyłając wiadomość z powrotem do dokumentu, który wysłał wiadomość w pierwszej kolejności.

przez http://dev.w3.org/html5/postmsg/#web-messaging

19greg96
źródło
1
easyXDM zapewnia abstrakcję nad PostMessage dla starszych przeglądarek (w tym awarię Flash (LocalConnection) dla upartych dinozaurów).
JonnyReeves
1
to świetnie ... po wypróbowaniu powyższych odpowiedzi w końcu dostałem twoją odpowiedź, dzięki!
vkGunasekaran
9

Dla przypomnienia napotkałem dzisiaj ten sam problem, ale tym razem strona została osadzona w obiekcie, a nie w ramce iframe (ponieważ był to dokument XHTML 1.1). Oto jak to działa z obiektami:

document
  .getElementById('targetFrame')
  .contentDocument
  .defaultView
  .targetFunction();

(przepraszam za brzydkie podziały linii, nie pasowały do ​​jednej linii)

Tamas Czinege
źródło
8

Ramka IFRAME powinna znajdować się w frames[]kolekcji. Użyj czegoś takiego

frames['iframeid'].method();
dkretz
źródło
Ta odpowiedź z pewnością mogłaby wykorzystać więcej informacji, ponieważ zdecydowanie istnieją inne względy. Po pierwsze, zakładam, że methoddeweloper musi zrobić właściwość obiektu iframe window.
matty
8

Quirksmode miał na ten temat post .

Ponieważ strona jest teraz zepsuta i dostępna tylko przez archive.org, odtworzyłem ją tutaj:

Ramki

Na tej stronie przedstawiam krótki przegląd dostępu do ramek iframe ze strony, na której są. Nic dziwnego, że istnieją pewne uwagi dotyczące przeglądarki.

Ramka iframe jest ramką wbudowaną, która mimo że zawiera całkowicie oddzielną stronę z własnym adresem URL, jest jednak umieszczana wewnątrz innej strony HTML. Daje to bardzo ładne możliwości w zakresie projektowania stron internetowych. Problem polega na uzyskaniu dostępu do ramki iframe, na przykład w celu załadowania do niej nowej strony. Ta strona wyjaśnia, jak to zrobić.

Rama czy obiekt?

Podstawowe pytanie dotyczy tego, czy element iframe jest postrzegany jako ramka, czy jako obiekt.

  • Jak wyjaśniono na stronie Wprowadzenie do ramek , jeśli używasz ramek, przeglądarka tworzy dla Ciebie ( top.frames[1].frames[2]i tak dalej) hierarchię ramek . Czy iframe pasuje do tej hierarchii ramek?
  • Czy też przeglądarka widzi element iframe jako kolejny obiekt, obiekt, który ma właściwość src? W takim przypadku musimy użyć standardowego wywołania DOM (na przykład, document.getElementById('theiframe'))aby uzyskać do niego dostęp. Zasadniczo przeglądarki zezwalają na oba widoki na „prawdziwe” (zakodowane na stałe) iframe, ale generowanych iframe nie można uzyskać jako ramek.

Atrybut NAME

Najważniejszą zasadą jest podanie dowolnego elementu iframe, który utworzysz name, nawet jeśli używasz również id.

<iframe src="iframe_page1.html"
    id="testiframe"
    name="testiframe"></iframe>

Większość przeglądarek potrzebuje tego nameatrybutu, aby element iframe stał się częścią hierarchii ramek. Niektóre przeglądarki (zwłaszcza Mozilla) wymagają, idaby iframe był dostępny jako obiekt. Przypisując oba atrybuty do elementu iframe, opcje pozostają otwarte. Ale namejest o wiele ważniejsze niż id.

Dostęp

Albo uzyskujesz dostęp do iframe jako obiektu i zmieniasz jego, srcalbo uzyskujesz dostęp do iframe jako ramki i zmieniasz jego location.href.

document.getElementById ('iframe_id'). src = 'newpage.html'; frames ['iframe_name']. location.href = 'newpage.html'; Składnia ramki jest nieco lepsza, ponieważ Opera 6 ją obsługuje, ale nie składni obiektowej.

Dostęp do iframe

Aby uzyskać pełną obsługę wielu przeglądarek, należy nadać ramce iframe nazwę i użyć

frames['testiframe'].location.href

składnia. O ile wiem, to zawsze działa.

Dostęp do dokumentu

Dostęp do dokumentu w ramce iframe jest dość prosty, pod warunkiem użycia nameatrybutu. Aby policzyć liczbę łączy w dokumencie w ramce iframe, wykonaj frames['testiframe'].document.links.length.

Wygenerowane iframe

Podczas generowania elementu iframe za pośrednictwem DOM W3C element iframe nie jest natychmiast wprowadzany do framestablicy, a frames['testiframe'].location.hrefskładnia nie działa od razu. Przeglądarka potrzebuje trochę czasu, zanim ramka iframe pojawi się w tablicy, czas, w którym żaden skrypt nie może zostać uruchomiony.

document.getElementById('testiframe').srcSkładnia działa dobrze w każdych okolicznościach.

targetAtrybutem link nie działa albo z wygenerowanych iframe, z wyjątkiem Opery, choć dałem wygenerowany iframe zarówno namei id.

Brak targetwsparcia oznacza, że ​​musisz używać JavaScript do zmiany zawartości generowanego elementu iframe, ale ponieważ i tak potrzebujesz JavaScript, aby go wygenerować, nie widzę w tym większego problemu.

Rozmiar tekstu w ramkach iframe

Ciekawy błąd tylko w Explorer 6:

Po zmianie rozmiaru tekstu za pomocą menu Widok rozmiary tekstu w ramkach iframe są odpowiednio zmieniane. Jednak ta przeglądarka nie zmienia podziałów wierszy w oryginalnym tekście, więc część tekstu może stać się niewidoczna lub mogą wystąpić podziały wierszy, podczas gdy linia może zawierać jeszcze jedno słowo.

activout.se
źródło
5
Ten link wydaje się być zepsuty.
zaphod
1
Co gorsza, nie mogę znaleźć pokrewnej strony nigdzie w trybie quirksmode.
stricjux
7

Znalazłem dość eleganckie rozwiązanie.

Jak powiedziałeś, dość łatwo jest wykonać kod znajdujący się w dokumencie nadrzędnym. I to jest podstawa mojego kodu, wręcz przeciwnie.

Gdy ładuje się mój element iframe, wywołuję funkcję znajdującą się w dokumencie nadrzędnym, przekazując jako argument odwołanie do funkcji lokalnej znajdującej się w dokumencie elementu iframe. Dokument nadrzędny ma teraz bezpośredni dostęp do funkcji iframe poprzez to odwołanie.

Przykład:

Na rodzicu:

function tunnel(fn) {
    fn();
}

W ramce iframe:

var myFunction = function() {
    alert("This work!");
}

parent.tunnel(myFunction);

Kiedy iframe się ładuje, wywoła parent.tunnel (YourFunctionReference), który wykona funkcję otrzymaną w parametrze.

To proste, bez konieczności radzenia sobie ze wszystkimi niestandardowymi metodami z różnych przeglądarek.

Julien L.
źródło
Cześć, czy możesz potwierdzić to „To proste, bez konieczności radzenia sobie ze wszystkimi niestandardowymi metodami z różnych przeglądarek”.
Milche Patern
1
Używam tej metody do różnych projektów, wydaje się, że działa bardzo dobrze. Nie przetestowałem osobiście wszystkich przeglądarek w obiegu, ale nigdy nie otrzymałem zgłoszenia błędu.
Julien L
Działa jak urok, IE 11, Chrome ~ 50.
Augustas,
1
Zgadnij, że to nie dotyczy wielu domen.
Tushar Shukla
@TusharShukla Niestety nie, nie jest.
jeff-h
6

Niektóre z tych odpowiedzi nie odnoszą się do problemu CORS lub nie pokazują, gdzie umieszczasz fragmenty kodu, aby umożliwić komunikację.

Oto konkretny przykład. Powiedzmy, że chcę kliknąć przycisk na stronie nadrzędnej i niech coś zrobi w ramce iframe. Oto jak bym to zrobił.

Parent_frame.html

<button id='parent_page_button' onclick='call_button_inside_frame()'></button>

function call_button_inside_frame() {
   document.getElementById('my_iframe').contentWindow.postMessage('foo','*');
}

iframe_page.html

window.addEventListener("message", receiveMessage, false);

function receiveMessage(event)
    {
      if(event) {
        click_button_inside_frame();
    }
}

function click_button_inside_frame() {
   document.getElementById('frame_button').click();
}

Aby przejść w drugą stronę (kliknij przycisk wewnątrz elementu iframe, aby wywołać metodę poza elementem iframe), po prostu zmień miejsce, w którym fragment kodu jest aktywny, i zmień to:

document.getElementById('my_iframe').contentWindow.postMessage('foo','*');

do tego:

window.parent.postMessage('foo','*')
Cybernetyczny
źródło
4

Kontynuując odpowiedź JoelAnair :

Aby uzyskać większą niezawodność, użyj:

var el = document.getElementById('targetFrame');

if(el.contentWindow)
{
   el.contentWindow.targetFunction();
}
else if(el.contentDocument)
{
   el.contentDocument.targetFunction();
}

Działa jak urok :)

Nitin Bansal
źródło
3

Postępując zgodnie z odpowiedzią Nitin Bansal

i 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 el = document.getElementById('targetFrame');

var frame_win = getIframeWindow(el);

if (frame_win) {
  frame_win.targetFunction();
  ...
}
...
Dominique Fortin
źródło
2
       $("#myframe").load(function() {
            alert("loaded");
        });
GeverGever
źródło
2

Te same rzeczy, ale nieco łatwiejszym sposobem będzie Jak odświeżyć stronę nadrzędną ze strony w elemencie iframe . Wystarczy wywołać funkcję strony nadrzędnej, aby wywołać funkcję javascript w celu ponownego załadowania strony:

window.location.reload();

Lub zrób to bezpośrednio ze strony w ramce iframe:

window.parent.location.reload();

Obie prace.

sangam
źródło
1

Jeśli chcesz wywołać funkcję JavaScript na obiekcie nadrzędnym z elementu iframe wygenerowanego przez inną funkcję, np. Shadowbox lub lightbox.

Powinieneś spróbować użyć windowobiektu i wywołać funkcję nadrzędną:

window.parent.targetFunction();
Santhanakumar
źródło
Wierzę, że OP próbuje iść w innym kierunku
CommandZ
-3

Użyj następującego, aby wywołać funkcję ramki na stronie nadrzędnej

parent.document.getElementById('frameid').contentWindow.somefunction()
Piaszczysty
źródło