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 me gusta

Estoy intentando hacer esto también, ya que me di cuenta de que si incrusto mis publicaciones de WordPress en Discourse, entonces puedo dirigir a mis usuarios a Discourse y no a dos sitios diferentes.

¿Alguien ha descubierto cómo hacer esto? He estado luchando con esto durante un tiempo.

¿Estás usando algo como esto para añadir el 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();

        // Devuelve un iframe para este tipo de publicación.
        return $output;
    }

    // Devuelve la salida predeterminada, o haz algo más con ella aquí.
    return $output;
}
add_filter( 'discourse_publish_format_html', 'your_namespace_publish_format_html' );

pero queriendo establecer la propiedad height basándote en la altura de la publicación?

Si es así, ¿qué tan exacto necesita ser? Me pregunto si podrías simplemente contar el número de caracteres de la publicación, luego sumar la altura de cualquier imagen u otro elemento que tenga una altura establecida, y luego añadir un poco más por si acaso. Podría tener algunas sugerencias sobre cómo hacerlo.

También podría ser posible usar Javascript para obtener la altura exacta de la publicación cuando se renderiza a un ancho determinado.

1 me gusta

Ah, ni siquiera he llegado tan lejos en términos de integrarlo en el filtro discourse_publish_format_html. Simplemente lo estaba agregando manualmente a una publicación en Discourse y tratando de averiguar el javascript para leer la altura del contenido dentro del iframe y luego cambiar el tamaño del iframe.

He visto bastantes tutoriales sobre cómo hacer eso en línea, pero por alguna razón no puedo hacer que ese js funcione en Discourse. Lo intenté con el cambio de página y decorateCookedElement en la API, y aún así no pude hacerlo funcionar.

Si es posible, preferiría hacerlo en Discourse, para poder cambiar el tamaño de los iframes a altura completa si también incrusto desde un sitio que no sea de WordPress.

Es un problema interesante. ¿Qué sucede si editas manualmente el atributo height de un elemento iframe en una publicación de Discourse? Cuando ves la publicación después de editar la altura, ¿se utiliza la nueva altura o tienes que volver a hornear la publicación de Discourse para que la altura editada tenga efecto?

Editar: es lo suficientemente interesante como para que lo pruebe hoy más tarde.

1 me gusta

Si lo agrego a la etiqueta iframe con height="400px", se redimensionará después de que actualice la página. Si lo agrego como height="100%", no parece hacer nada.

Si agrego propiedades CSS sobre el iframe, también parece cambiar la altura correctamente, al menos a lo que ingreso manualmente.

Ugh, lo conseguí. El problema era que estaba usando una clase personalizada en la etiqueta iframe y Discourse elimina todas esas etiquetas. Debería haber aprendido mi lección de hace unos días (Formatting posts to look more like Wordpress blog - #6 by jimkleiber). He leído que es por razones de seguridad por las que las clases HTML personalizadas no funcionan aquí, pero no estoy seguro de por qué :confused:

Sin embargo, aquí está el código para ajustar la altura de un iframe con el selector .topic-body iframe (no estoy seguro de si este código funciona para otros, parece que funciona para mí):

<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); // Ajusta el retraso según sea necesario                  
      },
      { id: "component-id", onlyStream: true}
    );
</script>

EDIT: en realidad, no funciona. Añadí height="4000px" en la etiqueta iframe y por eso funcionaba, creo.

1 me gusta

Es un problema complicado. No creo que sea fácil acceder al contenido del iframe desde dentro de Discourse. Podría ser posible hacerlo funcionar si la fuente del iframe y tu sitio de Discourse están en subdominios diferentes del mismo dominio.

Si entiendo correctamente el problema, necesitarías establecer document.domain tanto en el subdominio del que obtienes el contenido como en el script que ejecutas en Discourse, al dominio raíz.

Si la fuente del iframe es en realidad tu dominio raíz, quizás intentes ajustar el script a:

<script type="text/discourse-plugin" version="0.8.18">
   document.domain = "your_root_domain.com"; // edita eso a tu dominio
    api.decorateCookedElement(
      element => {

Si el dominio del iframe también es un subdominio de tu dominio raíz, necesitarías establecer document.domain al dominio raíz también en él.

Para estilizar iframes (o cualquier cosa) en Discourse, puedes envolver el contenido en un div que tenga un atributo de datos:

<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 del tema:

[data-full-height] > iframe {
      // opcionalmente estiliza el iframe externo aquí: alto, ancho, etc.
     // desafortunadamente height: 100% no funcionará - el elemento contenedor del iframe no tiene una altura establecida.
}

Si el enfoque de document.domain no funciona, puede haber otras soluciones: usar window.postMessage para comunicarte entre el documento padre del iframe y Discourse.

No creo que mi idea inicial de calcular la altura del iframe en su sitio de origen funcione: el ancho del iframe renderizado variará dependiendo del dispositivo en el que se vea Discourse.

1 me gusta

Hmm, mi foro es un subdominio de mi dominio raíz, así que agregué el dominio raíz como sugeriste y obtuve este error:

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

Ahora estoy intentando hacer la cosa de postMessage pero estoy fallando. Creo que necesito entender mejor cómo funciona el javascript de Discourse, creo que asumo que funciona como otros sitios y tal vez no lo hace, o tal vez solo soy yo que no sé hacer javascript muy bien, especialmente para cosas de origen cruzado lol.

¡Gracias por echarle un vistazo a esto!

Me he estado preguntando sobre esto durante un tiempo. Aquí hay una prueba de concepto (ten en cuenta que no resuelve el problema de eliminar las barras de desplazamiento de los iframes).

Agrega una etiqueta de script a la publicación que estás incrustando:

<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' // Usa un identificador único si tienes varios iframes
        }, '*'); // Considera especificar el dominio padre por seguridad
    }

    // Envía la altura inicial
    window.onload = sendHeight;

    // Opcional: Actualiza la altura al redimensionar u otros eventos
    window.onresize = sendHeight;
</script>

Estoy usando el identificador "zalgFrame" en el script.

En tu tema de 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; // mi dominio de prueba, actualízalo a tu dominio o coméntalo
  // obtiene la altura del iframe que se pasa desde `wp-discourse.test` y confirma que iframeId coincide con el iframeID que establecí allí
  if (event.data.iframeHeight && event.data.iframeId === 'zalgFrame') {
      // visita la página de Discourse con el iframe con tu consola abierta
      // deberías ver alturas actualizadas que se envían desde el sitio padre a medida que redimensionas la ventana
      console.log("recibimos un evento:" + event.data.iframeHeight);
      iframeHeight = event.data.iframeHeight;
      iframeId = event.data.iframeId;
  }
  }, false);
</script>

En una publicación de 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>

Así que es posible obtener la altura real del iframe renderizado desde la ventana padre.

No sé cómo obtener la altura de los datos del listener de eventos en una llamada a api.decorateCookedElement. No estoy seguro de que eso funcione para eliminar la barra de desplazamiento vertical de los iframes largos. Si intento codificar una altura grande (1600px) en el elemento iframe, todavía termino con una barra de desplazamiento.

Editar: por completitud:

<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;
        // basado en la suposición de que solo habrá 1 iframe envuelto en el div data-zalgFram
        let iframe = e.querySelector("[data-zalgFrame] iframe");
        if (iframe) {
          iframe.style.height = `${iframeHeight}px`;
        }
        // después de establecer la altura real renderizada del iframe
        // elimina el listener de eventos
        window.removeEventListener("message", handleMessage, false);
      }
    }
    window.addEventListener("message", handleMessage, false);
  },
  { id: "component-id", onlyStream: true }
);
</script>

Para cualquier altura superior a ~1000px, no parece haber forma de evitar que Discourse agregue una barra de desplazamiento, por lo que no recomiendo el enfoque.

Creo que la respuesta a la OP es que es algo posible, pero probablemente no logra mucho. (Excepto que aprendí sobre el método window.postMessage() :slight_smile:

2 Me gusta

Aprecio los valientes esfuerzos aquí y no quiero desanimarlos, sin embargo, debo admitir que me siento un poco escéptico sobre la premisa de este tema, es decir:

Tengo dos preguntas (genuinas) sobre esto, Jim:

  1. ¿Cuál es la razón por la que quieres un iframe aquí en lugar de la funcionalidad normal de incrustación de temas?
  2. Me pregunto por qué todavía tendrías un sitio de WordPress si no quieres que los usuarios consuman contenido allí.
1 me gusta

No puedo hablar por el autor original del tema, pero puedo responder en mi caso:

He modificado bastantes plugins en WordPress que me permiten tener un reproductor de podcasts con transcripciones interactivas (las palabras se resaltan a medida que se reproduce el audio y se puede hacer clic para saltar a esa parte del audio), capítulos/notas del programa interactivos y una lista de reproducción que se puede buscar/ordenar/filtrar.

Así que, para incrustarlos aquí sin un iframe, no podría acceder a JavaScript y a todo el estilo que he implementado.

Ah, y me resulta mucho más fácil crear esas cosas en WordPress que en Discourse, me cuesta mucho hacer JavaScript y plugins aquí.

Para alojar los episodios de podcast, necesitaré el sitio de WordPress de todos modos. Pero en cuanto a si quiero que los usuarios consuman contenido allí, no estoy seguro. Desde que he estado usando Discourse para comentarios en WordPress, la interactividad ha disminuido. Solía tener gente publicando en los comentarios de WordPress, pero Discourse les exige que crucen un umbral de dominio y luego interactúan en un lugar separado. Si Discourse facilitara que la gente publicara de forma incrustada en un foro de WordPress, probablemente me centraría en eso.

No estoy seguro de si es necesario, simplemente tengo la sensación de que quiero tener un lugar principal para que la gente se reúna, y antes pensaba que sería WordPress con comentarios/publicaciones de Discourse incrustados, pero ahora estoy pensando que Discourse con publicaciones de WordPress incrustadas podría ser más fácil y más probable que inspire a la gente a interactuar entre sí.

2 Me gusta

¡Genial!

¿Por qué no?

¡Entiendo! Sin embargo, dependiendo de tu respuesta a mi pregunta anterior (“¿Por qué no?”), incrustarlos correctamente en una publicación de Discourse sería un enfoque más estable que un iframe dinámico.

Lamento escuchar sobre la disminución, y también entiendo lo que quieres decir aquí. Me viene a la mente el tema más amplio de Simon sobre este tema.

1 me gusta

Quiero decir, ¿tal vez podría? Me costó usar un reproductor de audio con mediaelement.js antes aquí, creo que no entiendo muy bien la API de plugins. Simplemente parece mucho trabajo que tal vez podría hacer a largo plazo, pero ahora mismo, se ve bastante bien con la incrustación de iframe. El principal desafío sería la capacidad de búsqueda del texto que está incrustado en el iframe, pero estaba pensando en publicar ese texto en la publicación y ocultarlo o ponerlo debajo de un acordeón, para que aún apareciera en las búsquedas.

Además, creo que el problema mayor es que muchas de las clases HTML se eliminan cuando el contenido se procesa (o como sea la jerga, jajaja), y así que simplemente intentar publicar la publicación de WordPress aquí y usar un CSS similar parece requerir mucha reescritura, lo que me inspiró a escribir esto:

1 me gusta

Entiendo. Déjame pensar un poco en esto. No tengo ninguna idea importante sobre iframes dinámicos más allá de lo que Simon ya ha sugerido, sin embargo, tu caso me hace pensar un poco.

1 me gusta

Quizás valga la pena señalar que estoy trabajando activamente en esto (utilizando Discourse para potenciar el sistema de comentarios de sitios web). Me estoy centrando principalmente en sitios de WordPress sin cabeza en este momento, pero el enfoque general podría ser útil para sitios de WordPress normales y no WordPress.

2 Me gusta

No recuerdo dónde lo vi, pero hay una altura máxima oculta de creo que 1000px, ¿quizás en el contenido cocido?

Así que tal vez eso esté interfiriendo con tu solución.

Lo miraré mañana :folded_hands:t2:

1 me gusta

Está en los elementos iframe:

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

Eso se puede arreglar en un tema apuntando a los iframes con el atributo de datos:

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

Ese cambio debe hacerse para mostrar iframes más largos, pero las barras de desplazamiento que estaba viendo provenían de la página de origen del iframe. La única forma en que obtengo buenos resultados es creando versiones incrustadas de las publicaciones en mi blog, básicamente eliminando todo excepto el contenido de la publicación y jugando un poco con los estilos. Por ejemplo, con un tipo de publicación personalizado de 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 poco de azar, creo que `scrollHeight` es el objetivo correcto para este caso
        const height = Math.max(body.scrollHeight, body.offsetHeight,
            html.clientHeight, html.scrollHeight, html.offsetHeight);

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

    // Enviar altura inicial
    window.onload = sendHeight;

    // Opcional: Actualizar altura al redimensionar u otros eventos
    window.onresize = sendHeight;
</script>
<?php
endwhile; endif;
?>

Puede que lleve un poco de tiempo ajustar esto para que funcione en un sitio de producción, pero parece que vale la pena investigarlo un poco más.

Ok, creo que esto está funcionando, creo que tenía un error en otra parte de js que lo estaba impidiendo. ¡Bien hecho! Parece muy bien integrado en mi sitio. ¡Muchas gracias @simon!

1 me gusta