web-dev-qa-db-fra.com

Invocation de code JavaScript dans un iframe à partir de la page parente

Fondamentalement, j'ai un iframe incorporé dans une page et le iframe contient des routines JavaScript que j'ai besoin d'appeler à partir de la page parent.

Maintenant, l’inverse est assez simple, il suffit d’appeler parent.functionName(), mais malheureusement, j’ai exactement besoin de l’opposé.

Veuillez noter que mon problème ne change pas la source URL du iframe, mais appelle une fonction définie dans le iframe.

583
Tamas Czinege

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

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

Vous pouvez également accéder au cadre en utilisant window.frames au lieu de document.getElementById.

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

Il y a quelques bizarreries à connaître ici.

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

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

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

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

  5. Il est tout à fait possible que l’enfant iframe n’ait pas encore été chargé ou que quelque chose d’autre ait mal tourné pour le rendre inaccessible. Vous trouverez peut-être plus facile d'inverser le flux des communications: autrement dit, demandez à l'enfant iframe de notifier son script window.parent une fois le chargement terminé et qu'il est prêt à être rappelé. En transmettant un de ses propres objets (une fonction de rappel, par exemple) au script parent, ce dernier peut alors communiquer directement avec le script dans l'iframe sans avoir à se soucier du type de HTMLIFrameElement auquel il est associé.

142
bobince

Il est possible d'appeler une fonction JS parent à partir de iframename__, mais uniquement lorsque le parent et la page chargés dans iframeappartiennent au même domaine i.e. abc.com et utilisent le même protocole, c'est-à-dire qu'ils sont tous deux 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 de rechange à cette restriction serait extrêmement précaire.

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

64

Dans 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);
30
Tomalak

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

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

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

Pour inscrire 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 à ceci:

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 correspond au domaine attendu, puis examine le message qu'il affiche à l'utilisateur ou auquel il répond en renvoyant un message au document qui l'a envoyé en premier.

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

16
19greg96

Pour mémoire, j'ai rencontré le même problème aujourd'hui, mais cette fois-ci, la page a été incorporée dans un objet et non dans un iframe (puisqu'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 coupures de lignes laides, ne correspondait pas à une seule ligne)

9
Tamas Czinege

Quirksmode avait un post on this .

La page étant 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 existe certaines considérations du 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 de sites Web. Le problème est d'accéder à l'iframe, par exemple pour y charger une nouvelle page. Cette page explique comment le faire.

Cadre ou objet?

La question fondamentale est de savoir si l’iframe est vu comme un cadre ou comme un objet.

  • Comme expliqué dans les 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 cadres?
  • Ou le navigateur voit-il un iframe comme un simple objet, un objet qui a une propriété src? Dans ce cas, nous devons utiliser un standard appel DOM (comme document.getElementById('theiframe')) pour y accéder. En général, les navigateurs autorisent les deux vues sur des iframes "réels" (codés en dur), mais les iframes générés ne sont pas accessibles en tant que cadres. .

Attribut NAME

La règle la plus importante est d'indiquer à chaque iframe que vous créez un attribut namename__, même si vous utilisez également un idname__.

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

La plupart des navigateurs ont besoin de l'attribut namepour que l'iframe fasse partie de la hiérarchie des cadres. Certains navigateurs (notamment Mozilla) ont besoin de idpour rendre l'iframe accessible en tant qu'objet. En attribuant les deux attributs à l'iframe, vos options restent ouvertes. Mais nameest beaucoup plus important que idname__.

Accès

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

document.getElementById ('iframe_id'). src = 'newpage.html'; cadres ['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 complète entre navigateurs, vous devez attribuer un nom à l’iframe et utiliser le

frames['testiframe'].location.href

syntaxe. 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 que vous utilisiez l'attribut namename__. 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 W3C DOM , l'iframe n'est pas entré immédiatement dans le tableau frameset la syntaxe frames['testiframe'].location.href 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 être exécuté.

La syntaxe document.getElementById('testiframe').src fonctionne correctement dans toutes les circonstances.

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

L'absence de prise en charge de targetsignifie que vous devez utiliser JavaScript pour modifier le contenu d'un iframe généré, mais comme vous avez de toute façon besoin de JavaScript pour le générer, je ne vois pas cela comme un problème.

Taille du texte en iframes

Un bug curieux d'Explorer 6 seulement:

Lorsque vous modifiez la taille du texte dans 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 que des sauts de ligne peuvent se produire alors que la ligne peut encore contenir un autre mot.

8
activout.se

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

frames['iframeid'].method();
8
dkretz

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, à l'opposé.

Lorsque mon iframe est chargé, j'appelle une fonction située sur le document parent, en passant en argument une référence à une fonction locale, située dans le document de l'iframe. Le document parent dispose désormais d'un accès direct à la fonction de l'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 à traiter avec toutes les méthodes non standard des différents navigateurs.

7
Julien L

Certaines de ces réponses ne traitent pas du problème CORS, ou n'indiquent pas clairement l'endroit où vous placez les extraits de code pour rendre la communication possible.

Voici un exemple concret. Dites que je veux cliquer sur un bouton de la page parent et que celui-ci fasse quelque chose à l'intérieur de 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 dans iframe pour appeler la méthode en dehors d'iframe), il suffit de changer l'emplacement de l'extrait de code et de modifier ceci:

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

pour ça:

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

Continuant avec JoelAnair's réponse:

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

Travaillé comme un charme :)

3
Nitin Bansal

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();
  ...
}
...
3
Dominique Fortin

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

window.location.reload();

Ou faites ceci directement à partir de la page dans iframe:

window.parent.location.reload();

Les deux fonctionne.

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

Essayez simplement parent.myfunction()

1
Saeid Zebardast

Si nous voulons appeler la fonction javascript de la page parent à partir de l'iframe générée à partir du codage. ex shadowbox ou lightbox

window.parent.targetFunction ();

1
Santhanakumar