Вызов JavaScript-кода в iframe с родительской страницы - PullRequest
581 голосов
/ 30 октября 2008

По сути, у меня есть iframe, встроенный в страницу, а iframe имеет некоторые JavaScript подпрограммы, которые мне нужно вызвать с родительской страницы.

Теперь все наоборот, просто вам нужно позвонить parent.functionName(), но, к сожалению, мне нужно прямо противоположное этому.

Обратите внимание, что моя проблема не в изменении URL-адреса источника iframe, а в вызове функции, определенной в iframe.

Ответы [ 17 ]

522 голосов
/ 30 октября 2008

Предположим, что идентификатор вашего iFrame равен "targetFrame", а функция, которую вы хотите вызвать, - targetFunction():

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

Вы также можете получить доступ к кадру, используя window.frames вместо document.getElementById.

// this option does not work in most of latest versions of chrome and Firefox
window.frames[0].frameElement.contentWindow.targetFunction(); 
142 голосов
/ 30 октября 2008

Здесь есть некоторые причуды, о которых нужно знать.

  1. HTMLIFrameElement.contentWindow, вероятно, более простой способ, но это не совсем стандартное свойство, и некоторые браузеры не поддерживают его, в основном старые. Это связано с тем, что стандарт HTML DOM Level 1 не имеет ничего общего с объектом window.

  2. Вы также можете попробовать HTMLIFrameElement.contentDocument.defaultView, что разрешено парой старых браузеров, но IE - нет. Несмотря на это, в стандарте прямо не говорится, что вы возвращаете объект window по той же причине, что и (1), но вы можете выбрать здесь несколько дополнительных версий браузера, если вам это нужно.

  3. window.frames['name'] возврат окна является самым старым и, следовательно, наиболее надежным интерфейсом. Но тогда вам нужно использовать атрибут name="...", чтобы получить кадр по имени, что немного уродливо / устарело / transitional. (id="..." было бы лучше, но IE это не нравится.)

  4. window.frames[number] также очень надежен, но знание правильного индекса - хитрость. Вы можете сойти с рук, например. если вы знаете, что на странице есть только один iframe.

  5. Вполне возможно, что дочерний элемент iframe еще не загружен или что-то пошло не так, чтобы сделать его недоступным. Возможно, вам будет проще перевернуть поток сообщений: то есть, чтобы дочерний iframe уведомил свой скрипт window.parent, когда он закончил загрузку и был готов к обратному вызову. Передав один из своих собственных объектов (например, функцию обратного вызова) родительскому сценарию, этот родительский элемент может затем напрямую связаться со сценарием в iframe, не беспокоясь о том, с каким HTMLIFrameElement он связан.

64 голосов
/ 18 апреля 2010

Вызов родительской функции JS из iframe возможен, но только если родитель и страница, загруженная в iframe, находятся в одном домене, т.е. abc.com, и оба используют один и тот же протокол, т.е. оба включены http:// или https://.

Вызов не будет выполнен в следующих случаях:

  1. Родительская страница и страница iframe принадлежат другому домену.
  2. Они используют разные протоколы, один на http: //, а другой на https: //.

Любой способ обойти это ограничение будет крайне небезопасным.

Например, представьте, что я зарегистрировал домен superwinningcontest.com и отправил ссылки на электронные письма людей. Когда они загрузили главную страницу, я мог спрятать там несколько iframe и прочитать их ленту в Facebook, проверить последние транзакции Amazon или PayPal или - если они использовали службу, которая не обеспечивала достаточную безопасность - перевести деньги из их счетов. Вот почему JavaScript ограничен тем же доменом и тем же протоколом.

30 голосов
/ 30 октября 2008

В IFRAME сделайте вашу функцию общедоступной для объекта window:

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

Для доступа с родительской страницы используйте это:

var iframe = document.getElementById("iframeId");
iframe.contentWindow.myFunction(args);
16 голосов
/ 29 октября 2011

Если iFrame и содержащий его документ находятся в другом домене, ранее опубликованные методы могут не работать, но есть решение:

Например, если документ A содержит элемент iframe, который содержит документ B, а сценарий в документе A вызывает postMessage () для объекта Window документа B, то для этого объекта будет сгенерировано событие сообщения, помеченное как происходящее из Окно документа A. Сценарий в документе A может выглядеть следующим образом:

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

Чтобы зарегистрировать обработчик событий для входящих событий, скрипт будет использовать addEventListener () (или аналогичные механизмы). Например, сценарий в документе B может выглядеть так:

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

Этот сценарий сначала проверяет, является ли домен ожидаемым, а затем просматривает сообщение, которое он либо отображает пользователю, либо отвечает, отправляя сообщение обратно в документ, который отправил сообщение в первую очередь.

через http://dev.w3.org/html5/postmsg/#web-messaging

9 голосов
/ 30 июня 2009

Просто для записи, я столкнулся с той же проблемой сегодня, но на этот раз страница была встроена в объект, а не в iframe (так как это был документ XHTML 1.1). Вот как это работает с объектами:

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

(извините за уродливые разрывы строк, не поместились ни в одну строку)

8 голосов
/ 30 октября 2008

IFRAME должен быть в коллекции frames[]. Используйте что-то вроде

frames['iframeid'].method();
8 голосов
/ 30 октября 2008

Quirksmode имеет сообщение на этом .

Поскольку страница теперь сломана и доступна только через archive.org, я воспроизвел ее здесь:

IFrames

На этой странице я даю краткий обзор доступа к фреймам со страницы, на которой они находятся. Не удивительно, что есть некоторые соображения по поводу браузера.

iframe - это встроенный фрейм, фрейм, который, хотя и содержит совершенно отдельную страницу со своим собственным URL, тем не менее размещается внутри другой HTML-страницы. Это дает очень хорошие возможности в веб-дизайне. Проблема в том, чтобы получить доступ к iframe, например, чтобы загрузить в него новую страницу. На этой странице объясняется, как это сделать.

Рамка или объект?

Основной вопрос заключается в том, рассматривается ли iframe как рамка или как объект.

  • Как объяснено на страницах Введение в фреймы , если вы используете фреймы, браузер создает для вас иерархию фреймов (top.frames[1].frames[2] и т. П.). Вписывается ли iframe в эту иерархию фреймов?
  • Или браузер видит в iframe просто еще один объект, объект, у которого есть свойство src? В этом случае мы должны использовать стандартный вызов DOM (например, document.getElementById('theiframe')) для доступа к нему. В общем, браузеры допускают оба представления «реальных» (жестко закодированных) фреймов, но сгенерированные фреймы не могут быть доступны как фреймы.

атрибут NAME

Самое важное правило - указывать для любого iframe атрибут name, даже если вы также используете id.

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

Большинству браузеров необходим атрибут name, чтобы сделать iframe частью иерархии фреймов. Некоторым браузерам (особенно Mozilla) требуется id, чтобы сделать iframe доступным как объект. Присваивая оба атрибута iframe, вы сохраняете свои опции открытыми. Но name гораздо важнее, чем id.

Доступ

Либо вы получаете доступ к iframe как объект и изменяете его src, либо вы получаете доступ к iframe как фрейм и изменяете его location.href.

document.getElementById ('iframe_id'). Src = 'newpage.html'; frames ['iframe_name']. location.href = 'newpage.html'; Синтаксис фрейма немного предпочтительнее, потому что Opera 6 поддерживает его, но не синтаксис объекта.

Доступ к iframe

Так что для полного кросс-браузерного опыта вы должны дать iframe имя и использовать

frames['testiframe'].location.href

синтаксис. Насколько я знаю, это всегда работает.

Доступ к документу

Доступ к документу внутри iframe довольно прост при условии, что вы используете атрибут name. Чтобы подсчитать количество ссылок в документе в iframe, выполните frames['testiframe'].document.links.length.

Созданные фреймы iframe

Когда вы генерируете iframe с помощью W3C DOM , iframe не сразу вводится в массив frames, и синтаксис frames['testiframe'].location.href не будет работать сразу. Браузеру нужно немного времени, прежде чем iframe появится в массиве, время, в течение которого ни один скрипт не может быть запущен.

Синтаксис document.getElementById('testiframe').src отлично работает при любых обстоятельствах.

Атрибут target ссылки не работает ни с сгенерированными iframe, кроме как в Opera, хотя я дал своему сгенерированному iframe и name, и id.

Отсутствие поддержки target означает, что вы должны использовать JavaScript для изменения содержимого сгенерированного iframe, но так как вам все равно нужен JavaScript для его генерации, я не вижу в этом особой проблемы .

Размер текста в фреймах

Любопытная ошибка только в Explorer 6:

При изменении размера текста через меню «Вид» размеры текста в фреймах корректно изменяются. Однако этот браузер не изменяет разрывы строк в исходном тексте, поэтому часть текста может стать невидимой или же могут произойти разрывы строк, в то время как строка может содержать еще одно слово.

7 голосов
/ 08 февраля 2014

Я нашел довольно элегантное решение.

Как вы сказали, довольно легко выполнить код, расположенный в родительском документе. И это основа моего кода, сделайте прямо противоположное.

Когда мой iframe загружается, я вызываю функцию, расположенную в родительском документе, передавая в качестве аргумента ссылку на локальную функцию, расположенную в документе iframe. Родительский документ теперь имеет прямой доступ к функции iframe через эту ссылку.

Пример:

На родителя:

function tunnel(fn) {
    fn();
}

На фрейме:

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

parent.tunnel(myFunction);

Когда загружается iframe, он вызывает parent.tunnel (YourFunctionReference), который будет выполнять функцию, полученную в параметре.

Это просто, без необходимости разбираться со всеми нестандартными методами из разных браузеров.

4 голосов
/ 07 октября 2017

Некоторые из этих ответов не касаются проблемы CORS или не дают четкого представления о том, где вы размещаете фрагменты кода, чтобы сделать возможным общение.

Вот конкретный пример. Скажем, я хочу нажать кнопку на родительской странице, и сделать это внутри фрейма. Вот как бы я это сделал.

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

Чтобы перейти в другом направлении (нажмите кнопку внутри iframe, чтобы вызвать метод за пределами iframe), просто переключитесь, где находится фрагмент кода, и измените это:

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

к этому:

window.parent.postMessage('foo','*')
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...