Meminta kode JavaScript dalam iframe dari halaman induk

615

Pada dasarnya, saya memiliki iframeembedded di halaman dan iframememiliki beberapa rutinitas JavaScript yang saya butuhkan dari halaman induk.

Sekarang kebalikannya cukup sederhana karena Anda hanya perlu menelepon parent.functionName(), tetapi sayangnya, saya perlu kebalikannya.

Harap dicatat bahwa masalah saya tidak mengubah sumber URL dari iframe, tapi menyerukan fungsi didefinisikan dalam iframe.

Tamas Czinege
sumber
42
Catatan Anda tentang parent.fuctioName () memecahkan masalah saya memanggil fungsi di html induk iframe. Terima kasih!
CyberFonic
1
Catatan ketika debugging Anda dapat melakukan hal-hal seperti window.location.href atau parent.location.href untuk melihat url iframe, jika Anda ingin memverifikasi bahwa Anda memiliki referensi ke iframe yang Anda cari.
AaronLS
Lihat @le dorfier's Jawaban ... bekerja dengan sempurna untuk saya.
ErickBest

Jawaban:

542

Asumsikan id iFrame Anda adalah "targetFrame" dan fungsi yang ingin Anda panggil adalah targetFunction():

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

Anda juga dapat mengakses bingkai menggunakan window.framesbukan document.getElementById.

// this option does not work in most of latest versions of chrome and Firefox
window.frames[0].frameElement.contentWindow.targetFunction(); 
Joel Anair
sumber
3
berfungsi di FF 7, Chrome 12, IE 8/9, dan Safari (tidak yakin dengan versi ini)
Darcy
3
Solusi ini tidak berfungsi untuk gadget saya, berikut ini kode saya document.getElementById('remote_iframe_0').contentWindow.my.create_element_gadg‌​et('verify_user');"remote_iframe_0 dibuat secara terprogram oleh server apache shindig tetapi window.parent.document.getElementById('remote_iframe_0').contentWindow.my.create_element_gadg‌​et('verify_user');"berfungsi
J Bourne
3
@Dirty Henry apa yang Anda maksud dengan "fungsi jQuery?" jQuery adalah perpustakaan JavaScript.
Joel Anair
8
@JellicleCat Itu karena halaman induk dan iframe memiliki host yang berbeda, menjadikannya sebagai bingkai asal-silang, seperti pesannya memberitahu Anda. Selama protokol, domain dan port halaman induk dan iframe cocok, semuanya akan berfungsi dengan baik.
Mark Amery
1
Saya menambahkan contoh tentang cara menggunakan metode window.frames yang disebutkan.
Elijah Lynn
145

Ada beberapa kebiasaan yang perlu diperhatikan di sini.

  1. HTMLIFrameElement.contentWindowmungkin cara yang lebih mudah, tetapi itu bukan properti standar dan beberapa browser tidak mendukungnya, kebanyakan lebih tua. Ini karena standar HTML DOM Level 1 tidak ada hubungannya dengan windowobjek.

  2. Anda juga dapat mencoba HTMLIFrameElement.contentDocument.defaultView, yang memungkinkan beberapa peramban yang lebih tua tetapi IE tidak. Meski begitu, standar tidak secara eksplisit mengatakan bahwa Anda mendapatkan windowobjek kembali, untuk alasan yang sama seperti (1), tetapi Anda dapat mengambil beberapa versi browser tambahan di sini jika Anda peduli.

  3. window.frames['name']mengembalikan jendela adalah antarmuka tertua dan karenanya paling dapat diandalkan. Tetapi Anda kemudian harus menggunakan name="..."atribut untuk bisa mendapatkan bingkai dengan nama, yang sedikit jelek / usang / transisional. ( id="..."akan lebih baik tetapi IE tidak suka itu.)

  4. window.frames[number]juga sangat dapat diandalkan, tetapi mengetahui indeks yang tepat adalah triknya. Anda bisa lolos dengan ini misalnya. jika Anda tahu Anda hanya memiliki satu iframe di halaman.

  5. Sangat mungkin anak iframe belum memuat, atau sesuatu yang salah membuatnya tidak dapat diakses. Anda mungkin lebih mudah membalikkan arus komunikasi: yaitu, mintalah anak iframe memberi tahu window.parentskripnya ketika sudah selesai dimuat dan siap dipanggil kembali. Dengan mengirimkan salah satu objeknya sendiri (mis. Fungsi panggilan balik) ke skrip induk, induk tersebut kemudian dapat berkomunikasi langsung dengan skrip di iframe tanpa harus khawatir tentang HTMLIFrameElement yang terkait dengannya.

bobince
sumber
13
Barang bagus! Terutama saran Anda 5. telah terbukti sangat berharga. Itu memecahkan banyak sakit kepala ketika mencoba mengakses fungsi iFrame dari induk di Chrome. Melewatkan objek fungsi keluar dari iFrame membuatnya berjalan seperti pesona di Chrome, FF dan bahkan IE dari 9 hingga 7 dan juga membuatnya sangat mudah untuk memeriksa apakah fungsi telah dimuat. Terima kasih!
Jpsy
Nomor 3 berfungsi, tetapi lebih baik jika Anda melakukannya seperti @le dorfier memasukkannya ke dalam komentarnya. Perhatikan methode()bagian itu.
ErickBest
Harap perhatikan juga bahwa karena pembatasan keamanan di peramban modern (FF29 dan Chrome34 yang diuji) sangat penting di mana Anda melakukan panggilan fungsi. Hanya memanggil fungsi dari tag skrip atau $ (dokumen) .ready () tidak akan berfungsi. Alih-alih menempatkan panggilan di dalam tag muat tubuh, atau di jangkar <a href="javascript:doit();"> lakukan </a> seperti yang disarankan di tempat lain (lihat stackoverflow.com/questions/921387/… ). Melewati fungsi panggil ke skrip sebagai panggilan balik juga biasanya berfungsi.
titusn
@ chovy: Tidak, lihat klarifikasi dalam jawaban Vivek tentang ini.
bobince
66

Memanggil fungsi JS induk dari iframedimungkinkan, tetapi hanya ketika kedua induk dan halaman yang dimuat dalam iframeberasal dari domain yang sama yaitu abc.com, dan keduanya menggunakan protokol yang sama yaitu keduanya aktif http://atau https://.

Panggilan akan gagal dalam kasus yang disebutkan di bawah ini:

  1. Halaman induk dan halaman iframe berasal dari domain yang berbeda.
  2. Mereka menggunakan protokol yang berbeda, satu di http: // dan lainnya di https: //.

Solusi untuk pembatasan ini akan sangat tidak aman.

Sebagai contoh, bayangkan saya mendaftarkan domain superwinningcontest.com dan mengirimkan tautan ke email orang. Ketika mereka memuat halaman utama, saya bisa menyembunyikan beberapa iframedi sana dan membaca umpan Facebook mereka, memeriksa transaksi Amazon atau PayPal baru-baru ini, atau - jika mereka menggunakan layanan yang tidak menerapkan keamanan yang memadai - transfer uang keluar dari mereka akun. Itu sebabnya JavaScript terbatas pada domain yang sama dan protokol yang sama.

Vivek Singh CHAUHAN
sumber
6
Solusinya adalah dengan menggunakan en.wikipedia.org/wiki/Cross-document_messaging yang
Laramie
Adakah yang tahu jika sub-domain yang berbeda akan menyebabkan ini gagal? Saya mengalami kesulitan men-debug masalah yang saya yakini terkait dengan ini - iframe memanggil fungsi jendela induk, tetapi panggilan itu tidak terjadi.
Jake
1
Catatan, itu juga akan gagal untuk nomor port yang berbeda. yaitu abc.com:80! = abc.com:8080
prasanthv
31

Di IFRAME, jadikan fungsi Anda publik untuk objek jendela:

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

Untuk akses dari halaman induk, gunakan ini:

var iframe = document.getElementById("iframeId");
iframe.contentWindow.myFunction(args);
Tomalak
sumber
Jawaban yang bagus Tidak yakin dengan konvensi di sini. Saya punya pertanyaan lanjutan. Jika saya harus memulai Pertanyaan baru, saya akan. Saya punya satu masalah. Iframe halaman saya diisi secara dinamis. Ini berisi tombol yang onclick()acaranya hanya memanggil window.print(). Ini mencetak konten dengan iframesempurna. Tetapi ketika fungsi publik halaman iframe memanggil window.print()dan jika halaman induk memanggil fungsi publik, konten induk dicetak. Bagaimana saya harus mengkode ini sehingga panggilan ke window.print()dalam fungsi publik berperilaku seperti halnya dalam onclickacara tersebut?
Karl
1
@ Karl Anda tidak ingin menelepon onclick. Anda ingin menelepon iframe.contentWindow.print().
Tomalak
Sayangnya, itu tidak berhasil. Mungkin saya harus memulai Pertanyaan baru dan mendokumentasikan kode saya? Apa pun itu, konten halaman induk juga dicetak. document.getElementById('myFrame').contentWindow.print();' and the pubic function printContent () `(bukan pengendali event oncick) yang window.print()dipanggil dengan cara ini:document.getElementById('myFrame').contentWindow.printContent();
Karl
1
@ Kar - Ya, maka yang terbaik adalah memulai pertanyaan baru. Kecuali jika jendela induk berada pada domain yang berbeda dari iframe. Maka tidak ada yang Anda coba akan berhasil .
Tomalak
Apa yang saya laporkan di sini tampaknya merupakan masalah IE (pengujian di IE8). Tidak ada masalah dalam versi Chrome saat ini. Namun belum menguji browser lain. Bagi yang berminat, lihat jsFiddle ini (saya pikir ini adalah contoh yang baik dari pemuatan iframe dinamis dan memanggil fungsi publik dari induknya.)
Karl
20

Jika target iFrame dan dokumen yang berisi berada di domain yang berbeda, metode yang diposting sebelumnya mungkin tidak berfungsi, tetapi ada solusinya:

Misalnya, jika dokumen A berisi elemen iframe yang berisi dokumen B, dan skrip dalam dokumen A memanggil postMessage () pada objek Window dari dokumen B, maka peristiwa pesan akan ditembakkan pada objek itu, ditandai sebagai berasal dari Window of the dokumen A. Naskah dalam dokumen A mungkin terlihat seperti:

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

Untuk mendaftarkan pengendali acara untuk acara yang masuk, skrip akan menggunakan addEventListener () (atau mekanisme serupa). Misalnya, skrip dalam dokumen B mungkin terlihat seperti:

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

Skrip ini pertama-tama memeriksa domain adalah domain yang diharapkan, dan kemudian melihat pesan, yang ditampilkannya kepada pengguna, atau ditanggapi dengan mengirim pesan kembali ke dokumen yang mengirim pesan di tempat pertama.

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

19greg96
sumber
1
easyXDM menyediakan abstraksi lebih dari PostMessage untuk browser lama (termasuk Flash (LocalConnection) fallback untuk dinosaurus yang membandel).
JonnyReeves
1
itu hebat ... setelah mencoba jawaban di atas akhirnya saya mendapat jawaban Anda, terima kasih!
vkGunasekaran
9

Sekadar catatan, saya mengalami masalah yang sama hari ini, tetapi kali ini halamannya disematkan pada objek, bukan iframe (karena itu adalah dokumen XHTML 1.1). Begini cara kerjanya dengan objek:

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

(maaf untuk jeda baris jelek, tidak cocok dalam satu baris)

Tamas Czinege
sumber
8

IFRAME harus dalam frames[]koleksi. Gunakan sesuatu seperti

frames['iframeid'].method();
dkretz
sumber
Jawaban ini tentu saja dapat menggunakan lebih banyak informasi, karena pasti ada beberapa pertimbangan lain. Pertama, saya menganggap dev perlu membuat methodproperti dari objek iframe window.
matty
8

Quirksmode punya pos tentang ini .

Karena halaman sekarang rusak, dan hanya dapat diakses melalui archive.org, saya mereproduksi di sini:

IFrames

Pada halaman ini saya memberikan gambaran singkat tentang mengakses iframe dari halaman yang sedang mereka buka. Tidak mengherankan, ada beberapa pertimbangan browser.

Iframe adalah bingkai inline, sebuah bingkai yang, meskipun mengandung halaman yang sepenuhnya terpisah dengan URL-nya sendiri, tetap ditempatkan di dalam halaman HTML lain. Ini memberikan kemungkinan yang sangat bagus dalam desain web. Masalahnya adalah untuk mengakses iframe, misalnya untuk memuat halaman baru ke dalamnya. Halaman ini menjelaskan cara melakukannya.

Bingkai atau objek?

Pertanyaan mendasarnya adalah apakah iframe dilihat sebagai bingkai atau sebagai objek.

  • Seperti dijelaskan pada halaman Pendahuluan untuk membingkai , jika Anda menggunakan bingkai, peramban membuat hierarki bingkai untuk Anda ( top.frames[1].frames[2]dan semacamnya). Apakah iframe cocok dengan hierarki frame ini?
  • Atau apakah browser melihat iframe hanya sebagai objek lain, sebuah objek yang kebetulan memiliki properti src? Dalam hal ini kita harus menggunakan panggilan DOM standar (suka document.getElementById('theiframe'))mengaksesnya. Secara umum browser memungkinkan kedua tampilan pada iframe 'real' (hard-coded), tetapi iframe yang dihasilkan tidak dapat diakses sebagai frame.

Atribut NAME

Aturan yang paling penting adalah untuk memberikan iframe apa pun yang Anda buat nameatribut, bahkan jika Anda juga menggunakan id.

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

Sebagian besar browser memerlukan nameatribut untuk membuat bagian iframe dari hirarki frame. Beberapa browser (terutama Mozilla) membutuhkan idagar iframe dapat diakses sebagai objek. Dengan menetapkan kedua atribut ke iframe Anda membuat opsi Anda tetap terbuka. Tetapi namejauh lebih penting daripada id.

Mengakses

Entah Anda mengakses iframe sebagai objek dan mengubahnya srcatau Anda mengakses iframe sebagai bingkai dan mengubahnya location.href.

document.getElementById ('iframe_id'). src = 'newpage.html'; bingkai ['iframe_name']. location.href = 'newpage.html'; Sintaks bingkai sedikit lebih disukai karena Opera 6 mendukungnya tetapi bukan sintaks objek.

Mengakses iframe

Jadi untuk pengalaman lintas-peramban yang lengkap, Anda harus memberi nama iframe dan menggunakannya

frames['testiframe'].location.href

sintaksis. Sejauh yang saya tahu ini selalu berhasil.

Mengakses dokumen

Mengakses dokumen di dalam iframe cukup sederhana, asalkan Anda menggunakan nameatribut tersebut. Untuk menghitung jumlah tautan dalam dokumen di iframe, lakukan frames['testiframe'].document.links.length.

Iframe yang dihasilkan

Ketika Anda menghasilkan iframe melalui DOM W3C , iframe tidak segera dimasukkan ke dalam framesarray, danframes['testiframe'].location.href sintaks tidak akan langsung berfungsi. Peramban memerlukan sedikit waktu sebelum iframe muncul dalam array, waktu selama skrip tidak dapat berjalan.

Itu document.getElementById('testiframe').src sintaks bekerja dengan baik dalam segala situasi.

The targetatribut link tidak bekerja baik dengan iframe dihasilkan, kecuali dalam Opera, meskipun saya memberi saya dihasilkan iframe baik namedanid .

Kurangnya targetdukungan berarti bahwa Anda harus menggunakan JavaScript untuk mengubah konten iframe yang dihasilkan, tetapi karena Anda tetap membutuhkan JavaScript untuk menghasilkannya, saya tidak melihat ini sebagai masalah.

Ukuran teks dalam iframe

Bug hanya Explorer 6 yang penasaran:

Saat Anda mengubah ukuran teks melalui menu Lihat, ukuran teks dalam iframe diubah dengan benar. Namun, peramban ini tidak mengubah jeda baris dalam teks asli, sehingga bagian dari teks dapat menjadi tidak terlihat, atau jeda baris dapat terjadi sementara garis masih dapat menahan kata lain.

activout.se
sumber
5
Tautan itu tampaknya rusak.
zaphod
1
Dan untuk memperburuk masalah, saya tidak dapat menemukan halaman terkait di quirksmode.
stricjux
7

Saya menemukan solusi yang cukup elegan.

Seperti yang Anda katakan, cukup mudah untuk mengeksekusi kode yang terletak pada dokumen induk. Dan itulah basis kode saya, lakukan hal sebaliknya.

Ketika iframe saya dimuat, saya memanggil sebuah fungsi yang terletak di dokumen induk, sebagai argumen sebagai referensi ke fungsi lokal, yang terletak di dokumen iframe. Dokumen induk sekarang memiliki akses langsung ke fungsi iframe melalui referensi ini.

Contoh:

Pada orang tua:

function tunnel(fn) {
    fn();
}

Di iframe:

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

parent.tunnel(myFunction);

Ketika iframe dimuat, ia akan memanggil parent.tunnel (YourFunctionReference), yang akan menjalankan fungsi yang diterima dalam parameter.

Sederhana, tanpa harus berurusan dengan semua metode non-standar dari berbagai browser.

Julien L
sumber
Hai, dapatkah Anda mengonfirmasi ini "Sesederhana itu, tanpa harus berurusan dengan semua metode non-standar dari berbagai browser."?
Milche Patern
1
Saya menggunakan metode ini pada berbagai proyek, sepertinya bekerja dengan sangat baik. Saya belum menguji secara pribadi semua peramban yang beredar, tetapi saya juga tidak pernah mendapatkan laporan bug tentang itu.
Julien L
Bekerja seperti pesona, IE 11, Chrome ~ 50.
Augustas
1
Kira itu bukan lintas domain .. Apakah itu?
Tushar Shukla
@TusharShukla Sayangnya tidak, tidak.
jeff-h
6

Beberapa jawaban ini tidak membahas masalah CORS, atau tidak membuatnya menjadi jelas di mana Anda menempatkan cuplikan kode untuk memungkinkan komunikasi.

Ini adalah contoh nyata. Katakanlah saya ingin mengklik tombol pada halaman induk, dan minta yang melakukan sesuatu di dalam iframe. Inilah cara saya akan melakukannya.

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

Untuk menuju ke arah lain (klik tombol di dalam iframe untuk memanggil metode di luar iframe) cukup alihkan tempat cuplikan kode tinggal, dan ubah ini:

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

untuk ini:

window.parent.postMessage('foo','*')
Berhubung dgn sibernetika
sumber
4

Melanjutkan dengan jawaban JoelAnair :

Untuk ketahanan lebih, gunakan sebagai berikut:

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

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

Workd like charm :)

Nitin Bansal
sumber
3

Folowing jawaban Nitin Bansal

dan bahkan lebih kuat:

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

dan

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

var frame_win = getIframeWindow(el);

if (frame_win) {
  frame_win.targetFunction();
  ...
}
...
Dominique Fortin
sumber
2
       $("#myframe").load(function() {
            alert("loaded");
        });
GeverGever
sumber
2

Hal yang sama tetapi cara yang sedikit lebih mudah adalah Bagaimana me-refresh halaman induk dari halaman di dalam iframe . Cukup panggil fungsi halaman induk untuk menjalankan fungsi javascript untuk memuat ulang halaman:

window.location.reload();

Atau lakukan ini langsung dari halaman di iframe:

window.parent.location.reload();

Keduanya bekerja.

sangam
sumber
1

Jika Anda ingin menjalankan fungsi JavaScript pada Induk dari iframe yang dihasilkan oleh fungsi lain, ex shadowbox atau lightbox.

Anda harus mencoba memanfaatkan windowobjek dan menjalankan fungsi induk:

window.parent.targetFunction();
Santhanakumar
sumber
Saya percaya OP sedang mencoba untuk pergi ke arah lain
CommandZ
-3

Gunakan fungsi berikut untuk memanggil bingkai di halaman induk

parent.document.getElementById('frameid').contentWindow.somefunction()
Sandy
sumber