Викликання JavaScript-коду в iframe з батьківської сторінки


615

В основному, я iframeвбудований у сторінку, а у iframeдеяких є JavaScript мене підпрограми , до яких потрібно звернутися з батьківської сторінки.

Зараз навпаки досить просто, як вам потрібно лише дзвонити parent.functionName(), але, на жаль, мені потрібно саме протилежне.

Зверніть увагу , що моя проблема не змінює вихідний URL з iframe, але виклику функції , визначеної в iframe.


42
Ваша примітка про parent.fuctioName () вирішила мою проблему виклику функції в батьківському HTML-файлі iframe. Дякую!
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 (не впевнений у цій версії)
Дарсі

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
@Dirty Henry, що ти маєш на увазі під функцією jQuery? jQuery - бібліотека JavaScript.
Joel Anair

8
@JellicleCat Це тому, що батьківська сторінка та iframe мають різні хости, що робить її рамкою з перехресним походженням, як повідомляє повідомлення. Поки протокол, домен та порт батьківської сторінки та рамка рамки збігаються, все буде добре.
Марк Амері

1
Я додав приклад того, як використовувати згаданий метод window.frames.
Ілля Лінн

145

Тут є деякі химерності, про які слід знати.

  1. HTMLIFrameElement.contentWindowце, мабуть, простіший спосіб, але це не зовсім стандартна властивість, і деякі браузери не підтримують її, в основному, старші. Це тому, що стандарт HTML DOM рівня 1 нічого не може сказати про windowоб'єкт.

  2. Ви також можете спробувати HTMLIFrameElement.contentDocument.defaultView , що дозволяє кілька старих браузерів, але IE не дозволяє. Тим не менш, стандарт прямо не говорить про те, що ви отримуєте windowоб'єкт назад з тієї ж причини, що і (1), але ви можете підібрати тут кілька додаткових версій браузера, якщо вам все одно.

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

  4. window.frames[number]це також дуже надійно, але знати правильний показник - фокус. Ви можете піти від цього, наприклад. якщо ви знаєте, що у вас є лише одна рамка на сторінці.

  5. Цілком можливо, що дитина iframe ще не завантажив, або щось інше пішло не так, щоб зробити її недоступною. Можливо, вам буде легше змінити потік комунікацій: тобто дозволити дочірньому iframe повідомляти про його window.parentсценарій, коли він закінчиться завантажений і він готовий до передзвонення. Передаючи один із власних об'єктів (наприклад, функцію зворотного виклику) до батьківського сценарію, той батько може потім безпосередньо спілкуватися зі сценарієм у iframe, не турбуючись про те, з яким HTMLIFrameElement він пов'язаний.


13
Чудові речі! Особливо ваша пропозиція 5. виявилася надзвичайно цінною. Це вирішило мені багато головних болів при спробі отримати доступ до функцій iFrame від батьків у Chrome. Передача об’єкта функції з iFrame змусила його працювати як шарм у Chrome, FF та навіть IE з 9 до 7, а також дуже легко перевірити, чи функція вже завантажена. Дякую!
Jpsy

Номер 3 працює, але краще, якщо ви зробите це так, як @le dorfier, помістіть це у своєму коментарі. Зауважте methode()деталь.
ErickBest

Зауважте також, що через обмеження безпеки в сучасних браузерах (тестовані FF29 та Chrome34) дуже важливо, де ви здійснюєте функціональний дзвінок. Просто виклик функції з тегу сценарію або $ (документ) .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.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
@Karl Вам не хочеться телефонувати onclick. Ви хочете зателефонувати iframe.contentWindow.print().
Томалак

На жаль, це не працює. Можливо, я повинен почати нове запитання та документувати свій код? Так чи інакше, друкується вміст батьківської сторінки. document.getElementById('myFrame').contentWindow.print();' and the pubic function printContent () `(не обробник події oncick), який window.print()викликав, називався так:document.getElementById('myFrame').contentWindow.printContent();
Карл

1
@Karl - Так, тоді найкраще почати нове питання. Якщо батьківське вікно не знаходиться в іншому домені, ніж рамка iframe. Тоді нічого, що ви спробуєте, ніколи не вийде .
Томалак

Те, про що я повідомив тут, здається, є проблемою IE (тестування в IE8). У поточній версії Chrome немає проблем. Однак ще не потрібно тестувати інші браузери. Для зацікавлених дивіться цей jsFiddle (я думаю, це хороший приклад динамічного завантаження iframe та виклику публічної функції від батьків.)
Карл

20

Якщо ціль 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


1
easyXDM забезпечує абстракцію по PostMessage для старих браузерів (включаючи спалах Flash (LocalConnection) для впертих динозаврів).
JonnyReeves

1
це чудово ... після спробу вищезазначених відповідей нарешті я отримав вашу відповідь, дякую!
vkGunasekaran

9

Тільки для запису я зіткнувся з тим самим випуском сьогодні, але цього разу сторінка була вбудована в об’єкт, а не в рамку (оскільки це був XHTML 1.1 документ). Ось як це працює з об’єктами:

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

(вибачте за некрасиві перерви рядка, не вписувався в один рядок)


8

IFRAME має бути в frames[]колекції. Використовуйте щось подібне

frames['iframeid'].method();

Ця відповідь, безумовно, може використовувати більше інформації, оскільки, безумовно, є деякі інші міркування. Для одного, я припускаю, що розробник повинен створити methodвластивість об'єкта iframe window.
матовий

8

Quirksmode мав пост про це .

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

IFrames

На цій сторінці я даю короткий огляд доступу до 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, ви зберігаєте свої параметри відкритими. Але nameнабагато важливіше, ніж id.

Доступ

Або ви отримуєте доступ до iframe як об'єкт і змінюєте його, srcабо ви отримуєте доступ до iframe як фрейм і змінюєте йогоlocation.href .

document.getElementById ('iframe_id'). src = 'newpage.html'; фрейми ['iframe_name']. location.href = 'newpage.html'; Синтаксис кадру трохи кращий, тому що Opera 6 підтримує його, але не синтаксис об'єкта.

Доступ до iframe

Отже, для повного крос-браузерного досвіду ви повинні дати ім'я iframe і скористатися

frames['testiframe'].location.href

синтаксис. Наскільки я знаю, це завжди працює.

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

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

Породжені рамки кадрів

Коли ви генеруєте iframe через W3C DOM, iframe не відразу вводиться в framesмасив, однак іframes['testiframe'].location.href синтаксис не працюватиме одразу. Браузеру потрібно трохи часу, перш ніж в масиві з’явиться iframe, час, протягом якого жоден сценарій не може працювати.

The document.getElementById('testiframe').srcСинтаксис відмінно працює в будь-яких обставинах.

targetАтрибут посилання не працює , або з згенерованих фреймів, за винятком Opera, навіть якщо я дав згенерований IFrame як на nameіid .

Нестача target підтримки означає, що ви повинні використовувати JavaScript для зміни вмісту створеного iframe, але оскільки для його створення в будь-якому випадку вам потрібен JavaScript, я не вважаю це великою проблемою.

Розмір тексту в рамках

Цікавий помилка Explorer 6:

Коли ви змінюєте розмір тексту через меню «Перегляд», розміри тексту в рамках правильно змінюються. Однак цей веб-переглядач не змінює розривів рядків у початковому тексті, тому частина тексту може стати невидимою, або можуть виникнути розриви рядків, поки рядок все ще може містити інше слово.


5
Здається, це посилання розірвано.
зафод

1
І що ще гірше, я не можу знайти пов'язану сторінку ніде на quirksmode.
stricjux


7

Я знайшов досить елегантне рішення.

Як ви вже говорили, виконувати код, розташований на батьківському документі, досить просто. І це основа мого коду, робити саме навпаки.

Коли мій iframe завантажується, я викликаю функцію, розташовану на батьківському документі, передаючи в якості аргументу посилання на локальну функцію, розташовану в документі iframe. Тепер батьківський документ має прямий доступ до функції iframe через цю посилання.

Приклад:

Про батьків:

function tunnel(fn) {
    fn();
}

На iframe:

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

parent.tunnel(myFunction);

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

Це просто, не маючи справу з усіма нестандартними методами різних браузерів.


Привіт, чи можете ви підтвердити це "Це просто, без того, щоб мати справу з усіма нестандартними методами у різних браузерах."
Мілче Патерн

1
Я використовую цей метод у різних проектах, здається, працює дуже добре. Я особисто не перевіряв усі браузери в обігу, але жодного разу не отримував повідомлення про помилки.
Жульєн Л

Працює як шарм, IE 11, Chrome ~ 50.
Август

1
Здогадаєтесь, це не міждомен. Це?
Тушар Шукла

@TusharShukla На жаль, ні.
jeff-h

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

Прослідування відповіді Нітіна Бансала

і для ще більшої надійності:

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

Те саме, але трохи простішим способом буде, як оновити батьківську сторінку зі сторінки в рамках рамки . Просто зателефонуйте на функцію батьківської сторінки, щоб викликати функцію javascript, щоб перезавантажити сторінку:

window.location.reload();

Або зробіть це безпосередньо зі сторінки в iframe:

window.parent.location.reload();

Обидва твори.



1

Якщо ви хочете викликати функцію JavaScript на батьківщині з iframe, згенерованого іншою функцією ex shadowbox або lightbox.

Вам слід спробувати скористатися windowоб'єктом та викликати батьківську функцію:

window.parent.targetFunction();

Я вважаю, що ОП намагається піти в іншому напрямку
CommandZ

-3

Використовуйте наступні для виклику функції кадру на батьківській сторінці

parent.document.getElementById('frameid').contentWindow.somefunction()
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.