196 votes

Créer dynamiquement un iframe avec un HTML donné

J'essaie de créer un iframe à partir de JavaScript et de le remplir avec du HTML arbitraire, comme ceci :

var html = '<body>Foo</body>';
var iframe = document.createElement('iframe');
iframe.src = 'data:text/html;charset=utf-8,' + encodeURI(html);

Je m'attendrais iframe pour ensuite contenir une fenêtre et un document valides. Or, ce n'est pas le cas :

> console.log(iframe.contentWindow) ;
null

Essayez vous-même : http://jsfiddle.net/TrevorBurnham/9k9Pe/

Qu'est-ce que je néglige ?

10 votes

Notez que HTML5 a introduit un nouveau paramètre qui fait cela automatiquement : w3schools.com/tags/att_iframe_srcdoc.asp Le seul problème est la compatibilité des navigateurs...

0 votes

287voto

mschr Points 5003

Bien que votre src = encodeURI devrait fonctionner, j'aurais pris un autre chemin :

var iframe = document.createElement('iframe');
var html = '<body>Foo</body>';
document.body.appendChild(iframe);
iframe.contentWindow.document.open();
iframe.contentWindow.document.write(html);
iframe.contentWindow.document.close();

Comme il n'y a pas de contraintes liées au domaine x et que tout se fait par l'intermédiaire de la fonction iframe vous pourrez accéder au contenu du cadre et le manipuler par la suite. Tout ce dont vous devez vous assurer, c'est que le contenu a été rendu, ce qui (selon le type de navigateur) commencera pendant/après l'émission de la commande .write. mais Il n'est pas nécessaire de le faire lorsque close() s'appelle.

Une manière 100% compatible de faire un callback pourrait être cette approche :

<html><body onload="parent.myCallbackFunc(this.window)"></body></html>

Iframes a l'événement onload, cependant. Voici une approche pour accéder au html interne comme DOM (js) :

iframe.onload = function() {
   var div=iframe.contentWindow.document.getElementById('mydiv');
};

0 votes

Intéressant ; je n'avais pas vu cette technique auparavant. Je sais que l'encodage/décodage de l'URI augmente les performances, mais j'ai également vu qu'elle était décrite comme la seule alternative dans les environnements qui ne prennent pas en charge l'option srcdoc propriété . Y a-t-il un inconvénient à ce que le document.write approche ?

0 votes

Je ne pense pas, et c'est la solution la plus rétrocompatible que je puisse trouver en ce qui concerne la compatibilité des navigateurs. Il y a une bizarrerie quant à la façon d'accéder au contentDocument, mais je pense que ce qui précède devrait fonctionner partout (IE aussi) - bien, document.bodý est un hack pour ajouter mais sinon =) Si vous rencontrez des restrictions de sécurité, mettez SRC à about:blank - car les opérations sur les documents ne sont pas autorisées sur le domaine x, comme tout autre domaine du DOM.

1 votes

@mschr Cette méthode prend-elle en charge le code complet des pages HTML où les includes et les feuilles de style sont également chargés ? Voir stackoverflow.com/questions/19871886

152voto

gillesc Points 4093

Définir le src d'une nouvelle iframe en javascript ne déclenche pas l'analyseur HTML avant que l'élément ne soit inséré dans le document. Le HTML est alors mis à jour et l'analyseur HTML sera invoqué et traitera l'attribut comme prévu.

http://jsfiddle.net/9k9Pe/2/

var iframe = document.createElement('iframe');
var html = '<body>Foo</body>';
iframe.src = 'data:text/html;charset=utf-8,' + encodeURI(html);
document.body.appendChild(iframe);
console.log('iframe.contentWindow =', iframe.contentWindow);

Pour répondre à votre question, il est important de noter que cette approche présente des problèmes de compatibilité avec certains navigateurs. Veuillez consulter la réponse de @mschr pour une solution inter-navigateurs.

3 votes

Ça ne fonctionne même pas dans IE10. La réponse de @mschr fonctionne dans IE7+ à coup sûr, peut-être même dans des versions plus anciennes.

6 votes

Sa question était "Qu'est-ce que je néglige ?" et c'était le fait que le iframe n'était pas ajouté au document. Je n'ai jamais prétendu que c'était un problème inter-navigateurs et ce n'est que plus d'un an après ma réponse que quelqu'un s'est plaint. Non, ce n'est pas multi-navigateurs. Mais soyons honnêtes, si vous voulez un code de qualité, il y a probablement une solution beaucoup plus propre que d'utiliser un iframe en premier lieu :)

2 votes

Notez que encodeURI(...) ne code pas tous les caractères, donc si votre HTML contient des caractères spéciaux tels que # ça va se casser. encodeURIComponent(...) encode ces caractères. Voir cette réponse

26voto

freddyrobinson Points 415

Je sais qu'il s'agit d'une vieille question, mais j'ai pensé que je pourrais fournir un exemple en utilisant l'outil de gestion de l'information de l'entreprise. srcdoc comme c'est maintenant le cas largement soutenu et cette question est souvent posée.

Utilisation de la srcdoc vous pouvez fournir du HTML en ligne à intégrer. Il remplace l'attribut src s'il est pris en charge. Le navigateur se rabattra sur l'attribut src si cet attribut n'est pas pris en charge.

Je recommande également d'utiliser le sandbox pour appliquer des restrictions supplémentaires au contenu du cadre. Ceci est particulièrement important si le HTML n'est pas le vôtre.

const iframe = document.createElement('iframe');
const html = '<body>Foo</body>';
iframe.srcdoc = html;
iframe.sandbox = '';
document.body.appendChild(iframe);

Si vous devez prendre en charge des navigateurs plus anciens, vous pouvez vérifier si srcdoc et se rabattre sur l'une des autres méthodes des autres réponses.

function setIframeHTML(iframe, html) {
  if (typeof iframe.srcdoc !== 'undefined') {
    iframe.srcdoc = html;
  } else {
    iframe.sandbox = 'allow-same-origin';
    iframe.contentWindow.document.open();
    iframe.contentWindow.document.write(html);
    iframe.contentWindow.document.close();
  }
}

var iframe = document.createElement('iframe');
iframe.sandbox = '';
var html = '<body>Foo</body>';

document.body.appendChild(iframe);
setIframeHTML(iframe, html);

1 votes

Mais qu'en est-il si un nouvel élément n'a pas besoin d'être créé ? Si un iframe existe déjà et que les données doivent simplement être mises à jour ?

0 votes

Cette utilisation peut utiliser postMessage pour envoyer un message au JavaScript à l'intérieur de l'iframe décrivant les changements à effectuer, puis l'iframe peut se mettre à jour avec son propre code. C'est un peu délicat, mais je pense que c'est la seule solution.

17voto

zedd45 Points 460

Il existe une alternative pour créer un iframe dont le contenu est une chaîne de caractères HTML : l'attribut srcdoc . Ceci n'est pas supporté par les anciens navigateurs (le principal d'entre eux : Internet Explorer, et éventuellement Safari ?), mais il existe un polyfill pour ce comportement, que vous pourriez mettre dans des commentaires conditionnels pour IE, ou utiliser quelque chose comme has.js pour le charger paresseusement de manière conditionnelle.

2 votes

La prise en charge de cette fonction est assez répandue maintenant (sauf pour IE). Et c'est définitivement préférable à l'accès direct au contentDocument - d'autant plus que s'il est utilisé en conjonction avec l'attribut sandbox, vous ne pouvez pas accéder au contentDocument.

17voto

halfcube Points 1064

Merci pour votre excellente question, qui m'a déjà surpris à plusieurs reprises. Lorsque j'utilise la source HTML dataURI, je constate que je dois définir un document HTML complet.

Voir ci-dessous un exemple modifié.

var html = '<html><head></head><body>Foo</body></html>';
var iframe = document.createElement('iframe');
iframe.src = 'data:text/html;charset=utf-8,' + encodeURI(html);

prenez note du contenu html enveloppé avec <html> et les balises iframe.src chaîne.

L'élément iframe doit être ajouté à l'arbre DOM pour être analysé.

document.body.appendChild(iframe);

Vous ne pourrez pas inspecter le iframe.contentDocument à moins que vous disable-web-security sur votre navigateur. Vous obtiendrez un message

DOMException : Impossible de lire la propriété 'contentDocument' de 'HTMLIFrameElement' : Bloqué un cadre avec l'origine " http://localhost:7357 " d'accéder à un cadre d'origine croisée.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X