Appel de code JavaScript dans un iframe à partir de la page parent

615

Fondamentalement, j'ai iframeintégré une page et iframea des routines JavaScript que je dois invoquer à partir de la page parent.

Maintenant, le contraire est assez simple car il vous suffit d'appeler parent.functionName(), mais malheureusement, j'ai besoin exactement du contraire.

Veuillez noter que mon problème n'est pas de changer l' URL source de la iframe, mais d'appeler une fonction définie dans la iframe.

Tamas Czinege
la source
42
Votre note sur parent.fuctioName () a résolu mon problème d'appeler une fonction dans le html parent de l'iframe. Merci!
CyberFonic
1
Notez que lors du débogage, vous pouvez faire des choses comme window.location.href ou parent.location.href pour afficher l'url de l'iframe, si vous voulez vérifier que vous avez une référence à l'iframe que vous recherchez.
AaronLS
Voir la réponse de @le dorfier ... a parfaitement fonctionné pour moi.
ErickBest

Réponses:

542

Supposons que l'identifiant de votre iFrame soit "targetFrame" et que la fonction que vous souhaitez appeler soit targetFunction():

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

Vous pouvez également accéder au cadre à l'aide de window.framesau lieu de document.getElementById.

// this option does not work in most of latest versions of chrome and Firefox
window.frames[0].frameElement.contentWindow.targetFunction(); 
Joel Anair
la source
3
fonctionne dans FF 7, Chrome 12, IE 8/9 et Safari (pas sûr de cette version)
Darcy
3
Cette solution ne fonctionne pas pour mon gadget, voici mon code document.getElementById('remote_iframe_0').contentWindow.my.create_element_gadg‌​et('verify_user');"remote_iframe_0 est créé par programmation par un serveur apache shindig mais window.parent.document.getElementById('remote_iframe_0').contentWindow.my.create_element_gadg‌​et('verify_user');"fonctionne
J Bourne
3
@Dirty Henry qu'entendez-vous par "une fonction jQuery?" jQuery est une bibliothèque JavaScript.
Joel Anair
8
@JellicleCat C'est parce que la page parent et l'iframe ont des hôtes différents, ce qui en fait un cadre d'origine croisée, comme le message vous l'indique. Tant que le protocole, le domaine et le port de la page parent et de l'iframe correspondent, tout fonctionnera bien.
Mark Amery
1
J'ai ajouté un exemple sur la façon d'utiliser la méthode window.frames mentionnée.
Elijah Lynn
145

Il y a quelques bizarreries à connaître ici.

  1. HTMLIFrameElement.contentWindowest probablement le moyen le plus simple, mais ce n'est pas tout à fait une propriété standard et certains navigateurs ne le prennent pas en charge, principalement les plus anciens. En effet, la norme HTML DOM niveau 1 n'a rien à dire sur l' windowobjet.

  2. Vous pouvez également essayer HTMLIFrameElement.contentDocument.defaultView, ce que quelques navigateurs plus anciens autorisent, mais pas IE. Même ainsi, la norme ne dit pas explicitement que vous récupérez l' windowobjet, pour la même raison que (1), mais vous pouvez choisir ici quelques versions de navigateur supplémentaires si vous le souhaitez.

  3. window.frames['name']le retour de la fenêtre est l'interface la plus ancienne et donc la plus fiable. Mais vous devez ensuite utiliser un name="..."attribut pour pouvoir obtenir un cadre par son nom, qui est légèrement moche / obsolète / transitionnel. (ce id="..."serait mieux mais IE n'aime pas ça.)

  4. window.frames[number]est également très fiable, mais connaître le bon indice est l'astuce. Vous pouvez vous en tirer avec par exemple. si vous savez que vous n'avez qu'un seul iframe sur la page.

  5. Il est tout à fait possible que l'iframe enfant n'ait pas encore été chargé, ou que quelque chose d'autre ait mal tourné pour le rendre inaccessible. Il peut être plus facile d'inverser le flux des communications: c'est-à-dire que l'iframe enfant avise son window.parentscript lorsqu'il est terminé et prêt à être rappelé. En passant l'un de ses propres objets (par exemple, une fonction de rappel) au script parent, ce parent peut alors communiquer directement avec le script dans l'iframe sans avoir à se soucier de l'élément HTMLIFrameElement auquel il est associé.

bobince
la source
13
Super truc! Surtout votre suggestion 5. s'est avérée extrêmement précieuse. Cela m'a résolu beaucoup de maux de tête lorsque j'essayais d'accéder aux fonctions iFrame à partir du parent dans Chrome. Passer l'objet fonction à partir de l'iFrame l'a fait fonctionner comme un charme dans Chrome, FF et même IE de 9 à 7 et a également rendu très facile de vérifier si la fonction avait déjà été chargée. Merci!
Jpsy
Le numéro 3 fonctionne, mais mieux si vous le faites comme le dit @le dorfier dans son commentaire. Remarquez la methode()pièce.
ErickBest
Veuillez également noter qu'en raison des restrictions de sécurité dans les navigateurs modernes (testés FF29 et Chrome34), il importe beaucoup où vous placez votre appel de fonction. L'appel de la fonction à partir d'une balise de script ou de $ (document) .ready () ne fonctionnera pas. Au lieu de cela, placez l'appel dans la balise onload du corps ou dans une ancre <a href="javascript:doit();"> faites-le </a> comme suggéré ailleurs (voir stackoverflow.com/questions/921387/… ). Passer l'appel de fonction à un script en tant que rappel fonctionne également généralement.
titusn
@chovy: Non, voyez la clarification dans la réponse de Vivek à ce sujet.
bobince
66

Il iframeest possible d' appeler une fonction JS parent à partir de , mais uniquement lorsque le parent et la page chargée dans le iframesont du même domaine, c'est-à-dire abc.com, et que les deux utilisent le même protocole, c'est-à-dire que les deux sont sur http://ou https://.

L'appel échouera dans les cas mentionnés ci-dessous:

  1. La page parent et la page iframe proviennent d'un domaine différent.
  2. Ils utilisent différents protocoles, l'un sur http: // et l'autre sur https: //.

Toute solution à cette restriction serait extrêmement précaire.

Par exemple, imaginez que j'ai enregistré le domaine superwinningcontest.com et envoyé des liens vers les e-mails des gens. Quand ils ont chargé la page principale, je pouvais en cacher quelques-uns iframeet lire leur flux Facebook, vérifier les transactions récentes d'Amazon ou PayPal, ou - s'ils utilisaient un service qui n'implémentait pas une sécurité suffisante - transférer de l'argent de leur comptes. C'est pourquoi JavaScript est limité au même domaine et au même protocole.

Vivek Singh CHAUHAN
la source
6
La solution de contournement consiste à utiliser le beau et dangereux en.wikipedia.org/wiki/Cross-document_messaging
Laramie
Est-ce que quelqu'un sait si un sous-domaine différent entraînera l'échec de cela? J'ai du mal à déboguer un problème qui, selon moi, est lié à cela - l'iframe appelle une fonction de fenêtre parent, mais l'appel ne se produit pas.
Jake
1
Notez que cela échouerait également pour différents numéros de port. c'est-à-dire abc.com:80! = abc.com:8080
prasanthv
31

Dans l'IFRAME, rendez votre fonction publique à l'objet window:

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

Pour accéder à partir de la page parent, utilisez ceci:

var iframe = document.getElementById("iframeId");
iframe.contentWindow.myFunction(args);
Tomalak
la source
Très bonne réponse. Pas sûr de la convention ici. J'ai une question complémentaire. Si je devais commencer une nouvelle question, je le ferai. J'ai un problème. L'iframe de ma page est renseigné dynamiquement. Il contient un bouton dont l' onclick()événement appelle simplement window.print(). Cela imprime parfaitement le contenu du iframe. Mais lorsque la fonction publique de la page de l'iframe appelle window.print()et si la page parent appelle la fonction publique, le contenu du parent est imprimé. Comment dois-je coder cela pour que l'appel à window.print()la fonction publique se comporte comme dans l' onclickévénement?
Karl
1
@Karl Vous ne voulez pas appeler onclick. Tu veux appeler iframe.contentWindow.print().
Tomalak
Malheureusement, cela ne fonctionne pas. Peut-être que je devrais commencer une nouvelle question et documenter mon code? Dans les deux cas, le contenu de la page parent s'imprime également. document.getElementById('myFrame').contentWindow.print();' and the pubic function printContent () `(pas le gestionnaire d'événement oncick) qui appelle a window.print()été appelé de cette façon:document.getElementById('myFrame').contentWindow.printContent();
Karl
1
@Karl - Oui, alors il vaut mieux commencer une nouvelle question. Sauf si la fenêtre parent se trouve sur un domaine différent de l'iframe. Alors rien de ce que vous essayez ne fonctionnera .
Tomalak
Ce que j'ai signalé ici semble être un problème IE (test dans IE8). Aucun problème dans la version actuelle de Chrome. Je n'ai pas encore testé d'autres navigateurs cependant. Pour ceux qui sont intéressés, voir ce jsFiddle (je pense que c'est un bon exemple de chargement d'iframe dynamique et d'appel d'une fonction publique depuis le parent.)
Karl
20

Si la cible de l'iFrame et le document contenant se trouvent sur un domaine différent, les méthodes précédemment publiées peuvent ne pas fonctionner, mais il existe une solution:

Par exemple, si le document A contient un élément iframe qui contient le document B et que le script du document A appelle postMessage () sur l'objet Window du document B, un événement de message sera déclenché sur cet objet, marqué comme provenant de la fenêtre de document A. Le script du document A pourrait ressembler à:

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

Pour enregistrer un gestionnaire d'événements pour les événements entrants, le script utiliserait addEventListener () (ou des mécanismes similaires). Par exemple, le script du document B pourrait ressembler à:

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

Ce script vérifie d'abord que le domaine est le domaine attendu, puis examine le message, qu'il affiche à l'utilisateur ou répond en renvoyant un message au document qui a envoyé le message en premier lieu.

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

19greg96
la source
1
easyXDM fournit une abstraction sur PostMessage pour les navigateurs plus anciens (y compris une solution de rechange Flash (LocalConnection) pour les dinosaures tenaces).
JonnyReeves
1
c'est super ... après avoir essayé les réponses ci-dessus, j'ai finalement obtenu votre réponse, merci!
vkGunasekaran
9

Juste pour mémoire, j'ai rencontré le même problème aujourd'hui, mais cette fois, la page a été intégrée dans un objet, pas un iframe (car il s'agissait d'un document XHTML 1.1). Voici comment cela fonctionne avec les objets:

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

(désolé pour les sauts de ligne laids, ne tient pas dans une seule ligne)

Tamas Czinege
la source
8

L'IFRAME doit être dans la frames[]collection. Utilisez quelque chose comme

frames['iframeid'].method();
dkretz
la source
Cette réponse pourrait certainement utiliser plus d'informations, car il y a certainement d'autres considérations. D'une part, je suppose que le développeur doit faire methodune propriété de l' windowobjet iframe .
matty
8

Quirksmode avait un post à ce sujet .

Puisque la page est maintenant cassée et accessible uniquement via archive.org, je l'ai reproduite ici:

IFrames

Sur cette page, je donne un bref aperçu de l'accès aux iframes à partir de la page où ils se trouvent. Sans surprise, il y a quelques considérations sur le navigateur.

Un iframe est un cadre en ligne, un cadre qui, tout en contenant une page complètement séparée avec sa propre URL, est néanmoins placé dans une autre page HTML. Cela donne de très belles possibilités dans la conception web. Le problème est d'accéder à l'iframe, par exemple pour y charger une nouvelle page. Cette page explique comment procéder.

Cadre ou objet?

La question fondamentale est de savoir si l'iframe est considérée comme un cadre ou comme un objet.

  • Comme expliqué dans les pages Introduction aux cadres , si vous utilisez des cadres, le navigateur crée une hiérarchie de cadres pour vous ( top.frames[1].frames[2]et autres). L'iframe s'inscrit-il dans cette hiérarchie de trames?
  • Ou le navigateur voit-il un iframe comme simplement un autre objet, un objet qui se trouve avoir une propriété src? Dans ce cas, nous devons utiliser un appel DOM standard (comme document.getElementById('theiframe'))pour y accéder. En général, les navigateurs autorisent les deux vues sur les iframes «réels» (codés en dur), mais les iframes générés ne sont pas accessibles en tant que trames.

Attribut NAME

La règle la plus importante est de donner à tout iframe que vous créez un nameattribut, même si vous utilisez également un id.

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

La plupart des navigateurs ont besoin de l' nameattribut pour intégrer l'iframe à la hiérarchie des trames. Certains navigateurs (notamment Mozilla) nécessitent le idpour rendre l'iframe accessible en tant qu'objet. En attribuant les deux attributs à l'iframe, vous gardez vos options ouvertes. Mais nameest beaucoup plus important que id.

Accès

Soit vous accédez à l'iframe en tant qu'objet et changez son, srcsoit vous accédez à l'iframe en tant que cadre et vous changez son location.href.

document.getElementById ('iframe_id'). src = 'newpage.html'; frames ['iframe_name']. location.href = 'newpage.html'; La syntaxe du cadre est légèrement préférable car Opera 6 la prend en charge mais pas la syntaxe de l'objet.

Accéder à l'iframe

Donc, pour une expérience multi-navigateur complète, vous devez donner un nom à l'iframe et utiliser le

frames['testiframe'].location.href

syntaxe. Pour autant que je sache, cela fonctionne toujours.

Accéder au document

L'accès au document à l'intérieur de l'iframe est assez simple, à condition d'utiliser l' nameattribut. Pour compter le nombre de liens dans le document dans l'iframe, faites frames['testiframe'].document.links.length.

Iframes générés

Lorsque vous générez un iframe via le DOM W3C, l'iframe n'est pas immédiatement entré dans le framestableau, cependant, et la frames['testiframe'].location.hrefsyntaxe ne fonctionnera pas immédiatement. Le navigateur a besoin d'un peu de temps avant que l'iframe n'apparaisse dans le tableau, temps pendant lequel aucun script ne peut s'exécuter.

La document.getElementById('testiframe').srcsyntaxe fonctionne bien en toutes circonstances.

L' targetattribut d'un lien ne fonctionne pas non plus avec les iframes générés, sauf dans Opera, même si j'ai donné à mon iframe généré à la fois un nameet un id.

Le manque de targetsupport signifie que vous devez utiliser JavaScript pour modifier le contenu d'une iframe générée, mais comme vous avez quand même besoin de JavaScript pour le générer, je ne vois pas cela comme un problème.

Taille du texte dans les iframes

Un curieux bug uniquement pour Explorer 6:

Lorsque vous modifiez la taille du texte via le menu Affichage, les tailles de texte dans les iframes sont correctement modifiées. Toutefois, ce navigateur ne modifie pas les sauts de ligne dans le texte d'origine, de sorte qu'une partie du texte peut devenir invisible, ou des sauts de ligne peuvent se produire alors que la ligne peut toujours contenir un autre mot.

activout.se
la source
5
Ce lien semble rompu.
zaphod
1
Et pour aggraver les choses, je ne peux pas trouver la page associée nulle part sur quirksmode.
strictjux
7

J'ai trouvé une solution assez élégante.

Comme vous l'avez dit, il est assez facile d'exécuter du code situé sur le document parent. Et c'est la base de mon code, faites exactement le contraire.

Lorsque mon iframe se charge, j'appelle une fonction située sur le document parent, en passant comme argument une référence à une fonction locale, située dans le document de l'iframe. Le document parent a désormais un accès direct à la fonction iframe via cette référence.

Exemple:

Sur le parent:

function tunnel(fn) {
    fn();
}

Sur l'iframe:

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

parent.tunnel(myFunction);

Lorsque l'iframe se charge, il appelle parent.tunnel (YourFunctionReference), qui exécute la fonction reçue en paramètre.

C'est simple, sans avoir à gérer toutes les méthodes non standard des différents navigateurs.

Julien L
la source
Bonjour, pouvez-vous confirmer ceci "C'est aussi simple que cela, sans avoir à gérer toutes les méthodes non standard des différents navigateurs."?
Milche Patern
1
J'utilise cette méthode sur divers projets, semble très bien fonctionner. Je n'ai pas personnellement testé tous les navigateurs en circulation, mais je n'ai jamais reçu de rapport de bogue non plus.
Julien L
Fonctionne comme un charme, IE 11, Chrome ~ 50.
Augustas
1
Je suppose que ce n'est pas inter-domaines .. Est-ce?
Tushar Shukla
@TusharShukla Malheureusement non, ce n'est pas le cas.
jeff-h
6

Certaines de ces réponses ne résolvent pas le problème CORS ou ne précisent pas où vous placez les extraits de code pour rendre la communication possible.

Voici un exemple concret. Disons que je veux cliquer sur un bouton de la page parent et que cela fasse quelque chose dans l'iframe. Voici comment je le ferais.

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

Pour aller dans l'autre sens (cliquez sur le bouton à l'intérieur de l'iframe pour appeler la méthode à l'extérieur de l'iframe), il suffit de changer l'emplacement de l'extrait de code, et de changer cela:

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

pour ça:

window.parent.postMessage('foo','*')
Cybernétique
la source
4

Poursuivant avec la réponse de JoelAnair :

Pour plus de robustesse, utilisez comme suit:

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

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

Fonctionne comme un charme :)

Nitin Bansal
la source
3

Suivre la réponse de Nitin Bansal

et pour encore plus de robustesse:

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

et

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

var frame_win = getIframeWindow(el);

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

Les mêmes choses mais un moyen un peu plus simple seront Comment actualiser la page parent à partir de la page dans iframe . Appelez simplement la fonction de la page parent pour appeler la fonction javascript pour recharger la page:

window.location.reload();

Ou faites-le directement depuis la page dans iframe:

window.parent.location.reload();

Les deux œuvres.

sangam
la source
1

Si vous souhaitez appeler la fonction JavaScript sur le parent à partir de l'iframe généré par une autre fonction, par exemple shadowbox ou lightbox.

Vous devriez essayer d'utiliser l' windowobjet et invoquer la fonction parent:

window.parent.targetFunction();
Santhanakumar
la source
Je crois que le PO essaie d'aller dans l'autre sens
CommandZ
-3

Utiliser la suite pour appeler la fonction d'un cadre dans la page parent

parent.document.getElementById('frameid').contentWindow.somefunction()
Sablonneux
la source