Comment redimensionner un iframe d'un autre domaine ?
-Editer
Faites défiler la page pour trouver des solutions ou lisez comment NE PAS le faire :D
Après de nombreuses heures de bidouillage de code, la conclusion est que tout ce qui se trouve à l'intérieur de l'iframe n'est pas accessible, même les barres de défilement qui s'affichent sur mon domaine. J'ai essayé de nombreuses techniques, sans succès.
Pour gagner du temps, n'empruntez pas cette voie, utilisez simplement sendMessages pour les communications entre domaines. Il y a des plug-ins pour HTML < 5 que j'utilise - Allez en bas pour un bel exemple :)
Ces derniers jours, j'ai essayé d'intégrer une iframe dans un site. Il s'agit d'une solution à court terme pendant que l'autre partie développe une API (cela pourrait prendre des mois...). Et parce qu'il s'agit d'une solution à court terme, nous ne voulons pas utiliser easyXDM - J'ai accès à l'autre domaine mais il est déjà assez difficile de leur demander d'ajouter l'en-tête p3p tel qu'il est......
3 iframes
La solution la plus proche que j'ai trouvée était les 3 iframes- mais cela fait perdre la tête à chrome et safari donc je ne peux pas l'utiliser.
ouvert en chrome
http://css-tricks.com/examples/iFrameResize/crossdomain.php#frameId=frame-one&height=1179
Mesurer les barres de défilement
J'ai trouvé un autre post sur la façon d'utiliser la hauteur de défilement pour essayer de redimensionner le formulaire en théorie, cela fonctionne bien mais je n'ai pas pu l'appliquer correctement en utilisant la hauteur de défilement des iframes .
document.body.scrollHeight
Cela utilise évidemment la hauteur des corps (il n'est pas possible d'accéder à ces propriétés - 100% est basé sur la zone d'affichage du client et non sur la hauteur du document du domaine X).
J'ai essayé d'utiliser Jquery pour obtenir la hauteur des iframes.
$('#frameId').Height()
$('#frameId').clientHeight
$('#frameId').scrollHeight
les valeurs de retour sont différentes dans chrome et ie - ou n'ont pas de sens du tout. Le problème est que tout ce qui se trouve à l'intérieur du cadre est refusé - même la barre de défilement...
Styles calculés
Mais si j'inspecte un élément de l'iframe dans Chrome, il me montre les dimensions du document à l'intérieur de l'iframe (en utilisant jquery x-domain pour obtenir iframe.heigh - accès refusé). Il n'y a rien dans le CSS calculé
Maintenant, comment chrome calcule-t-il cela ? (edit- le navigateur re-rend la page en utilisant son moteur de rendu intégré pour calculer tous ces paramètres - mais ils ne sont attachés nulle part pour éviter la fraude inter-domaine donc )
HTML4
J'ai lu les spécifications de HTML4.x et il y est dit qu'il devrait y avoir des valeurs en lecture seule exposées via document.element mais l'accès est refusé via jquery.
Cadre Proxy
J'ai emprunté la voie de la transmission par proxy du site et du calcul, ce qui est correct, jusqu'à ce qu'un utilisateur se connecte via l'iframe et que le proxy reçoive une page de connexion au lieu du contenu réel. De plus, pour certains, appeler la page deux fois n'est pas acceptable.
http://www.codeproject.com/KB/aspnet/asproxy.aspx
http://www.johnchapman.name/aspnet-proxy-page-cross-domain-requests-from-ajax-and-javascript/
Rendu de la page
Je ne suis pas allé aussi loin, mais il existe des moteurs jscript qui regardent la source et rendent à nouveau la page en se basant sur le fichier source... mais il faudrait pirater ces jscripts... et ce n'est pas une situation idéale pour les entités commerciales... et certains font appel à des applets java ou à un rendu côté serveur.
http://en.wikipedia.org/wiki/Server-side_JavaScript
http://htmlunit.sourceforge.net/ <-java pas jscript
EDIT 09-2013 UPDATE
Tout cela peut être fait avec des sockets HTML5. Mais easyXDM est une excellente solution de repli pour les pages de plaintes non HTML5.
Solution 1 Très bonne solution !
Utilisation d'easyXDM
Sur votre serveur, vous avez mis en place une page sous la forme de
<html>
<head>
<script src="scripts/easyXDM.js" type="text/javascript"></script>
<script type="text/javascript" language="javascript">
var transport = new easyXDM.Socket(/** The configuration */{
remote: "http://www.OTHERDOMAIN.com/resize_intermediate.html?url=testpages/resized_iframe_1.html",
//ID of the element to attach the inline frame to
container: "embedded",
onMessage: function (message, origin) {
var settings = message.split(",");
//Use jquery on a masterpage.
//$('iframe').height(settings[0]);
//$('iframe').width(settings[1]);
//The normal solution without jquery if not using any complex pages (default)
this.container.getElementsByTagName("iframe")[0].style.height = settings[0];
this.container.getElementsByTagName("iframe")[0].style.width = settings[1];
}
});
</script>
</head>
<body>
<div id="embedded"></div>
</body>
et sur le domaine de l'appelant, il suffit d'ajouter le html intermiedate_frame et easyXDM.js au même endroit. Comme un dossier parent - vous pouvez alors accéder à des répertoires relatifs ou à un dossier contenu juste pour vous.
OPTION 1
Si vous ne voulez pas ajouter des scripts à toutes les pages, regardez l'option 2 !
Il leur suffit alors d'ajouter un simple jscript à la fin de chacune des pages sur lesquelles le redimensionnement doit avoir lieu. Il n'est pas nécessaire d'inclure l'easyxdm dans chacune de ces pages.
<script type="text/javascript">
window.onload = function(){ parent.socket.postMessage( (parseInt(document.body.clientHeight)) + "," + ( document.body.clientWidth ) ); };
</script>
J'ai modifié les paramètres qu'il envoie. Si vous voulez que la largeur fonctionne correctement, les pages de l'autre domaine doivent inclure la largeur de la page dans un style quelque part qui ressemble à ceci :
<style type="text/css">
html, body {
overflow: hidden;
margin: 0px;
padding: 0px;
background-color: rgb(75,0,85);
color:white;
width:660px
}
a {
color:white;
visited:white;
}
</style>
Cela fonctionne très bien pour moi. Si la largeur n'est pas incluse, le cadre se comporte un peu bizarrement et essaie de deviner ce qu'il devrait être et ne se rétrécit pas si vous en avez besoin.
OPTION 2
Modifier la trame intermédiaire afin d'interroger les changements
Votre cadre intermédiaire devrait ressembler à quelque chose comme ceci
<!doctype html>
<html>
<head>
<title>Frame</title>
<script type="text/javascript" src="easyXDM.js">
</script>
<script type="text/javascript">
var iframe;
var socket = new easyXDM.Socket({
//This is a fallback- not needed in many cases
swf: "easyxdm.swf",
onReady: function(){
iframe = document.createElement("iframe");
iframe.frameBorder = 0;
document.body.appendChild(iframe);
iframe.src = "THE HOST FRAME";
iframe.onchange = messageBack();
},
onMessage: function(url, origin){
iframe.src = url;
}
});
//Probe child.frame for dimensions.
function messageBack(){
socket.postMessage ( iframe.contentDocument.body.clientHeight + "," + iframe.contentDocument.body.clientWidth);
};
//Poll for changes on children every 500ms.
setInterval("messageBack()",500);
</script>
<style type="text/css">
html, body {
overflow: hidden;
margin: 0px;
padding: 0px;
width: 100%;
height: 100%;
}
iframe {
width: 100%;
height: 100%;
border: 0px;
}
</style>
</head>
<body>
</body>
</html>
L'intervalle pourrait être rendu plus efficace en vérifiant si la taille a changé et en ne l'envoyant que si les dimensions changent, au lieu d'envoyer un message toutes les 500 ms. Si vous mettez en œuvre cette vérification, vous pouvez réduire l'intervalle d'envoi à 50 ms !
Fonctionne sur tous les navigateurs et est rapide. Superbes fonctions de débogage !
Excellent Work to Sean Kinsey who made the script!!!
Solution 2 (Fonctionne mais n'est pas génial)
Donc, en gros, si vous avez un accord mutuel avec l'autre domaine, vous pouvez ajouter une bibliothèque pour gérer le sendmessage. Si vous n'avez pas d'accès à l'autre domaine Continuez à chercher d'autres astuces, car je n'ai pas pu trouver ou justifier pleinement celles que j'ai trouvées.
Ainsi, l'autre domaine inclura ces éléments dans sa balise Head.
<script src="scripts/jquery-1.5.2.min.js" type="text/javascript"></script>
<script src="scripts/jquery.postmessage.min.js" type="text/javascript"></script>
<script src="scripts/club.js" type="text/javascript"></script>
Dans le club.js il y a juste quelques appels personnalisés que j'ai fait pour les appels de redimensionnement et contient
$(document).ready(function () {
var parent_url = decodeURIComponent( document.location.hash.replace( /^#/, '' ) ),link;
//Add source url hash to each url to authorise callback when navigating inside the frame.Without this clicking any links will break the communication and no messages will be received
$('a[href]').each(function(){
this.href = this.href + document.location.hash ;
});
//Get the dimensions and send back to calling page.
var h1 = document.body.scrollHeight;
var w1 = document.body.scrollWidth;
$.postMessage({ if_height: h1, if_width: w1 }, parent_url, parent );
});
Et votre page fera tout le travail et aura un joli script...
//This is almost like request.querystring used to get the iframe data
function querySt(param, e) {
gy = e.split("&");
for (i = 0; i < gy.length; i++) {
ft = gy[i].split("=");
if (ft[0] == param) {
return ft[1];
}
}
}
$(function () {
// Keep track of the iframe dimensions.
var if_height;
var if_width;
// Pass the parent page URL into the Iframe in a meaningful way (this URL could be
// passed via query string or hard coded into the child page, it depends on your needs).
src = 'http://www.OTHERDOAMIN.co.uk/OTHERSTARTPAGE.htm' + '#' + encodeURIComponent(document.location.href),
// Append the Iframe into the DOM.
iframe = $('<iframe " src="' + src + '" width="100%" height="100%" scrolling="no" frameborder="0"><\/iframe>').appendTo('#iframe');
// Setup a callback to handle the dispatched MessageEvent event. In cases where
// window.postMessage is supported, the passed event will have .data, .origin and
// .source properties. Otherwise, this will only have the .data property.
$.receiveMessage(function (e) {
// Get the height from the passsed data.
//var h = Number(e.data.replace(/.*if_height=(\d+)(?:&|$)/, '$1'));
var h = querySt("if_height", e.data);
var w = querySt("if_width", e.data);
if (!isNaN(h) && h > 0 && h !== if_height) {
// Height has changed, update the iframe.
iframe.height(if_height = h);
}
if (!isNaN(w) && w > 0 && w !== if_width) {
// Height has changed, update the iframe.
iframe.width(if_width = w);
}
//For debugging only really- can remove the next line if you want
$('body').prepend("Recieved" + h + "hX" + w + "w .. ");
// An optional origin URL (Ignored where window.postMessage is unsupported).
//Here you must put the other domain.com name only! This is like an authentication to prevent spoofing and xss attacks!
}, 'http://www.OTHERDOMAIN.co.uk');
});
OPTION 3
Il existe maintenant une petite bibliothèque JS pour gérer le redimensionnement des iFrames inter-domaines. Il faut toujours que l'iFrame contienne un peu de JavaScript, mais il ne s'agit que de 2,8k (765 octets compressés) de JS natif qui n'a pas de dépendances et qui ne fait rien tant qu'il n'est pas appelé par la page parent. Cela signifie que c'est un invité agréable sur les systèmes des autres.
Ce code utilise mutationObserver pour détecter les modifications du DOM et guette également les événements de redimensionnement, de sorte que l'iFrame reste à la taille du contenu. Fonctionne dans IE8+.