Impostare esattamente l'altezza di un iframe esterno in base al contenuto con JavaScript

Un iframe esterno rappresenta un caso particolare di gestione della sicurezza cross-origin che ci permette di approfondire l'argomento della comunicazione tra finestre (window) in JavaScript.

Il problema

Abbiamo un elemento iframe sul nostro sito che carica un widget HTML da un altro sito. Per impostazione predefinita, gli iframe necessitano di un'altezza ma se il loro contenuto eccederà in tal senso, sul nostro sito verrà mostrata una barra di scorrimento verticale sull'elemento.

Sappiamo dalla teoria che la proprietà document.documentElement.scrollHeight rappresenta l'altezza complessiva dei contenuti di un documento HTML. In questo caso, tuttavia, se proviamo ad accedere al DOM del nostro iframe, otterremo:

Uncaught SecurityError: Failed to read a named property 'document' from 'Window': Blocked a frame with origin "https://nostrosito.tld" from accessing a cross-origin frame.

L'errore ci informa che poiché il documento HTML incluso nell'iframe si trova su un altro sito (diversa origin), non possiamo accedere al suo oggetto window direttamente. Si tratta di una normale precauzione di sicurezza che ci permette di introdurre un concetto chiave: la comunicazione tra finestre (window).

La finestra genitore

Quando si usano gli iframe, viene creato un rapporto genitore-figlio tra la pagina che include l'iframe e la pagina remota inclusa dall'iframe.

Si dice infatti che la pagina del nostro sito è il genitore (parent) della pagina remota. Quindi se nel codice JavaScript della pagina remota inseriamo window.parent otterremo un riferimento all'oggetto window della pagina che include l'iframe.

In pratica, avviene l'opposto di quanto stavamo cercando di ottenere in precedenza e che ci ha condotti all'errore: dal figlio al genitore e non viceversa.

La comunicazione sicura tra finestre avviene tramite il metodo window.postMessage() che ci consente di inviare un oggetto di dati.

Quindi nel codice JavaScript della pagina remota possiamo inviare alla finestra genitore l'altezza del documento in questo modo:

(function () {
  function notifyHeight() {
    const height = document.documentElement.scrollHeight;
    window.parent.postMessage({ type: 'widget-height', height }, '*');
  }

  document.addEventListener('DOMContentLoaded', () => {
    window.addEventListener('load', notifyHeight);
  });
})();

E nel codice JavaScript del nostro sito, possiamo gestire l'evento message dell'oggetto window andando ad impostare dinamicamente l'altezza dell'iframe inviataci tramite postMessage().

(function () {
  function handleIframeHeight() {
    const iframe = document.querySelector('iframe');
    if (!iframe) return;
    window.addEventListener('message', function (e) {
      if (e.data && e.data.type === 'widget-height') {
        iframe.style.height = e.data.height + 'px';
      }
    });
  }

  document.addEventListener('DOMContentLoaded', handleIframeHeight, false);
})();

Si tratta di una soluzione molto efficace e sicura che ci permette di evitare errori di sicurezza nel browser.

Demo

JavaScript External Iframe Height

Conclusione

La comunicazione tra finestre tramite dati è sicuramente una caratteristica molto interessante che ci permette di evitare improvvisati workaround che non possiedono la sicurezza e l'affidabilità dei metodi nativi.