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 个赞

我也在尝试这样做,因为我意识到如果我将 WordPress 帖子嵌入 Discourse,那么我就可以只将用户引导到 Discourse,而不是两个不同的网站。

有人找到解决办法了吗?我为此挣扎了很久。

您是否正在使用类似这样的方法来添加 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();

        // Return an iframe for this post type.
        return $output;
    }

    // Return the default output, or do something else with it here.
    return $output;
}
add_filter( 'discourse_publish_format_html', 'your_namespace_publish_format_html' );

但希望根据帖子的高度来设置 height 属性?

如果是这样,需要精确到什么程度?我想知道您是否可以计算帖子中的字符数,然后加上任何具有固定高度的图片或其他元素的高度,最后再多加一些以防万一。我可能有一些关于如何做到这一点的建议。

也有可能使用 Javascript 来获取帖子在给定宽度下渲染时的确切高度。

1 个赞

我还没有在 discourse_publish_format_html 过滤器中集成它。我只是手动将其添加到了 Discourse 的帖子中,并试图找出如何读取 iframe 中内容的 JavaScript,然后调整 iframe 的大小。

我在网上看到过不少关于如何实现这一点的教程,但不知何故,我无法让这个 JavaScript 在 Discourse 上运行。我尝试了使用 api 中的 page change 和 decorateCookedElement,但仍然无法使其正常工作。

如果可能的话,我更希望在 Discourse 中完成,这样我就可以将 iframe 调整为全高,即使我嵌入的不是来自 WordPress 网站的内容。

这是一个有趣的问题。如果在 Discourse 帖子中的 iframe 元素上手动编辑 height 属性会发生什么?编辑高度后查看帖子时,会使用新高度,还是必须重新烘焙 Discourse 帖子才能使编辑的高度生效?

编辑:这很有趣,我今天晚些时候会测试一下。

1 个赞

如果在 iframe 标签中添加 height="400px",刷新页面后它会调整大小。如果添加 height="100%",似乎没有任何效果。

如果为 iframe 添加 CSS 属性,它似乎也能正确更改高度,至少能更改为我手动输入的值。

ugh,我终于搞定了。问题在于我在 iframe 标签上使用了自定义类,而 Discourse 会剥离所有这些标签。我应该从几天前吸取教训(https://meta.discourse.org/t/formatting-posts-to-look-more-like-wordpress-blog/301133/6)。我读到这是自定义 HTML 类在这里不起作用的安全原因,但我不太确定为什么 :confused:

不过,这是使用 .topic-body iframe 选择器调整 iframe 高度的代码(不确定这段代码是否对其他人有效,对我来说似乎有效):

<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); // 根据需要调整延迟                  
      },
      { id: "component-id", onlyStream: true}
    );
</script>

编辑:实际上,它不起作用。我在 iframe 标签中添加了 height="4000px",我认为这就是它起作用的原因。

1 个赞

这是一个棘手的问题。我认为从 Discourse 内部访问 iframe 的内容并不容易。如果 iframe 的源和你的 Discourse 站点位于同一域的不同子域上,那么 可能 可以使其工作。

如果我理解正确的话,你需要将 document.domain 设置在你要从中获取内容的子域上,以及在 Discourse 上运行的脚本中设置根域。

如果 iframe 的源实际上是你的根域,也许可以尝试调整脚本:

<script type="text/discourse-plugin" version="0.8.18">
   document.domain = "your_root_domain.com"; // 编辑为你的域名
    api.decorateCookedElement(
      element => {

如果 iframe 的域也是你根域的一个子域,你也需要将 document.domain 设置为根域。

要为 iframe(或任何内容)设置样式,你可以将内容包装在一个带有数据属性的 div 中:

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

[data-full-height] > iframe {
      // 可选地在此处设置外部 iframe 的样式:高度、宽度等
     // 不幸的是,height: 100% 将不起作用 - iframe 的包含元素没有设置高度。
}

如果 document.domain 方法不起作用,可能还有其他解决方案 - 使用 window.postMessage 在 iframe 的父文档和 Discourse 之间进行通信。

我认为我最初计算 iframe 在其源站点上高度的想法将不起作用 - 渲染的 iframe 的宽度将根据查看 Discourse 的设备而变化。

1 个赞

嗯,我的论坛是根域的一个子域,所以我添加了你建议的根域,并遇到了这个错误:

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

现在我正在尝试使用 postMessage,但失败了。我认为我需要更好地理解 Discourse 的javascript是如何工作的,我猜它和其他网站一样工作,也许不是,或者也许只是我不太懂javascript,尤其是跨域的东西,哈哈。

感谢你花时间查看这个问题!

我对此已经好奇了很久。这是一个概念验证(请注意,它并没有解决从 iframe 中删除滚动条的问题)。

在您嵌入的帖子中添加一个脚本标签:

<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' // 如果有多个 iframe,请使用唯一标识符
        }, '*'); // 考虑为安全起见指定父域
    }

    // 发送初始高度
    window.onload = sendHeight;

    // 可选:在调整大小或其他事件时更新高度
    window.onresize = sendHeight;
</script>

我在脚本中使用了标识符 "zalgFrame"

在您的 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; // 我的测试域,请更新为您的域或注释掉
  // 获取从 wp-discourse.test 传递过来的 iframe 高度,并确认 iframeId 与我设置的 iframeId 匹配
  if (event.data.iframeHeight && event.data.iframeId === 'zalgFrame') {
      // 打开控制台,访问带有 iframe 的 Discourse 页面
      // 您应该能看到随着您调整窗口大小,父站点正在发送更新的高度
      console.log("we got an event:" + event.data.iframeHeight);
      iframeHeight = event.data.iframeHeight;
      iframeId = event.data.iframeId;
  }
  }, false);
</script>

在 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>

因此,可以从父窗口获取渲染后 iframe 的实际高度。

我不知道如何将事件监听器中的数据高度获取到 api.decorateCookedElement 的调用中。我不确定这是否能解决长 iframe 的垂直滚动条问题。如果我尝试硬编码一个较大的高度(1600px)到 iframe 元素中,仍然会出现滚动条。

编辑:为了完整起见:

<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;
        // 基于假设只有一个 iframe 包裹在 data-zalgFram div 中
        let iframe = e.querySelector("[data-zalgFrame] iframe");
        if (iframe) {
          iframe.style.height = `${iframeHeight}px`;
        }
        // 设置完 iframe 的实际渲染高度后
        // 移除事件监听器
        window.removeEventListener("message", handleMessage, false);
      }
    }
    window.addEventListener("message", handleMessage, false);
  },
  { id: "component-id", onlyStream: true }
);
</script>

对于高度超过 ~1000px 的内容,似乎无法避免 Discourse 添加滚动条,因此我不推荐这种方法。

我认为对 OP 的回答是,这在某种程度上是可能的,但可能没什么用。(除了我了解了 window.postMessage() 方法 :slight_smile:

2 个赞

我赞赏这里的英勇努力,也不想给他们泼冷水,但我必须承认,我对这个话题的前提感到有些怀疑,即:

我对 Jim 有两个(真正)问题:

  1. 你为什么在这里想要一个 iframe 而不是正常的文章嵌入功能?
  2. 如果你不想让用户在那里消费内容,我很好奇你为什么还要保留一个 WordPress 网站?
1 个赞

我不能代表原帖作者发言,但我可以就我的情况给出答案:

我在 WordPress 上自己动手做了一些插件,这些插件可以让我拥有一个播客播放器,带有交互式字幕(单词会随着音频播放而高亮显示,并且可以点击跳转到音频的那个部分)、交互式章节/节目笔记,以及一个可搜索/可排序/可过滤的播放列表。

所以,如果只是在这里嵌入它们而没有 iframe,我将无法访问我为它编写的 JavaScript 和所有样式。

哦,而且我觉得在 WordPress 上动手做这些东西比在 Discourse 上容易得多,我在 Discourse 上做 JavaScript 和插件时真的很吃力。

托管播客节目,我无论如何都需要 WordPress 网站。但至于我是否希望用户在那里消费内容,我不确定。自从我一直在 WordPress 上使用 Discourse 进行评论以来,互动性有所下降。我以前让人们在 WordPress 评论区发帖,但 Discourse 要求他们跨越一个域名阈值,然后他们就会在另一个地方进行互动。如果 Discourse 能让人们更轻松地在 WordPress 论坛上以嵌入方式发帖,我可能会专注于此。

我不确定这是否是必需的,我只是觉得我想要一个人们聚集的主要场所,以前我认为会是 WordPress 加上嵌入式的 Discourse 评论/发帖,但现在我正在考虑 Discourse 加上嵌入式的 WordPress 帖子可能更容易,并且更有可能激发人们之间的互动。

2 个赞

太棒了!

为什么不行?

我明白了!尽管如此,根据你对我之前问题的回答(“为什么不行?”),将它们正确地嵌入到Discourse帖子中将比动态iframe更稳定的方法。

很抱歉听到互动性下降,我也明白你的意思。西蒙关于这个主题的更广泛的帖子让我想到了这一点:

1 个赞

我的意思是,也许我可以?我以前在这里挣扎着让一个音频播放器使用 mediaelement.js,我认为我不太理解插件 API。这似乎需要很多很多的工作,也许我可以在长远来看完成它,但现在,它用 iframe 嵌入看起来相当不错。主要的挑战将是 iframe 中嵌入的文本的可搜索性,但我想在帖子中发布该文本并隐藏它或将其放在手风琴下,这样它仍然会出现在搜索结果中。

另外,我认为更大的问题实际上是,当内容被“煮熟”(或者 whatever the lingo is lol)时,许多 HTML 类会被剥离,所以仅仅尝试在这里发布 WordPress 帖子并使用类似的 CSS 就需要大量的重写,这启发了我写下这个:

1 个赞

好的。让我仔细考虑一下。除了Simon已经提出的建议之外,我对于动态iframe没有更深入的见解,但您的情况让我有所思考。

1 个赞

也许值得注意的是,我正在积极研究这个问题(使用 Discourse 为网站提供评论系统支持)。目前主要关注无头 WordPress 网站,但通用方法可能对常规 WordPress 和非 WordPress 网站有所帮助。

2 个赞

我不记得在哪里看到的了,但我认为有一个隐藏的最大高度,可能是 1000px,也许是在已处理的内容上?

所以也许这会干扰你的解决方案。

我明天会看看 :folded_hands:t2:

1 个赞

它在 iframe 元素上:

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

这可以通过主题通过定位带有 data 属性的 iframe 来修复:

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

需要进行此更改才能显示更长的 iframe,但我看到的滚动条来自 iframe 源页面。我获得良好结果的唯一方法是创建我博客上帖子的嵌入式版本 - 基本上只删除帖子内容并稍微调整样式。例如,使用 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;
        // a bit of a crap shoot, I think `scrollHeight` is the correct target for this case
        const height = Math.max(body.scrollHeight, body.offsetHeight,
            html.clientHeight, html.scrollHeight, html.offsetHeight);

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

    // Send initial height
    window.onload = sendHeight;

    // Optional: Update height on resize or other events
    window.onresize = sendHeight;
</script>
<?php
endwhile; endif;
?>

在生产站点上实现这一点可能需要一些调整,但似乎值得进一步研究。

好的,我认为我已经解决了这个问题,似乎是我在 js 中有其他错误导致它无法工作。太棒了,它似乎与我的网站集成得非常好。非常感谢你,@simon

1 个赞