Gọi mã JavaScript trong iframe từ trang mẹ


615

Về cơ bản, tôi có một iframenhúng trong một trang và iframecó một số thói quen JavaScript mà tôi cần phải gọi từ trang mẹ.

Bây giờ thì ngược lại khá đơn giản khi bạn chỉ cần gọi parent.functionName(), nhưng thật không may, tôi cần chính xác điều ngược lại.

Xin lưu ý rằng vấn đề của tôi không phải là thay đổi URL nguồn của iframe, mà gọi một hàm được xác định trong iframe.


42
Lưu ý của bạn về Parent.fuctioName () đã giải quyết vấn đề của tôi khi gọi một hàm trong html cha của iframe. Thanx!
CyberFonic

1
Lưu ý khi gỡ lỗi, bạn có thể thực hiện những việc như window.location.href hoặc Parent.location.href để xem url của iframe, nếu bạn muốn xác minh rằng bạn có tham chiếu đến iframe mà bạn đang tìm kiếm.
AaronLS

Xem câu trả lời của @le dorfier ... đã làm việc hoàn hảo với tôi.
ErickBest

Câu trả lời:


542

Giả sử id của iFrame của bạn là "targetFrame" và chức năng bạn muốn gọi là targetFunction():

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

Bạn cũng có thể truy cập khung bằng cách sử dụng window.framesthay vì document.getElementById.

// this option does not work in most of latest versions of chrome and Firefox
window.frames[0].frameElement.contentWindow.targetFunction(); 

3
hoạt động trong FF 7, Chrome 12, IE 8/9 và Safari (không chắc chắn về phiên bản này)
Darcy

3
Giải pháp này không hoạt động cho tiện ích của tôi, đây là mã của tôi document.getElementById('remote_iframe_0').contentWindow.my.create_element_gadg‌​et('verify_user');"remote_iframe_0 được tạo bởi chương trình máy chủ apind shindig nhưng window.parent.document.getElementById('remote_iframe_0').contentWindow.my.create_element_gadg‌​et('verify_user');"hoạt động
J Bourne

3
@Denty Henry, ý của bạn là "hàm jQuery?" jQuery là một thư viện JavaScript.
Joel Anair

8
@JellicleCat Đó là vì trang mẹ và iframe có các máy chủ khác nhau, làm cho nó trở thành khung có nguồn gốc chéo, như thông báo cho bạn biết. Miễn là giao thức, tên miền và cổng của trang mẹ và iframe khớp nhau, mọi thứ sẽ hoạt động tốt.
Đánh dấu Amery

1
Tôi đã thêm một ví dụ về cách sử dụng phương thức window.frames đã đề cập.
Elijah Lynn

145

Có một số quirks cần được nhận thức ở đây.

  1. HTMLIFrameElement.contentWindowcó lẽ là cách dễ dàng hơn, nhưng nó không hoàn toàn là một tài sản tiêu chuẩn và một số trình duyệt không hỗ trợ nó, hầu hết là các trình duyệt cũ hơn. Điều này là do tiêu chuẩn HTML Cấp 1 của DOM không có gì để nói về windowđối tượng.

  2. Bạn cũng có thể thử HTMLIFrameElement.contentDocument.defaultView, một vài trình duyệt cũ hơn cho phép nhưng IE thì không. Mặc dù vậy, tiêu chuẩn không nói rõ ràng rằng bạn lấy windowlại đối tượng, với cùng lý do như (1), nhưng bạn có thể chọn một vài phiên bản trình duyệt bổ sung tại đây nếu bạn quan tâm.

  3. window.frames['name']Trả về cửa sổ là giao diện lâu đời nhất và đáng tin cậy nhất. Nhưng sau đó bạn phải sử dụng một name="..."thuộc tính để có thể có được một khung theo tên, hơi xấu / không dùng nữa / chuyển tiếp. ( id="..."sẽ tốt hơn nhưng IE không như vậy.)

  4. window.frames[number]cũng rất đáng tin cậy, nhưng biết chỉ số đúng là mẹo. Bạn có thể thoát khỏi điều này, vd. nếu bạn biết bạn chỉ có một iframe trên trang.

  5. Hoàn toàn có thể iframe con chưa được tải hoặc có lỗi gì đó khiến nó không thể truy cập được. Bạn có thể thấy việc đảo ngược luồng truyền thông dễ dàng hơn: nghĩa là, iframe con thông báo cho window.parenttập lệnh của nó khi nó được tải xong và sẵn sàng để được gọi lại. Bằng cách chuyển một trong các đối tượng của chính nó (ví dụ: hàm gọi lại) cho tập lệnh gốc, phụ huynh đó có thể giao tiếp trực tiếp với tập lệnh trong iframe mà không phải lo lắng về HTMLIFrameEuity được liên kết với cái gì.


13
Công cụ tuyệt vời! Đặc biệt là đề xuất của bạn 5. đã cho thấy là vô cùng giá trị. Nó đã giải quyết cho tôi rất nhiều vấn đề đau đầu khi cố gắng truy cập các chức năng iFrame từ cha mẹ trong Chrome. Việc chuyển đối tượng chức năng ra khỏi iFrame khiến nó chạy như một nét quyến rũ trong Chrome, FF và thậm chí IE từ 9 xuống 7 và cũng rất dễ kiểm tra xem chức năng đã được tải chưa. Cảm ơn!
Jpsy

Số 3 hoạt động, nhưng tốt hơn nếu bạn làm như @le dorfier đưa nó vào bình luận của mình. Chú ý methode()một phần.
ErickBest

Cũng xin lưu ý rằng do các hạn chế bảo mật trong các trình duyệt hiện đại (đã kiểm tra FF29 và Chrome34), vấn đề rất lớn khi bạn thực hiện cuộc gọi chức năng của mình. Chỉ cần gọi hàm từ thẻ script hoặc $ (tài liệu). Đã () sẽ không hoạt động. Thay vào đó, hãy đặt cuộc gọi bên trong thẻ onload của cơ thể hoặc trong một neo <a href="javascript:doit();"> thực hiện </a> như được đề xuất ở mọi nơi (xem stackoverflow.com/questions/921387/ .) Truyền hàm gọi cho tập lệnh dưới dạng gọi lại cũng thường hoạt động.
Titusn

@chovy: Không, hãy xem phần làm rõ trong câu trả lời của Vivek về điều này.
bobince

66

Có thể gọi hàm JS từ cha mẹ iframe, nhưng chỉ khi cả cha mẹ và trang được tải trong iframecùng một tên miền abc.com và cả hai đều sử dụng cùng một giao thức, tức là cả hai đều bật http://hoặc https://.

Cuộc gọi sẽ thất bại trong các trường hợp được đề cập dưới đây:

  1. Trang mẹ và trang iframe là từ các miền khác nhau.
  2. Họ đang sử dụng các giao thức khác nhau, một giao thức là trên http: // và giao thức khác là trên https: //.

Bất kỳ cách giải quyết nào cho hạn chế này sẽ cực kỳ không an toàn.

Ví dụ, hãy tưởng tượng tôi đã đăng ký tên miền superwinningcontest.com và gửi liên kết đến email của mọi người. Khi họ tải lên trang chính, tôi có thể ẩn một vài iframegiây trong đó và đọc nguồn cấp dữ liệu Facebook của họ, kiểm tra các giao dịch gần đây của Amazon hoặc PayPal hoặc - nếu họ sử dụng dịch vụ không thực hiện bảo mật đầy đủ - chuyển tiền ra khỏi họ tài khoản. Đó là lý do tại sao JavaScript bị giới hạn trong cùng một miền và cùng giao thức.


6
Cách giải quyết là sử dụng en.wikipedia.org/wiki/Cross-document_messaging
Laramie

Có ai biết nếu một tên miền phụ khác sẽ gây ra lỗi này không? Tôi gặp khó khăn khi gỡ lỗi một vấn đề mà tôi tin là có liên quan đến vấn đề này - iframe đang gọi chức năng cửa sổ cha mẹ, nhưng cuộc gọi không xảy ra.
Jake

1
Lưu ý, nó cũng sẽ thất bại cho các số cổng khác nhau. tức là abc.com:80 = abc.com:8080!
prasanthv

31

Trong IFRAME, đặt chức năng của bạn ở chế độ công khai cho đối tượng cửa sổ:

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

Để truy cập từ trang mẹ, hãy sử dụng:

var iframe = document.getElementById("iframeId");
iframe.contentWindow.myFunction(args);

Câu trả lời chính xác. Không chắc chắn về quy ước ở đây. Tôi có một câu hỏi tiếp theo. Nếu tôi nên bắt đầu một câu hỏi mới, tôi sẽ. Tôi có một vấn đề. Khung nội tuyến của trang của tôi được điền một cách linh hoạt. Nó chứa một nút mà onclick()sự kiện chỉ đơn giản là gọi window.print(). Điều này in nội dung của iframehoàn hảo. Nhưng khi chức năng công khai của trang iframe gọi window.print()và nếu trang mẹ gọi chức năng công khai, nội dung của cha mẹ sẽ được in. Làm thế nào tôi nên mã này để cuộc gọi đến window.print()trong chức năng công cộng hoạt động như trong onclicksự kiện?
Karl

1
@Karl Bạn không muốn gọi onclick. Bạn muốn gọi iframe.contentWindow.print().
Tomalak

Thật không may, điều đó không làm việc. Có lẽ tôi nên bắt đầu một Câu hỏi mới và ghi lại mã của mình? Dù bằng cách nào, nội dung trang cha cũng in. document.getElementById('myFrame').contentWindow.print();' and the pubic function printContent () `(không phải là trình xử lý sự kiện oncick) mà các cuộc gọi window.print()được gọi theo cách này:document.getElementById('myFrame').contentWindow.printContent();
Karl

1
@Karl - Vâng, tốt nhất là bắt đầu một câu hỏi mới. Trừ khi cửa sổ cha nằm trên một miền khác với iframe. Sau đó, không có gì bạn cố gắng sẽ làm việc .
Tomalak

Những gì tôi báo cáo ở đây dường như là một vấn đề IE (thử nghiệm trong IE8). Không có vấn đề trong phiên bản hiện tại của Chrome. Vẫn chưa thử nghiệm các trình duyệt khác. Đối với những người quan tâm, hãy xem jsFiddle này (Tôi nghĩ rằng đây là một ví dụ hay về tải iframe động và gọi hàm công khai từ cha mẹ.)
Karl

20

Nếu mục tiêu của iFrame và tài liệu chứa trên một tên miền khác, các phương thức được đăng trước đó có thể không hoạt động, nhưng có một giải pháp:

Ví dụ: nếu tài liệu A chứa phần tử iframe chứa tài liệu B và tập lệnh trong tài liệu A gọi postMessage () trên đối tượng Window của tài liệu B, thì một sự kiện thông báo sẽ được kích hoạt trên đối tượng đó, được đánh dấu là bắt nguồn từ Window of tài liệu A. Kịch bản trong tài liệu A có thể trông giống như:

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

Để đăng ký một trình xử lý sự kiện cho các sự kiện đến, tập lệnh sẽ sử dụng addEventListener () (hoặc các cơ chế tương tự). Ví dụ: tập lệnh trong tài liệu B có thể trông như sau:

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

Tập lệnh này trước tiên kiểm tra tên miền là miền dự kiến, sau đó xem tin nhắn, nó sẽ hiển thị cho người dùng hoặc trả lời bằng cách gửi tin nhắn trở lại tài liệu đã gửi tin nhắn ở vị trí đầu tiên.

thông qua http://dev.w3.org/html5/postmsg/#web-messaging


1
easyXDM cung cấp một bản tóm tắt về PostMessage cho các trình duyệt cũ hơn (bao gồm dự phòng Flash (LocalConnection) cho những con khủng long cứng đầu).
JonnyReeves

1
thật tuyệt ... sau khi thử câu trả lời trên cuối cùng tôi cũng nhận được câu trả lời của bạn, cảm ơn!
vkGunasekaran

9

Chỉ để ghi lại, tôi đã gặp vấn đề tương tự ngày hôm nay nhưng lần này trang được nhúng vào một đối tượng, không phải iframe (vì đó là tài liệu XHTML 1.1). Đây là cách nó hoạt động với các đối tượng:

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

(xin lỗi vì ngắt dòng xấu xí, không vừa trong một dòng)


8

IFRAME nên có trong frames[]bộ sưu tập. Sử dụng một cái gì đó như

frames['iframeid'].method();

Câu trả lời này chắc chắn có thể sử dụng thêm một số thông tin, vì chắc chắn có một số cân nhắc khác. Đối với một người, tôi giả sử nhà phát triển cần tạo methodmột thuộc tính của windowđối tượng iframe .
matty

8

Quirksmode đã có một bài về điều này .

Vì trang hiện đã bị hỏng và chỉ có thể truy cập qua archive.org, tôi đã sao chép nó ở đây:

IFrames

Trên trang này tôi cung cấp một tổng quan ngắn về việc truy cập iframe từ trang họ đang truy cập. Không ngạc nhiên, có một số cân nhắc trình duyệt.

Một iframe là một khung nội tuyến, một khung, trong khi chứa một trang hoàn toàn riêng biệt với URL riêng của nó, dù sao cũng được đặt trong một trang HTML khác. Điều này mang lại khả năng rất tốt trong thiết kế web. Vấn đề là truy cập iframe, ví dụ để tải một trang mới vào đó. Trang này giải thích làm thế nào để làm điều đó.

Khung hay vật?

Câu hỏi cơ bản là liệu iframe được xem như một khung hay là một đối tượng.

  • Như đã giải thích trên trang Giới thiệu về khung , nếu bạn sử dụng khung, trình duyệt sẽ tạo cấu trúc phân cấp khung cho bạn ( top.frames[1].frames[2]và như vậy). Liệu iframe có phù hợp với hệ thống phân cấp khung này không?
  • Hay trình duyệt xem iframe chỉ là một đối tượng khác, một đối tượng tình cờ có thuộc tính src? Trong trường hợp đó, chúng tôi phải sử dụng một lệnh gọi DOM tiêu chuẩn (muốn document.getElementById('theiframe'))truy cập nó. Trong các trình duyệt chung cho phép cả hai chế độ xem trên các iframe 'thực' (được mã hóa cứng), nhưng các iframe được tạo không thể được truy cập dưới dạng khung.

Thuộc tính NAME

Quy tắc quan trọng nhất là đưa ra bất kỳ iframe nào bạn tạo namethuộc tính, ngay cả khi bạn cũng sử dụng một id.

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

Hầu hết các trình duyệt cần namethuộc tính để làm cho phần iframe của phân cấp khung. Một số trình duyệt (đặc biệt là Mozilla) cần idlàm cho iframe có thể truy cập dưới dạng đối tượng. Bằng cách gán cả hai thuộc tính cho iframe, bạn giữ cho các tùy chọn của mình mở. Nhưng namequan trọng hơn nhiều id.

Truy cập

Hoặc bạn truy cập iframe dưới dạng đối tượng và thay đổi đối tượng srchoặc bạn truy cập iframe dưới dạng khung và thay đổi khung location.href.

document.getEuityById ('iframe_id'). src = 'newpage.html'; khung ['iframe_name']. location.href = 'newpage.html'; Cú pháp khung là hơi thích hợp hơn vì Opera 6 hỗ trợ nó nhưng không phải cú pháp đối tượng.

Truy cập iframe

Vì vậy, để có trải nghiệm trình duyệt chéo hoàn chỉnh, bạn nên đặt tên iframe và sử dụng

frames['testiframe'].location.href

cú pháp. Theo tôi biết điều này luôn luôn hoạt động.

Truy cập tài liệu

Truy cập tài liệu bên trong iframe khá đơn giản, miễn là bạn sử dụng namethuộc tính. Để đếm số lượng liên kết trong tài liệu trong iframe, hãy làm frames['testiframe'].document.links.length.

Tạo iframe

Tuy nhiên, khi bạn tạo iframe thông qua W3C DOM , iframe không được nhập ngay vào framesmảng và frames['testiframe'].location.hrefcú pháp sẽ không hoạt động ngay lập tức. Trình duyệt cần một ít thời gian trước khi iframe xuất hiện trong mảng, thời gian trong đó không có tập lệnh nào có thể chạy.

Các document.getElementById('testiframe').srccú pháp hoạt động tốt trong mọi hoàn cảnh.

Các targetthuộc tính của một liên kết không hoạt động hoặc với iframe tạo ra, ngoại trừ trong Opera, mặc dù tôi đã tôi tạo iframe cả namevà một id.

Việc thiếu targethỗ trợ có nghĩa là bạn phải sử dụng JavaScript để thay đổi nội dung của iframe được tạo, nhưng vì dù sao bạn cũng cần JavaScript để tạo nó ở nơi đầu tiên, nên tôi không thấy vấn đề này nhiều.

Kích thước văn bản trong iframe

Một lỗi chỉ tò mò của Explorer 6:

Khi bạn thay đổi kích thước văn bản thông qua menu Xem, kích thước văn bản trong iframe được thay đổi chính xác. Tuy nhiên, trình duyệt này không thay đổi ngắt dòng trong văn bản gốc, do đó một phần của văn bản có thể trở nên vô hình hoặc ngắt dòng có thể xảy ra trong khi dòng vẫn có thể giữ một từ khác.


5
Liên kết đó dường như bị phá vỡ.
zaphod

1
Và để làm cho vấn đề tồi tệ hơn, tôi không thể tìm thấy trang liên quan ở đâu trên quirksmode.
stricjux


7

Tôi tìm thấy một giải pháp khá thanh lịch.

Như bạn đã nói, việc thực thi mã nằm trên tài liệu gốc khá dễ dàng. Và đó là cơ sở của mã của tôi, làm ngược lại.

Khi iframe của tôi tải, tôi gọi một hàm nằm trên tài liệu gốc, chuyển qua làm đối số tham chiếu đến hàm cục bộ, nằm trong tài liệu của iframe. Tài liệu gốc hiện có quyền truy cập trực tiếp vào chức năng của iframe thông qua tham chiếu này.

Thí dụ:

Về cha mẹ:

function tunnel(fn) {
    fn();
}

Trên iframe:

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

parent.tunnel(myFunction);

Khi iframe tải, nó sẽ gọi Parent.tunnel (YourFunctionReference), sẽ thực thi chức năng nhận được trong tham số.

Điều đó đơn giản, không phải đối phó với tất cả các phương pháp phi tiêu chuẩn từ các trình duyệt khác nhau.


Xin chào, bạn có thể xác nhận điều này "Đơn giản mà không phải đối phó với tất cả các phương pháp phi tiêu chuẩn từ các trình duyệt khác nhau."?
Milche P Parent

1
Tôi sử dụng phương pháp này trên các dự án khác nhau, dường như hoạt động rất tốt. Cá nhân tôi đã không kiểm tra tất cả các trình duyệt đang lưu hành, nhưng tôi cũng không bao giờ nhận được báo cáo lỗi về nó.
Julien L

Hoạt động như một bùa mê, IE 11, Chrome ~ 50.
Augustas

1
Đoán đó không phải là tên miền chéo .. Phải không?
Tushar Shukla

@TusharShukla Thật không may, không phải vậy.
jeff-h

6

Một số câu trả lời này không giải quyết được vấn đề CORS hoặc không làm rõ nơi bạn đặt đoạn mã để thực hiện giao tiếp.

Dưới đây là một ví dụ cụ thể. Nói rằng tôi muốn nhấp vào một nút trên trang mẹ và yêu cầu một cái gì đó bên trong iframe. Đây là cách tôi sẽ làm điều đó.

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

Để đi theo hướng khác (nhấp vào nút bên trong iframe để gọi phương thức bên ngoài iframe), chỉ cần chuyển đổi nơi đoạn mã sống và thay đổi điều này:

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

đến đây:

window.parent.postMessage('foo','*')

4

Tiếp tục với câu trả lời của JoelAnair :

Để mạnh mẽ hơn, sử dụng như sau:

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

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

Làm việc như quyến rũ :)


3

Câu trả lời của Folowing Nitin Bansal

và để mạnh mẽ hơn nữa:

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

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

var frame_win = getIframeWindow(el);

if (frame_win) {
  frame_win.targetFunction();
  ...
}
...

2
       $("#myframe").load(function() {
            alert("loaded");
        });

2

Những điều tương tự nhưng cách dễ dàng hơn một chút sẽ là Cách làm mới trang mẹ từ trang trong iframe . Chỉ cần gọi chức năng của trang mẹ để gọi chức năng javascript để tải lại trang:

window.location.reload();

Hoặc làm điều này trực tiếp từ trang trong iframe:

window.parent.location.reload();

Cả hai công trình.



1

Nếu bạn muốn gọi hàm JavaScript trên Parent từ iframe được tạo bởi một hộp bóng tối khác hoặc hộp đèn.

Bạn nên cố gắng sử dụng windowđối tượng và gọi hàm cha:

window.parent.targetFunction();

Tôi tin rằng OP đang cố gắng đi theo hướng khác
CommandZ

-3

Sử dụng theo sau để gọi chức năng của một khung trong trang mẹ

parent.document.getElementById('frameid').contentWindow.somefunction()
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.