Вызов JavaScript-кода в iframe с родительской страницы

615

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

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

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

Тамас Чинеге
источник
42
Ваше замечание о parent.fuctioName () решило мою проблему вызова функции в HTML-файле родителя iframe. Thanx!
CyberFonic
1
Обратите внимание, что при отладке вы можете делать такие вещи, как window.location.href или parent.location.href, чтобы просмотреть URL-адрес iframe, если вы хотите убедиться, что у вас есть ссылка на искомый iframe.
AaronLS
Смотрите ответ @le dorfier ... отлично сработал для меня.
ErickBest

Ответы:

542

Предположим, что идентификатор вашего 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(); 
Джоэл Анаир
источник
3
работает в FF 7, Chrome 12, IE 8/9 и Safari (не уверен в этой версии)
Darcy
3
Это решение не работает для моего гаджета, вот мой код document.getElementById('remote_iframe_0').contentWindow.my.create_element_gadg‌​et('verify_user');"remote_iframe_0 создан программно сервером Apache Shindig, но window.parent.document.getElementById('remote_iframe_0').contentWindow.my.create_element_gadg‌​et('verify_user');"работает
J Bourne
3
@ Грязный Генри, что ты имеешь в виду под "функцией jQuery?" jQuery - это библиотека JavaScript.
Джоэл Анаир
8
@JellicleCat Это потому, что родительская страница и iframe имеют разные хосты, что делает его рамкой перекрестного происхождения, как говорится в сообщении. Пока протокол, домен и порт родительской страницы и iframe совпадают, все будет работать нормально.
Марк Эмери
1
Я добавил пример использования упомянутого метода window.frames.
Элайджа Линн
145

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

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

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

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

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

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

bobince
источник
13
Качественный товар! Особенно ваше предложение 5. оказалось чрезвычайно ценным. Это решило мне много головной боли при попытке получить доступ к функциям iFrame от родителя в Chrome. Передача объекта функции из iFrame заставила его работать как Charm в Chrome, FF и даже IE с 9 до 7, а также позволила очень легко проверить, загружена ли функция уже. Спасибо!
Jpsy
Номер 3 работает, но лучше, если вы сделаете это, как @le dorfier, в комментариях. Обратите внимание на methode()часть.
ErickBest
Также обратите внимание, что из-за ограничений безопасности в современных браузерах (протестированных FF29 и Chrome34) очень важно, где вы выполняете вызов функции. Просто вызов функции из тега скрипта или $ (document) .ready () не сработает. Вместо того, чтобы поместить вызов в OnLoad тег тела, или в якорной <a href="javascript:doit();"> сделать это </a> как предложено elswhere (см stackoverflow.com/questions/921387/... ). Передача вызова функции в сценарий в качестве обратного вызова также обычно работает.
titusn
@chovy: Нет, см. разъяснение в ответе Вивека по этому поводу.
bobince
66

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

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

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

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

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

Вивек Сингх Чаухан
источник
6
Обходной путь должен использовать красивый и опасный en.wikipedia.org/wiki/Cross-document_messaging
Ларами
Кто-нибудь знает, если другой поддомен приведет к сбою? Мне трудно отладить проблему, которая, по моему мнению, связана с этим - iframe вызывает функцию родительского окна, но этот вызов не происходит.
Джейк
1
Обратите внимание, что он также не будет работать для разных номеров портов. то есть abc.com:80! = abc.com:8080
prasanthv
31

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

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

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

var iframe = document.getElementById("iframeId");
iframe.contentWindow.myFunction(args);
Томалак
источник
Отличный ответ. Не уверен в соглашении здесь. У меня есть дополнительный вопрос. Если я должен начать новый вопрос, я буду. У меня есть одна проблема. Iframe моей страницы заполняется динамически. Он содержит кнопку, onclick()событие которой просто вызывает window.print(). Это выводит содержимое из iframeпрекрасно. Но когда вызывается публичная функция страницы iframe window.print()и если родительская страница вызывает публичную функцию, печатается содержимое родительской страницы. Как я должен кодировать это так, чтобы вызов window.print()в публичной функции вел себя так же, как в onclickсобытии?
Карл
1
@ Карл Ты не хочешь звонить onclick. Вы хотите позвонить iframe.contentWindow.print().
Томалак
К сожалению, это не работает. Может быть, я должен начать новый вопрос и документировать свой код? В любом случае, содержимое родительской страницы тоже печатается. document.getElementById('myFrame').contentWindow.print();' and the pubic function printContent () `(не обработчик события oncick), вызов которого window.print()был вызван так:document.getElementById('myFrame').contentWindow.printContent();
Карл
1
@ Карл - Да, тогда лучше начать новый вопрос. Если родительское окно не находится в другом домене, чем iframe. Тогда ничто из того, что вы попробуете, никогда не сработает .
Томалак
То, что я здесь сообщил, похоже, является проблемой IE (тестирование в IE8). Нет проблем в текущей версии Chrome. Однако пока не нужно тестировать другие браузеры. Для тех, кто заинтересован, посмотрите этот jsFiddle (я думаю, что это хороший пример динамической загрузки iframe и вызова публичной функции от родителя.)
Карл
20

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

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

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

19greg96
источник
1
easyXDM предоставляет абстракцию над PostMessage для старых браузеров (включая запасной вариант Flash (LocalConnection) для упрямых динозавров).
JonnyReeves
1
это здорово ... после попытки выше ответов, наконец, я получил ваш ответ, спасибо!
vkGunasekaran
9

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

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

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

Тамас Чинеге
источник
8

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

frames['iframeid'].method();
dkretz
источник
Этот ответ, безусловно, может использовать больше информации, поскольку, безусловно, есть и другие соображения. Во-первых, я предполагаю, что разработчик должен создать methodсвойство объекта iframe window.
Мэтти
8

У 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 имя и использовать

frames['testiframe'].location.href

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

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

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

Сгенерированные фреймы

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

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

targetАтрибут ссылка не работает , либо с сгенерированных фреймов, за исключением Opera, даже если я дал сгенерированный IFrame как на nameи id.

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

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

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

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

activout.se
источник
5
Эта ссылка, кажется, не работает.
Заход
1
И что еще хуже, я не могу найти соответствующую страницу нигде в quirksmode.
stricjux
7

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

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

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

Пример:

На родителя:

function tunnel(fn) {
    fn();
}

На фрейме:

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

parent.tunnel(myFunction);

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

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

Жюльен Л
источник
Привет, вы можете подтвердить это «Это так просто, не имея дело со всеми нестандартными методами из разных браузеров»?
Мильче Патерн
1
Я использую этот метод в различных проектах, кажется, работает очень хорошо. Я лично не проверял все браузеры в обращении, но я никогда не получал сообщения об ошибках на нем.
Жюльен Л
Работает как шарм, IE 11, Chrome ~ 50.
Август
1
Угадайте, что это не междоменный .. Не так ли?
Тушар Шукла
@TusharShukla К сожалению, нет, это не так.
Джефф-ч
6

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

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

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','*')
кибернетический
источник
4

Продолжая с ответом JoelAnair :

Для большей надежности используйте следующее:

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

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

Работал как шарм :)

Нитин Бансал
источник
3

Folowing ответ Нитин Бансал

и для еще большей надежности:

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");
        });
GeverGever
источник
2

То же самое, но немного проще: как обновить родительскую страницу со страницы внутри iframe . Просто вызовите функцию родительской страницы, чтобы вызвать функцию javascript для перезагрузки страницы:

window.location.reload();

Или сделать это прямо со страницы в iframe:

window.parent.location.reload();

Обе работы.

сангам
источник
1

Попробуй просто parent.myfunction()

Саид Зебардаст
источник
stackoverflow.com/questions/12960494/…
Мильче Патерн
@Saeid ОП хочет вызвать функцию в iframe из родительского окна, а не наоборот.
Жан-Франсуа Бошан
1

Если вы хотите вызвать функцию JavaScript в Parent из iframe, сгенерированного другой функцией ex shadowbox или lightbox.

Вы должны попытаться использовать windowобъект и вызвать родительскую функцию:

window.parent.targetFunction();
Santhanakumar
источник
Я считаю, что ОП пытается пойти в другом направлении
CommandZ
-3

Используйте следующее для вызова функции фрейма на родительской странице

parent.document.getElementById('frameid').contentWindow.somefunction()
Сэнди
источник