web-dev-qa-db-fra.com

iframe Ajuster automatiquement la hauteur lorsque le contenu change

J'ai un iframe comme vous pouvez le voir sur le lien suivant; -

http://one2onecars.com

L'iframe est la réservation en ligne au centre de l'écran. Le problème que j'ai est que, bien que la hauteur de l'iframe soit correcte lors du chargement de la page, j'en ai besoin pour ajuster automatiquement la hauteur au fur et à mesure que le contenu de la page s'ajuste. Par exemple, si je fais une recherche de code postal dans la réservation en ligne, cela crée un menu déroulant et rend le bouton 'Next Step' non visible.

Ce que je dois faire, c'est que lorsque le contenu de la réservation en ligne change, l'iframe s'ajuste automatiquement à la nouvelle hauteur de l'iframe (dynamiquement) car il ne charge aucune autre page.

J'ai essayé plusieurs scripts différents en utilisant jquery pour essayer de résoudre ce problème, mais ils semblent tous ne régler automatiquement la hauteur de l'iframe qu'au premier chargement de la page et pas lorsque le contenu de l'iframe change.

Est-ce même possible de le faire?

Le code que j'ai en ce moment est avec une hauteur définie pour le moment: -

        <div id="main-online-booking">

            <iframe id="main-online-frame" class="booking-dimensions" src="http://www.marandy.com/one2oneob/login-guest.php" scrolling="no" frameborder="0"></iframe>

        </div>

#main-online-booking {
    height: 488px;
    border-bottom: 6px #939393 solid;
    border-left: 6px #939393 solid;
    border-right: 6px #939393 solid;
    z-index: 4;
    background-color: #fff;
}


.booking-dimensions {
    width: 620px;
    height: 488px;
}

Si quelqu'un peut m'aider, je serais très apprécié!

19
nsilva

setInterval

Le seulement  (corrigé en raison des progrès de la technologie des navigateurs, voir réponse de David Bradshaw ) une manière rétrocompatible pour y parvenir avec un iframe consiste à utiliser setInterval et à conserver un œil sur le contenu de l'iframe vous-même. Lorsqu'il change de hauteur, vous mettez à jour la taille de l'iframe. Il n'y a pas un tel événement que vous pouvez écouter, ce qui rendra la tâche facile malheureusement.

Un exemple de base, cela ne fonctionnera que si le contenu iframe dont la taille a changé fait partie du flux de la page principale. Si les éléments sont flottants ou positionnés, vous devrez les cibler spécifiquement pour rechercher les changements de hauteur.

jQuery(function($){
  var lastHeight = 0, curHeight = 0, $frame = $('iframe:eq(0)');
  setInterval(function(){
    curHeight = $frame.contents().find('body').height();
    if ( curHeight != lastHeight ) {
      $frame.css('height', (lastHeight = curHeight) + 'px' );
    }
  },500);
});

Évidemment, selon ce que vous voulez, vous pouvez modifier la perspective de ce code afin qu'il fonctionne à partir de l'iframe, sur lui-même, plutôt que de vous attendre à faire partie de la page principale.

problème inter-domaines

Le problème que vous constaterez est qu'en raison de la sécurité du navigateur, il ne vous permettra pas d'accéder au contenu de l'iframe s'il se trouve sur un hôte différent de la page principale, il n'y a donc en fait rien que vous puissiez faire à moins d'avoir un moyen d'ajouter tout script au format HTML qui apparaît dans l'iframe.

ajax

Certains autres suggèrent d'essayer d'utiliser le service tiers via AJAX, à moins que le service ne prenne en charge cette méthode, il est très peu probable que vous puissiez le faire fonctionner - surtout s'il s'agit d'un service de réservation qui aura très probablement besoin pour fonctionner sur https/ssl.

Comme il semble que vous ayez un contrôle total sur le contenu de l'iframe, vous disposez de toutes les options, AJAX avec JSONP serait une option. Cependant, un mot d'avertissement. Si votre système de réservation est à plusieurs étapes vous devez vous assurer que vous disposez d'une interface utilisateur bien conçue - et éventuellement d'un code de gestion de l'historique/des fragments - si vous devez suivre la route AJAX. Tout cela parce que vous ne pouvez jamais savoir quand un l'utilisateur décidera de naviguer vers l'avant ou vers l'arrière dans son navigateur (qu'une iframe traiterait automatiquement, dans des limites raisonnables). Une interface utilisateur bien conçue peut empêcher les utilisateurs de le faire.

communication inter-domaines

Si vous contrôlez les deux côtés (ce qui semble être le cas), vous disposez également de l'option de communication interdomaine en utilisant window.postMessage - voir ici pour plus d'informations https://developer.mozilla.org/en-US/docs/DOM/window.postMessage

26
Pebbl

Le navigateur moderne et en partie IE8 ont de nouvelles fonctionnalités qui rendent cette tâche plus facile qu'auparavant.

PostMessage

L'API postMessage fournit une méthode simple pour communiquer entre un iFrame et son parent.

Pour envoyer un message à la page parent, vous l'appelez comme suit.

parent.postMessage('Hello parent','http://Origin-domain.com');

Dans l'autre sens, nous pouvons envoyer le message à l'iFrame avec le code suivant.

var iframe = document.querySelector('iframe');
iframe.contentWindow.postMessage('Hello my child', 'http://remote-domain.com:8080');

Pour recevoir un message, créez un écouteur d'événement pour l'événement de message.

function receiveMessage(event)
{
  if (event.Origin !== "http://remote-domain.com:8080")
    return;

  console.log(event.data);
}

if ('addEventListener' in window){
    window.addEventListener('message', receiveMessage, false);
} else if ('attachEvent' in window){ //IE
    window.attachEvent('onmessage', receiveMessage);

Ces exemples utilisent la propriété Origin pour limiter la destination du message et pour vérifier d'où il provient. Il est possible de spécifier * pour autoriser l'envoi vers n'importe quel domaine et dans certains cas, vous souhaiterez peut-être accepter des messages de n'importe quel domaine. Cependant, si vous faites cela, vous devez considérer les implications de sécurité et implémenter vos propres vérifications sur le message entrant pour vous assurer qu'il contient ce que vous attendez. Dans ce cas, l'iframe peut publier sa hauteur sur '*', car nous pourrions avoir plus d'un domaine parent. Cependant, c'est une bonne idée de vérifier que les messages entrants proviennent de l'iFrame.

function isMessageFromIFrame(event,iframe){
    var
        Origin  = event.Origin,
        src     = iframe.src;

    if ((''+Origin !== 'null') && (Origin !== src.substr(0,Origin.length))) {
        throw new Error(
            'Unexpect message received from: ' + Origin +
            ' for ' + iframe.id + '. Message was: ' + event.data  
        );
    }

    return true;
}

MutationObserver

L'autre avancée dans les navigateurs plus modernes est MutationObserver qui vous permet de surveiller les changements dans le DOM; il est donc désormais possible de détecter les modifications susceptibles d'affecter la taille de l'iFrame sans avoir à interroger en permanence avec setInterval.

function createMutationObserver(){
    var
        target = document.querySelector('body'),

        config = {
            attributes            : true,
            attributeOldValue     : false,
            characterData         : true,
            characterDataOldValue : false,
            childList             : true,
            subtree               : true
        },

        observer = new MutationObserver(function(mutations) {
            parent.postMessage('[iframeResize]'+document.body.offsetHeight,'*');
        });

    log('Setup MutationObserver');
    observer.observe(target, config);
}

var MutationObserver = window.MutationObserver || window.WebKitMutationObserver;

if (MutationObserver){
    createMutationObserver();
}

Déterminer une hauteur précise

Obtenir une hauteur précise pour l'iFrame n'est pas aussi simple qu'il devrait l'être, car vous avez le choix entre six propriétés différentes que vous pouvez vérifier et aucune d'entre elles ne donne une réponse constamment correcte. La meilleure solution que j'ai trouvée est cette fonction qui fonctionne tant que vous n'utilisez pas CSS pour déborder la balise body.

function getIFrameHeight(){
    function getComputedBodyStyle(prop) {
        return parseInt(
            document.defaultView.getComputedStyle(document.body, null),
            10
        );
    }

    return document.body.offsetHeight +
        getComputedBodyStyle('marginTop') +
        getComputedBodyStyle('marginBottom');
}

Ceci est la version IE9, pour la version beaucoup plus longue d'IE8 voir ceci réponse .

Si vous débordez le corps et que vous ne pouvez pas corriger votre code pour l'arrêter, utilisez les propriétés offsetHeight ou scrollHeight de document.documentElement sont vos meilleures options. Les deux ont des avantages et des inconvénients et il vaut mieux simplement tester les deux et voir ce qui fonctionne pour vous.

Autres issues

D'autres choses à considérer incluent, avoir plus d'un iFrame sur la page, CSS: Checkbox et: Hover événements provoquant le redimensionnement de la page, évitant l'utilisation de la hauteur automatique dans le corps des iFrames et les balises html et enfin la fenêtre en cours de redimensionnement.

Bibliothèque IFrame Resizer

J'ai enveloppé tout cela dans une simple bibliothèque sans dépendance, qui fournit également des fonctions supplémentaires non discutées ici.

https://github.com/davidjbradshaw/iframe-resizer

Cela fonctionne avec IE8 +.

20
David Bradshaw

J'ai écrit ce script et ça fonctionne parfaitement pour moi. Sentez-vous libre de l'utiliser!

function ResizeIframeFromParent(id) {
    if (jQuery('#'+id).length > 0) {
        var window = document.getElementById(id).contentWindow;
        var prevheight = jQuery('#'+id).attr('height');
        var newheight = Math.max( window.document.body.scrollHeight, window.document.body.offsetHeight, window.document.documentElement.clientHeight, window.document.documentElement.scrollHeight, window.document.documentElement.offsetHeight );
        if (newheight != prevheight && newheight > 0) {
            jQuery('#'+id).attr('height', newheight);
            console.log("Adjusting iframe height for "+id+": " +prevheight+"px => "+newheight+"px");
        }
    }
}

Vous pouvez appeler la fonction dans une boucle:

<script>
jQuery(document).ready(function() {
    // Try to change the iframe size every 2 seconds
    setInterval(function() {
        ResizeIframeFromParent('iframeid');
    }, 2000);
});
</script>
7
Bram Verstraten

utilisez ce script:

$(document).ready(function () {
  // Set specific variable to represent all iframe tags.
  var iFrames = document.getElementsByTagName('iframe');

  // Resize heights.
  function iResize() {
    // Iterate through all iframes in the page.
    for (var i = 0, j = iFrames.length; i < j; i++) {
    // Set inline style to equal the body height of the iframed content.
      iFrames[i].style.height = iFrames[i].contentWindow.document.body.offsetHeight + 'px';
    }
  }

 // Check if browser is Safari or Opera.
 if ($.browser.safari || $.browser.opera) {
    // Start timer when loaded.
    $('iframe').load(function () {
    setTimeout(iResize, 0);
    });

   // Safari and Opera need a kick-start.
   for (var i = 0, j = iFrames.length; i < j; i++) {
     var iSource = iFrames[i].src;
     iFrames[i].src = '';
     iFrames[i].src = iSource;
   }
 } else {
    // For other good browsers.
    $('iframe').load(function () {
    // Set inline style to equal the body height of the iframed content.
    this.style.height = this.contentWindow.document.body.offsetHeight + 'px';
    });
  }
});

Remarque: utilisez-le sur le serveur Web.

0
Jai