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 curtida

Estou tentando fazer isso também, pois percebi que se eu incorporar minhas postagens do WordPress no Discourse, poderei direcionar meus usuários para o Discourse e não para dois sites diferentes.

Alguém descobriu como fazer isso? Tenho lutado com isso há um tempo.

Você está usando algo como isto para adicionar o 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();

        // Retorna um iframe para este tipo de post.
        return $output;
    }

    // Retorna a saída padrão, ou faz algo mais com ela aqui.
    return $output;
}
add_filter( 'discourse_publish_format_html', 'your_namespace_publish_format_html' );

mas querendo definir a propriedade height com base na altura do post?

Se sim, quão exato precisa ser? Estou me perguntando se você poderia apenas contar o número de caracteres no post, depois adicionar a altura de quaisquer imagens ou outros elementos que tenham uma altura definida, e então adicionar um pouco mais para garantir. Eu posso ter algumas sugestões sobre como fazer isso.

Também pode ser possível usar Javascript para obter a altura exata do post quando renderizado em uma determinada largura.

1 curtida

Ah, eu ainda nem cheguei a esse ponto de integrá-lo ao filtro discourse_publish_format_html. Eu estava apenas adicionando manualmente a um post no Discourse e tentando descobrir o javascript para ler a altura do conteúdo dentro do iframe e, em seguida, redimensionar o iframe.

Eu vi muitos tutoriais sobre como fazer isso online, mas por algum motivo não consigo fazer esse js funcionar no Discourse. Tentei com a mudança de página e o decorateCookedElement na API, e ainda assim não consigo fazer funcionar.

Se possível, eu preferiria fazer isso no próprio Discourse, para que eu possa redimensionar iframes para a altura total se eu incorporar de um site não-WordPress também.

É um problema interessante. O que acontece se você editar manualmente o atributo height de um elemento iframe em uma postagem do Discourse? Ao visualizar a postagem após editar a altura, a nova altura é usada ou você precisa refazer a postagem do Discourse para que a altura editada tenha efeito?

Editar: é interessante o suficiente que testarei isso ainda hoje.

1 curtida

Se eu adicioná-lo à tag iframe com height="400px", ele redimensionará após a atualização da página. Se eu adicioná-lo como height="100%", ele não parece fazer nada.

Se eu adicionar propriedades CSS sobre o iframe, ele também parece mudar a altura corretamente, pelo menos para o que eu insiro manualmente.

Ugh, consegui fazer funcionar. O problema foi que eu estava usando uma classe personalizada na tag iframe e o Discourse remove todas essas tags. Eu deveria ter aprendido minha lição há alguns dias (Formatting posts to look more like Wordpress blog - #6 by jimkleiber). Li que é por um motivo de segurança que classes HTML personalizadas não funcionam aqui, mas não tenho certeza do porquê :confused:

No entanto, aqui está o código para ajustar a altura de um iframe com o seletor .topic-body iframe (não tenho certeza se este código funciona para outros, parece funcionar para mim):

<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); // Ajuste o atraso conforme necessário                  
      },
      { id: "component-id", onlyStream: true}
    );
</script>

EDIT: na verdade, não funciona. Adicionei height="4000px" na tag iframe e é por isso que estava funcionando, eu acho.

1 curtida

É um problema complicado. Não acho que seja fácil acessar o conteúdo do iframe de dentro do Discourse. Poderia ser possível fazê-lo funcionar se a origem do iframe e o seu site Discourse estiverem em subdomínios diferentes do mesmo domínio.

Se eu estiver entendendo o problema corretamente, você precisaria definir document.domain tanto no subdomínio de onde você está obtendo o conteúdo quanto no script que você está executando no Discourse para o domínio raiz.

Se a origem do iframe for realmente o seu domínio raiz, talvez tente ajustar o script para:

<script type="text/discourse-plugin" version="0.8.18">
   document.domain = "seu_dominio_raiz.com"; // edite isso para o seu domínio
    api.decorateCookedElement(
      element => {

Se o domínio do iframe também for um subdomínio do seu domínio raiz, você precisaria definir document.domain para o domínio raiz nele também.

Para estilizar iframes (ou qualquer coisa) no Discourse, você pode envolver o conteúdo em um div que tenha um atributo de dados:

<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 do Tema:

[data-full-height] > iframe {
      // opcionalmente estilize o iframe externo aqui: altura, largura, etc
     // infelizmente height: 100% não funcionará - o elemento contêiner do iframe não tem uma altura definida.
}

Se a abordagem document.domain não funcionar, pode haver outras soluções - usar window.postMessage para se comunicar entre o documento pai do iframe e o Discourse.

Não acho que minha ideia inicial de calcular a altura do iframe em seu site de origem funcionará - a largura do iframe renderizado variará dependendo do dispositivo em que o Discourse é visualizado.

1 curtida

Hmm, meu fórum é um subdomínio do meu domínio raiz, então adicionei o domínio raiz como você sugeriu e recebi este erro:

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

Agora estou tentando fazer a coisa do postMessage, mas falhando. Acho que preciso entender melhor como o JavaScript do Discourse funciona, acho que assumo que ele funciona como outros sites e talvez não funcione, ou talvez seja apenas eu não sabendo fazer JavaScript tão bem, especialmente para coisas cross-origin lol.

Obrigado por dar uma olhada nisso!

Estive pensando sobre isso por um tempo. Aqui está uma prova de conceito (note que ela não resolve o problema de remover as barras de rolagem dos iframes).

Adicione uma tag de script à postagem que você está incorporando:

<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' // Use um identificador exclusivo se você tiver vários iframes
        }, '*'); // Considere especificar o domínio pai para segurança
    }

    // Envia a altura inicial
    window.onload = sendHeight;

    // Opcional: Atualiza a altura em redimensionamento ou outros eventos
    window.onresize = sendHeight;
</script>

Estou usando o identificador "zalgFrame" no script.

Em seu tema do 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; // meu domínio de teste, atualize para o seu domínio ou comente
  // obtém a altura do iframe que é passada de `wp-discourse.test` e confirma que o iframeId corresponde ao iframeID que defini lá    
  if (event.data.iframeHeight && event.data.iframeId === 'zalgFrame') {
      // visite a página do Discourse com o iframe com seu console aberto
      // você deverá ver as alturas atualizadas sendo enviadas do site pai conforme você redimensiona a janela
      console.log("recebemos um evento:" + event.data.iframeHeight); 
      iframeHeight = event.data.iframeHeight;
      iframeId = event.data.iframeId;
  }
  }, false);
</script>

Em uma postagem do 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>

Portanto, é possível obter a altura real do iframe renderizado da janela pai.

Não sei como obter a altura dos dados do listener de eventos em uma chamada para api.decorateCookedElement, porém. Não tenho certeza se isso funcionaria para remover a barra de rolagem vertical de iframes longos. Se eu tentar codificar uma altura grande (1600px) no elemento iframe, ainda acabo com uma barra de rolagem.

Editar: para fins de completude:

<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;
        // com base na suposição de que haverá apenas 1 iframe envolvido na div data-zalgFram
        let iframe = e.querySelector("[data-zalgFrame] iframe");
        if (iframe) {
          iframe.style.height = `${iframeHeight}px`;
        }
        // após definir a altura real renderizada do iframe
        // remova o listener de eventos
        window.removeEventListener("message", handleMessage, false);
      }
    }
    window.addEventListener("message", handleMessage, false);
  },
  { id: "component-id", onlyStream: true }
);
</script>

Para qualquer coisa com mais de ~1000px de altura, não parece haver nenhuma maneira de evitar que uma barra de rolagem seja adicionada pelo Discourse, então não estou recomendando a abordagem.

Acho que a resposta para o OP é que é meio que possível, mas provavelmente não realiza muito. (Exceto que aprendi sobre o método window.postMessage() :slight_smile:

2 curtidas

Agradeço os esforços valentes aqui e não quero diminuí-los, no entanto, devo admitir que me sinto um pouco cético quanto à premissa deste tópico, ou seja:

Tenho duas perguntas (genuínas) sobre isso, Jim:

  1. Qual o motivo de você querer um iframe aqui em vez da funcionalidade normal de incorporação de tópicos?
  2. Fico curioso para saber por que você ainda teria um site WordPress se não quisesse que os usuários consumissem conteúdo lá?
1 curtida

Não posso falar pelo autor do tópico original, mas posso dar respostas no meu caso:

Eu montei vários plugins no WordPress que me permitem ter um player de podcast com transcrições interativas (as palavras destacam conforme o áudio toca e pode-se clicar para pular para essa parte do áudio), capítulos/notas do show interativos e uma playlist pesquisável/ordenável/filtrável.

Portanto, para apenas incorporá-los aqui sem um iframe, eu não conseguiria acessar o javascript e todo o estilo que coloquei nele.

Ah, e acho muito mais fácil para mim montar essas coisas no WordPress do que no Discourse, eu realmente tenho dificuldade em fazer javascript e plugins aqui.

Para hospedar os episódios de podcast, precisarei do site WordPress de qualquer maneira. Mas se quero que os usuários consumam conteúdo lá, não tenho certeza. Desde que tenho usado o Discourse para comentários no WordPress, a interatividade diminuiu. Eu costumava ter pessoas postando em comentários do WordPress, mas o Discourse exige que elas cruzem um limite de domínio e, em seguida, elas estão interagindo em um lugar separado. Se o Discourse facilitasse para as pessoas postarem de forma incorporada em um fórum do WordPress, eu provavelmente me concentraria nisso.

Não tenho certeza se é necessário, apenas tenho a sensação de que quero ter um lugar principal para as pessoas se reunirem, e antes pensei que seria o WordPress com comentários/postagens incorporadas do Discourse, mas agora estou pensando que o Discourse com posts incorporados do WordPress pode ser mais fácil e mais provável de inspirar as pessoas a interagir umas com as outras.

2 curtidas

Legal!

Por que não?

Eu entendo! No entanto, dependendo da sua resposta à minha pergunta anterior (“Por que não?”), incorporá-los corretamente em uma postagem do Discourse seria uma abordagem mais estável do que um iframe dinâmico.

Sinto muito por saber da queda, e também entendo o que você quer dizer aqui. O tópico mais amplo de Simon sobre esse tema vem à mente

1 curtida

Quer dizer, talvez eu pudesse? Tive dificuldade em fazer um reprodutor de áudio usar mediaelement.js aqui antes, acho que não entendo muito bem a API de plugins. Parece muito trabalho que talvez eu pudesse fazer a longo prazo, mas no momento, está realmente muito bom com o embed de iframe. O principal desafio seria a capacidade de pesquisa do texto que está incorporado no iframe, mas eu estava pensando em publicar esse texto na postagem e escondê-lo ou colocá-lo sob um acordeão, para que ele ainda aparecesse nas pesquisas.

Além disso, acho que o problema maior é que muitas das classes HTML são removidas quando o conteúdo é “cozido” (ou seja qual for o termo, rs), e então simplesmente tentar publicar a postagem do WordPress aqui e usar um CSS semelhante parece exigir muita reescrita, o que me inspirou a escrever isto:

1 curtida

Entendo. Deixe-me pensar um pouco sobre isso. Não tenho nenhuma visão significativa sobre iframes dinâmicos além do que Simon já sugeriu, no entanto, seu caso está me fazendo pensar um pouco.

1 curtida

Talvez valha a pena notar que estou trabalhando ativamente nisso (usando o Discourse para alimentar o sistema de comentários de sites). Focado principalmente em sites WordPress headless no momento, mas a abordagem geral pode ser útil para sites WordPress regulares e não WordPress.

2 curtidas

Não me lembro onde vi, mas há uma altura máxima oculta de acho que 1000px, talvez no conteúdo cozido?

Então, talvez isso esteja atrapalhando sua solução.

Vou dar uma olhada amanhã :folded_hands:t2:

1 curtida

Está em elementos iframe:

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

Isso pode ser corrigido em um tema direcionando iframes com o atributo de dados:

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

Essa alteração precisa ser feita para exibir iframes mais longos, mas as barras de rolagem que eu estava vendo vinham da página de origem do iframe. A única maneira de obter bons resultados é criar versões incorporadas de postagens em meu blog - basicamente, remover tudo, exceto o conteúdo da postagem e mexer um pouco nos estilos. Por exemplo, com um tipo de postagem personalizada do 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;
        // um pouco de tentativa e erro, acho que `scrollHeight` é o alvo correto para este caso
        const height = Math.max(body.scrollHeight, body.offsetHeight,
            html.clientHeight, html.scrollHeight, html.offsetHeight);

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

    // Envia a altura inicial
    window.onload = sendHeight;

    // Opcional: Atualiza a altura ao redimensionar ou em outros eventos
    window.onresize = sendHeight;
</script>
<?php
endwhile; endif;
?>

Pode levar um pouco de ajuste para fazer isso funcionar em um site de produção, mas parece valer a pena investigar um pouco mais.

Ok, consegui fazer funcionar, acho que estava com um bug em outro lugar do js que estava impedindo. Woohoo, parece muito bem integrado ao meu site. Muito obrigado @simon!

1 curtida