How to automatically adjust iframe height for embedded wordpress posts

I am currently working on a custom embed template for wordpress posts so I can embed them via wp discourse plugin in an iframe

Currently you can only add a fixed iframe height into the post. The post looks like this:

The HTML used:

<iframe src="https://wordpress-92041-921046.cloudwaysapps.com/growbox-dimensionieren/" width="1200" height="2000" "frameborder="0"></iframe>
  1. Is there any way to set up the iframe height in a variable manner to adjust to the embedded content size?
  2. Since it will be put into a wp discourse template (like @simon) did here , is there any way to set the iframe height in a dynamic way based on some parameters of the wp post? (character count or such?)
1 « J'aime »

J’essaie de faire la même chose, car j’ai réalisé qu’en intégrant mes articles WordPress sur Discourse, je pourrais simplement diriger mes utilisateurs vers Discourse et non vers deux sites différents.

Quelqu’un a-t-il trouvé comment faire cela ? Je lutte depuis un moment avec ça.

Utilisez-vous quelque chose comme ceci pour ajouter l’iframe :

function your_namespace_publish_format_html( $output ) {
    global $post;

    if ( 'my_iframe_post_type' === $post->post_type) {
        ob_start();

        ?>
    <iframe width="690" height="600" src="<?php echo esc_url( the_permalink() ); ?>" frameborder="0"></iframe>
    <?php
        $output = ob_get_clean();

        // Retourne un iframe pour ce type de publication.
        return $output;
    }

    // Retourne la sortie par défaut, ou fait autre chose ici.
    return $output;
}
add_filter( 'discourse_publish_format_html', 'your_namespace_publish_format_html' );

mais vous souhaitez définir la propriété height en fonction de la hauteur de la publication ?

Si oui, quelle doit être la précision ? Je me demande si vous pourriez simplement compter le nombre de caractères dans la publication, puis ajouter la hauteur de toutes les images ou autres éléments qui ont une hauteur définie, puis en ajouter un peu plus pour faire bonne mesure. J’ai peut-être quelques suggestions sur la façon de procéder.

Il serait également possible d’utiliser Javascript pour obtenir la hauteur exacte de la publication lorsqu’elle est rendue à une largeur donnée.

1 « J'aime »

Ah, je ne suis même pas arrivé aussi loin en termes d’intégration dans le filtre discourse_publish_format_html. J’ajoutais juste manuellement à un post dans Discourse et j’essayais de comprendre le javascript pour lire la hauteur du contenu à l’intérieur de l’iframe, puis redimensionner l’iframe.

J’ai vu pas mal de tutoriels sur la façon de faire cela en ligne, mais pour une raison quelconque, je n’arrive pas à faire fonctionner ce JS sur Discourse. J’ai essayé avec le changement de page et decorateCookedElement dans l’API, et pourtant je n’arrive toujours pas à le faire fonctionner.

Si possible, je préférerais le faire dans Discourse lui-même, afin de pouvoir redimensionner les iframes à pleine hauteur si j’intègre également à partir d’un site non WordPress.

C’est un problème intéressant. Que se passe-t-il si vous modifiez manuellement l’attribut height d’un élément iframe dans une publication Discourse ? Lorsque vous affichez la publication après avoir modifié la hauteur, la nouvelle hauteur est-elle utilisée, ou devez-vous retraiter la publication Discourse pour que la hauteur modifiée prenne effet ?

Edit : c’est assez intéressant pour que je teste cela plus tard dans la journée.

1 « J'aime »

Si je l’ajoute à la balise iframe avec height="400px", elle sera redimensionnée après que j’aie actualisé la page. Si je l’ajoute comme height="100%", cela ne semble rien faire.

Si j’ajoute des propriétés CSS à l’iframe, cela semble également modifier la hauteur correctement, du moins à ce que j’ai saisi manuellement.

Ugh, j’ai réussi. Le problème était que j’utilisais une classe personnalisée sur la balise iframe et Discourse supprime toutes ces balises. J’aurais dû tirer la leçon d’il y a quelques jours (Formatting posts to look more like Wordpress blog - #6 by jimkleiber). J’ai lu que c’était pour des raisons de sécurité que les classes HTML personnalisées ne fonctionnaient pas ici, mais je ne suis pas sûr pourquoi :confused:

Cependant, voici le code pour ajuster la hauteur d’un iframe avec le sélecteur .topic-body iframe (je ne suis pas sûr si ce code fonctionne pour les autres, il semble fonctionner pour moi) :

<script type="text/discourse-plugin" version="0.8.18">
    api.decorateCookedElement(
      element => {
        setTimeout(function() {
          let iframes = element.querySelectorAll('.topic-body iframe');
          if (iframes) {
            iframes.forEach(function(iframe) {
              iframe.onload = function() {
                let iframeDocument = this.contentDocument || this.contentWindow.document;
                let contentHeight = Math.max(
                  iframeDocument.body.scrollHeight,
                  iframeDocument.documentElement.scrollHeight
                ) + 'px';
                this.style.height = contentHeight;
              };
            });
          }
        }, 5000); // Ajustez le délai au besoin                  
      },
      { id: "component-id", onlyStream: true}
    );
</script>

EDIT : en fait, ça ne fonctionne pas. J’ai ajouté height="4000px" dans la balise iframe et c’est pourquoi cela fonctionnait, je pense.

1 « J'aime »

C’est un problème délicat. Je ne pense pas qu’il soit facile d’accéder au contenu de l’iframe depuis Discourse. Il pourrait être possible d’y parvenir si la source de l’iframe et votre site Discourse se trouvent sur des sous-domaines différents du même domaine.

Si je comprends bien le problème, vous devrez définir document.domain sur le sous-domaine d’où vous extrayez le contenu, et dans le script que vous exécutez sur Discourse, à l’aide du domaine racine.

Si la source de l’iframe est en fait votre domaine racine, essayez peut-être d’ajuster le script à :

<script type="text/discourse-plugin" version="0.8.18">
   document.domain = "votre_domaine_racine.com"; // modifiez ceci pour votre domaine
    api.decorateCookedElement(
      element => {

Si le domaine de l’iframe est également un sous-domaine de votre domaine racine, vous devrez également y définir document.domain sur le domaine racine.

Pour styliser les iframes (ou quoi que ce soit d’autre) sur Discourse, vous pouvez envelopper le contenu dans un div qui a un attribut de données :

<div data-full-height>
<iframe src="http://wp-discourse.test/zalg_iframe/this-is-a-test-this-is-only-a-test/" height="600" width="690"></iframe>
</div>

CSS du thème :

[data-full-height] > iframe {
      // vous pouvez éventuellement styliser l'iframe externe ici : hauteur, largeur, etc.
     // malheureusement, height: 100% ne fonctionnera pas - l'élément contenant de l'iframe n'a pas de hauteur définie.
}

Si l’approche document.domain ne fonctionne pas, il peut y avoir d’autres solutions - utiliser window.postMessage pour communiquer entre le document parent de l’iframe et Discourse.

Je ne pense pas que mon idée initiale de calculer la hauteur de l’iframe sur son site source fonctionnera - la largeur de l’iframe rendu variera en fonction de l’appareil sur lequel Discourse est consulté.

1 « J'aime »

Hmm, mon forum est un sous-domaine de mon domaine racine, j’ai donc ajouté le domaine racine comme vous l’avez suggéré et j’ai obtenu cette erreur :

Uncaught DOMException: Failed to read a named property 'document' from 'Window': Blocked a frame with origin 

J’essaie maintenant de faire la chose postMessage mais j’échoue. Je pense que j’ai besoin de mieux comprendre comment fonctionne le javascript de Discourse, je pense que je suppose qu’il fonctionne comme les autres sites et peut-être que ce n’est pas le cas, ou peut-être que c’est juste moi qui ne sais pas bien faire du javascript, surtout pour les trucs inter-origines lol.

Merci d’avoir jeté un coup d’œil à cela !

Je me posais la question depuis un moment. Voici une preuve de concept (notez qu’elle ne résout pas le problème de la suppression des barres de défilement des iframes).

Ajoutez une balise script au message que vous intégrez :

<script>
    function sendHeight() {
        const body = document.body,
            html = document.documentElement;

        const height = Math.max(body.scrollHeight, body.offsetHeight,
            html.clientHeight, html.scrollHeight, html.offsetHeight);

        window.parent.postMessage({
            'iframeHeight': height,
            'iframeId': 'zalgFrame' // Utilisez un identifiant unique si vous avez plusieurs iframes
        }, '*'); // Envisagez de spécifier le domaine parent pour la sécurité
    }

    // Envoyer la hauteur initiale
    window.onload = sendHeight;

    // Optionnel : Mettre à jour la hauteur lors du redimensionnement ou d'autres événements
    window.onresize = sendHeight;
</script>

J’utilise l’identifiant "zalgFrame" dans le script.

Dans votre thème Discourse :

<script type="text/discourse-plugin" version="1.29.0">
let iframeHeight, iframeId;
window.addEventListener('message', (event) => {
  if (event.origin !== "http://wp-discourse.test") return; // mon domaine de test, mettez-le à jour vers votre domaine ou commentez-le
  // récupère la hauteur de l'iframe passée depuis `wp-discourse.test` et confirme que l'iframeId correspond à l'iframeID que j'y ai défini
  if (event.data.iframeHeight && event.data.iframeId === 'zalgFrame') {
      // visitez la page Discourse avec l'iframe avec votre console ouverte
      // vous devriez voir les hauteurs mises à jour envoyées depuis le site parent lorsque vous redimensionnez la fenêtre
      console.log("we got an event:" + event.data.iframeHeight);
      iframeHeight = event.data.iframeHeight;
      iframeId = event.data.iframeId;
  }
  }, false);
</script>

Dans un message Discourse :

<div data-iframe-test-one>
<iframe src="http://wp-discourse.test/zalg_iframe/this-is-a-test-this-is-only-a-test/" width="100%" height="1659"></iframe>
</div>

Il est donc possible d’obtenir la hauteur réelle de l’iframe rendu depuis la fenêtre parente.

Je ne sais pas comment obtenir la hauteur à partir des données de l’écouteur d’événements pour l’appeler avec api.decorateCookedElement. Je ne suis pas sûr que cela suffirait à supprimer la barre de défilement verticale des iframes longs. Si j’essaie de coder en dur une grande hauteur (1600px) dans l’élément iframe, je me retrouve toujours avec une barre de défilement.

Edit : pour plus de complétude :

<script type="text/discourse-plugin" version="1.29.0">
api.decorateCookedElement(
  (e) => {
    let iframeHeight, iframeId;

    function handleMessage(event) {
      if (event.origin !== "http://wp-discourse.test") return;
      if (event.data.iframeHeight && event.data.iframeId === "zalgFrame") {
        iframeHeight = event.data.iframeHeight;
        iframeId = event.data.iframeId;
        // basé sur l'hypothèse qu'il n'y aura qu'un seul iframe enveloppé dans la div data-zalgFram
        let iframe = e.querySelector("[data-zalgFrame] iframe");
        if (iframe) {
          iframe.style.height = `${iframeHeight}px`;
        }
        // après avoir défini la hauteur réelle rendue de l'iframe
        // supprimer l'écouteur d'événements
        window.removeEventListener("message", handleMessage, false);
      }
    }
    window.addEventListener("message", handleMessage, false);
  },
  { id: "component-id", onlyStream: true }
);
</script>

Pour tout ce qui dépasse ~1000px de hauteur, il ne semble y avoir aucun moyen d’éviter qu’une barre de défilement ne soit ajoutée par Discourse, je ne recommande donc pas cette approche.

Je pense que la réponse à la question initiale est que c’est en quelque sorte possible, mais que cela n’apporte probablement pas grand-chose. (Sauf que j’ai découvert la méthode window.postMessage() :slight_smile:

2 « J'aime »

J’apprécie les efforts vaillants ici et je ne veux pas les atténuer, cependant je dois admettre que je suis un peu sceptique quant à la prémisse de ce sujet, c’est-à-dire :

J’ai deux questions (authentiques) à ce sujet Jim :

  1. Quelle est la raison pour laquelle vous souhaitez une iframe ici au lieu de la fonctionnalité d’intégration de sujet normale ?
  2. Je suis curieux de savoir pourquoi vous auriez encore un site Wordpress si vous ne voulez pas que les utilisateurs y consomment du contenu ?
1 « J'aime »

Je ne peux pas parler au nom de l’auteur du sujet original, mais je peux répondre dans mon cas :

J’ai bricolé pas mal de plugins sur WordPress qui me permettent d’avoir un lecteur de podcast avec des transcriptions interactives (les mots se surlignent pendant que l’audio joue et on peut cliquer pour sauter à cette partie de l’audio), des chapitres/notes d’émission interactifs, et une liste de lecture consultable/triable/filtrable.

Donc, pour simplement les intégrer ici sans iframe, je ne pourrais pas accéder au javascript et à tout le style que j’y ai mis.

Oh, et je trouve beaucoup plus facile pour moi de bricoler ces choses sur WordPress que sur Discourse, j’ai vraiment du mal à faire du javascript et des plugins ici.

Pour héberger les épisodes de podcast, j’aurai de toute façon besoin du site WordPress. Mais quant à savoir si je veux que les utilisateurs y consomment du contenu, je ne suis pas sûr. Depuis que j’utilise Discourse pour les commentaires sur WordPress, l’interactivité a diminué. J’avais l’habitude d’avoir des gens qui postaient dans les commentaires WordPress, mais Discourse leur demande de franchir un seuil de domaine, et ensuite ils interagissent dans un endroit séparé. Si Discourse facilitait l’intégration de contenu sur un forum WordPress, je me concentrerais probablement là-dessus.

Je ne suis pas sûr que ce soit nécessaire, j’ai juste le sentiment de vouloir avoir un lieu principal où les gens puissent se rassembler, et avant je pensais que ce serait WordPress avec des commentaires/publications Discourse intégrés, mais maintenant je pense que Discourse avec des publications WordPress intégrées pourrait être plus facile et plus susceptible d’inciter les gens à interagir les uns avec les autres.

2 « J'aime »

Cool !

Pourquoi pas ?

Je comprends ! Néanmoins, en fonction de votre réponse à ma question précédente (“Pourquoi pas ?”), les intégrer correctement dans un post Discourse serait une approche plus stable qu’une iframe dynamique.

Je suis désolé d’apprendre cette baisse, et je comprends aussi ce que vous voulez dire ici. Le sujet plus large de Simon sur ce thème me vient à l’esprit

1 « J'aime »

Je veux dire, peut-être que je pourrais ? J’ai eu du mal à faire fonctionner un lecteur audio avec mediaelement.js ici auparavant, je pense que je ne comprends pas très bien l’API des plugins. Cela semble juste être beaucoup de travail que je pourrais peut-être faire à long terme, mais pour l’instant, cela semble très bien avec l’intégration iframe. Le principal défi serait la recherche du texte intégré dans l’iframe, mais je pensais publier ce texte dans le message et le masquer ou le mettre sous un accordéon, afin qu’il apparaisse toujours dans les recherches.

De plus, je pense que le plus gros problème est en fait que tant de classes HTML sont supprimées lorsque le contenu est cuit (ou quel que soit le jargon lol), et donc essayer simplement de publier le message WordPress ici et d’utiliser un CSS similaire semble nécessiter beaucoup de réécriture, ce qui m’a inspiré à écrire ceci :

1 « J'aime »

Je vois. Laissez-moi réfléchir à cela un peu. Je n’ai pas d’aperçu significatif sur les iframes dynamiques au-delà de ce que Simon a déjà suggéré, cependant votre cas me fait réfléchir un peu.

1 « J'aime »

Il est peut-être utile de noter que je travaille activement sur ce sujet (en utilisant Discourse pour alimenter le système de commentaires des sites Web). Je me concentre principalement sur les sites WordPress headless pour le moment, mais l’approche générale pourrait être utile pour les sites WordPress classiques et non-WordPress.

2 « J'aime »

Je ne me souviens plus où je l’ai vu, mais il y a une hauteur maximale cachée de je pense 1000px, peut-être sur le contenu cuit ?

Alors peut-être que cela interfère avec votre solution.

J’y jetterai un œil demain :folded_hands:t2:

1 « J'aime »

C’est sur les éléments iframe :

iframe {
  max-width: 100%;
  max-height: #{\"min(1000px, 200vh)\"};
}

Cela peut être corrigé dans un thème en ciblant les iframe avec l’attribut data :

[data-zalgFrame] > iframe {
    max-height: 100%;
    border: none;
}

Ce changement doit être effectué pour afficher des iframe plus longs, mais les barres de défilement que je voyais provenaient de la page source de l’iframe. La seule façon d’obtenir de bons résultats est de créer des versions intégrées des publications sur mon blog - en gros, supprimer tout sauf le contenu de la publication et jouer un peu avec les styles. Par exemple, avec un type de publication personnalisé WordPress :

single-zag_iframe.php
<?php
if ( have_posts() ) : while ( have_posts() ) : the_post();
?>
<style>
    body {
        overflow: hidden;
        height: 100%;
    }
    article.zalg-iframe {
        width: 100%;
        height: 100%;
        margin-left: auto;
        margin-right: auto;
        font-size: 1.25em;
        word-break: break-word;
    }
    article.zalg-iframe img {
        max-width: 100%;
        height: auto;
    }
</style>
<article class="zalg-iframe">
    <?php
    the_content();
    ?>
</article>
<script>
    function sendHeight() {
        const body = document.body,
            html = document.documentElement;
        // un peu au hasard, je pense que `scrollHeight` est la cible correcte dans ce cas
        const height = Math.max(body.scrollHeight, body.offsetHeight,
            html.clientHeight, html.scrollHeight, html.offsetHeight);

        window.parent.postMessage({
            'iframeHeight': height,
            'iframeId': 'zalgFrame'
        }, '*');
    }

    // Envoyer la hauteur initiale
    window.onload = sendHeight;

    // Optionnel : Mettre à jour la hauteur lors du redimensionnement ou d'autres événements
    window.onresize = sendHeight;
</script>
<?php
endwhile; endif;
?>

Il faudra peut-être un peu de bricolage pour que cela fonctionne sur un site de production, mais cela semble valoir la peine d’être examiné de plus près.

Ok, ça fonctionne, je pense que j’avais un bug ailleurs en JS qui empêchait ça. Youpi, ça semble très bien intégré à mon site. Merci beaucoup @simon !

1 « J'aime »