What is it with H5P and this “resizing”?

If you have ever had trouble with H5P and let someone explain to you what went wrong, the term “resizing” may have been used. This is a term that may pop up related to different issues with H5P, so why not try to explain this once and for all – fingers crossed 🙂

Why resizing does matter

H5P content, like interactive quizzes, can change in size when you interact with it. For example, clicking “Check” on a multiple-choice question makes the content expand to show feedback at the bottom. Typically, web browsers handle this resizing automatically by giving the content more space.

However, most H5P content lives within an “iframe,” which is like a separate window within your webpage. Think of your webpage as an envelope, the iframe as a window, and H5P content as a letter inside the envelope. That essentially is where the analogy ends, but I like it a lot to explain iframes.

The key point is that the webpage and the H5P content inside the iframe exist in different contexts. They don’t know about each other, and without further doing they do not communicate with each other.

The challenge of resizing

When you click on the “Check” button, the content’s height changes, but the iframe remains unaware of this. It’s part of the parent page and doesn’t recognize the content’s size change. So, we need a way to resize the iframe.

H5P content can’t directly resize the iframe because it’s limited to its context. Instead, it sends a message to H5P core, saying, “I’ve changed my size; please let others know.” H5P core achieves this using a method called “postMessage” to send messages between different browser contexts.

Side note for developers: Yes, you can work around that context issue if the page and the content are served from the same domain, but that will not always be the case, and H5P has a general solution for that in stock.

A piece of JavaScript code, known as the “resizer script” and also part of H5P core, operates in the parent page’s context. It listens for these “resize” messages that are also known as events. When it receives one, it adjusts the iframe’s size to match the content. This works because the resizer script and the iframe share the same context.

That’s just part of the story though. Basically the same happens when you change the size of the browser window. In that case, your browser will broadcast a message saying: “Hey, everybody, I was changed in size!” The resizer script can “hear” this. It will resize the iframe as needed and post a message to the part of H5P core inside the iframe. That part of H5P core will forward the message to the H5P content. In some cases, that’s enough and the browser will do the work, but in other cases the H5P content type will need to recompute particular things. That really differs from content type to content type.

Side note: Despite the extra effort that iframes require – as we will see shorty – they were chosen as a protective measure to prevent the page’s stylesheet to spill over into the content’s stylesheet or the other way around, and also to prevent data leaks when embedding content from another domain.

Side note for developers: In theory, the H5P content type could listen to the browser window’s resize events directly despite being in a different context, but it should not do this for two reasons:

  1. It’s an extra custom listener that is not required, and to keep the code simple, just use the existing interface that the H5P.EventDispatcher provides.
  2. H5P core or a parent content type may dispatch resize events to its children not only when the browser window resizes, but also for other reasons. You would miss those if you relied on putting resize event listeners on the window object.

Putting that knowledge to use

There’s more to “resizing”, but we can already explain some phenomena with the knowledge that we have gained so far. For instance:

I have included some H5P content in my page, but the bottom is cut off!

So, what could be the problem? It might have something to do with the content type. Here’s a hint: The content might have changed in height, but it didn’t notify H5P core to adjust its size accordingly. In simpler terms, it forgot to tell the parent page that it needed more room in the iframe. As a result, the bottom part of the content could get cut off. This idea matches what we’ve seen.

But don’t worry, we have a way to test this theory. We can resize the browser window, and if everything looks good after that, it’s likely a problem with how the content handles resizing.

The same issue might occur if there’s extra space at the bottom of the content that disappears when we resize the browser window.

I have embedded some H5P content in my page, but the bottom is cut off!

Can you spot the difference to the previous phenomenon? The result is the same, but the scenario is a little different. This one means that you have used some HTML embed code to put H5P content into your page. Like so:

This is the demo content for an H5P multiple choice question from H5P.org. It is running on the h5p.org server, but we’re watching it through the window in the envelope which is this page.

Everything seems to be working smoothly here. However, some individuals may encounter an issue where the iframe never adjusts its height, causing parts of the content to be cut off at the bottom.

Let’s break it down: H5P content functions correctly on h5p.org, and your webpage is running fine too. You can see the content on your page. So, what’s missing? That’s right, it’s the “resizer script.” We need that to ensure proper resizing.

If we examine the embed code that you can copy by clicking the “Embed” button beneath the H5P content, it looks like this:

<iframe
  src="https://h5p.org/h5p/embed/712" width="1090" height="580"
  frameborder="0" allowfullscreen="allowfullscreen" allow="geolocation *;
  microphone *; camera *; midi *; encrypted-media *"
  title="Multiple Choice">
</iframe>

<script
  src="https://h5p.org/sites/all/modules/h5p/library/js/h5p-resizer.js"
  charset="UTF-8">
</script>

I added some line breaks for clarity, but that’s not relavant for the functionality.

The important fact is: When it comes to adding H5P content to your HTML page, you’ll notice two essential things: an iframe tag and a script` tag.

The iframe tag creates a window for the H5P content and loads it from a specified URL. It also handles some additional tasks relevant to other aspects.

The script tag instructs your page to incorporate a piece of JavaScript code from a specified URL. Notice the script file is named “h5p-resizer.js”? This is a crucial part of H5P core that manages communication between your page and the H5P content. Without it, when the browser window resizes, the H5P content won’t know. Similarly, when the H5P content resizes, H5P core inside the iframe sends out a message, but there’s no one listening, so the iframe won’t resize.

But wait, if we include that script tag, everything should work fine, right? But what if you’ve added it and it’s still not working? In that case, please revisit your page’s HTML code. There’s a possibility that the iframe tag is there, but the script tag is missing. Why does this happen?

Many platforms, for security reasons, filter out script tags completely. They want to prevent any potential injection of harmful code into the page. Unfortunately, this means you can’t embed H5P content in such cases with resizing in place.

If you’re lucky, some platforms might allow script tags that reference JavaScript files hosted on the same server, but only if an admin has reviewed and approved those files. You don’t necessarily have to use the suggested h5p-resizer.js script from https://h5p.org/sites/all/modules/h5p/library/js/h5p-resizer.js. An admin could download it, make sure it’s safe, and then host it on the same server as your platform. For instance, I could upload the file to https://snordian.de/whateverNameILike.js and use it to avoid running code from third-party servers on my page.

Alternatively, the admin could load the script on every page, whether it contains H5P content or not. Another option is to create a small script that loads the resizer script only when there’s an iframe with H5P content on the page.

Phew, that was a lot already!

Absolutely, we’ve covered quite a bit, but there’s more to explore. Resizing H5P content remains significant in various scenarios. Take H5P.Column, for instance. It doesn’t have any specific elements that need to signal resizing to H5P core, and it can be resized by the browser seamlessly. However, H5P.Column does include subcontent types, and they might require the entire resizing process.

This brings us to…

This content behaves strange: It does not scale properly, looks distorted, is misplaced, …

When you’re dealing with a wrapping content type like Column, it plays a crucial role in managing resize events for its subcontents. It needs to pass on any resize events it receives from H5P core to its subcontents. This allows the subcontents to perform their resizing computations and actions if necessary.

Conversely, the wrapping content type must also be attentive to resize events coming from its subcontents. It should relay these events to H5P core. However, it must be careful not to create an infinite loop in the process.

If you notice strange behavior in subcontents related to sizing, it could be due to a subcontent issue when it’s used within a wrapping content type rather than as a standalone content. Alternatively, the wrapping content type may not have an effective mechanism for handling the flow of resize events up and down the hierarchy.

The former issue might explain why you can’t find certain H5P content types as subcontents in others. They might work fine on their own but encounter challenges when used as subcontents, requiring more considerations when calculating sizes. However, it’s important to note that flawed sizing behavior is just one potential reason why content might not function as expected within a wrapping content type, which could be explored in a separate article, maybe.

Still not everything!?

No, the world of resizing is even bigger. There’s one quite common issue that even has its own page within the H5P documentation on h5p.org. That page may not be self-explanatory, however. But it should not be too difficult to explain by now.

I have included H5P content in that other accordion plugin, or tabs plugin, or lightbox plugin, …

That headline could continue with or “some other thingy that puts H5P content in some other container with varying size”, but lets stick with the accordion. The principle is the same for all of the types.

Many platforms offer various plugins that allow you to place content inside an accordion – not referring to the H5P.Accordion content type. Typically, accordions only reveal one panel at a time, expanding them when you click on a panel header. But why might this cause resizing issues when H5P content is placed inside these panels? Let’s break it down.

Imagine you have an accordion with three panels, and you’ve added H5P content to each panel. When you load the page, the first panel is usually expanded, so it has a visible height. However, the other two panels are collapsed (or “hidden”) and have a height of 0 pixels.

Now, H5P core comes into play. It tries to set the height for each content’s iframe based on the container it’s in. If that container has a height greater than 0 pixels, everything works as expected. H5P sets the iframe’s size, and depending on the accordion panel, the height may be limited or adjusted to fit the H5P content. In any case, you can see the content.

However, if the container has a height of 0 pixels, either explicitly or implicitly, H5P sets the iframe height to 0 pixels as well. At this point, it doesn’t matter because panels 2 and 3 are still collapsed. But when you click on panel 2 or 3, nothing happens; you can’t see the H5P content.

What’s missing here? It’s our old friend, the resizer script. This script only listens for resize events from the browser, and it doesn’t know anything about the accordion panel it’s placed in. So, it doesn’t realize that expanding a panel provides more space. As a result, it doesn’t resize the iframe or send a resize event to adjust the H5P content.

How can we test this theory? Well, we can resize the browser window after expanding an accordion panel. If the H5P content becomes visible, it means a resize event sent by the browser has resolved the issue.

There are a couple of potential ways out of this misery, but all come with some price to pay.

  • Accordion plugin maintainer adding JavaScript
    The maintainer of the accordion plugin could include a simple line of JavaScript code that triggers a resize event whenever a panel is collapsed or expanded. Something like window.dispatchEvent(new Event('resize')); This event would inform H5P core to adjust the iframe size. However, there’s a question of why the maintainer should support another plugin like H5P when the accordion itself doesn’t need this feature.
  • H5P Group adding custom code
    H5P Group could add custom code to detect other plugin containers where the H5P iframe might be placed. This code would enable H5P to resize as needed. But again, the question arises: Why should H5P Group add code for various arbitrary plugins?
  • H5P Group using a general ResizeObserver
    H5P Group could employ a so called ResizeObserver within the iframe’s resizer script. This observer would check if the parent container (e.g. the accordion panel) has resized and prompt H5P core to resize the iframe accordingly. However, it’s difficult to determine automatically whether the ResizeObserver is always necessary, and adding it unconditionally might lead to inefficiencies.
  • Creating a custom workaround with “fake” resize events
    A custom workaround could involve sending “fake” browser resize events at regular intervals to ensure H5P content appears. While this would work, it might be resource-intensive and lead to performance issues, especially on less powerful devices.
    Handling a resize event may be costly and should not be triggered lightly. It may not matter much if you have a single standalone content type like a Multiple Choice quiz that doesn’t do anything with a resize event. But you could also have content with multiple nested subcontents that all perform resize computations and actions of some sort. That will bind computational resources. Now imagine doing this on a lower powered device like a smartphone. Now imagine having the resize interval down to 100 ms. That leads to 10 resize events per second, potentially to avoid a visible delay … This would make the user interface unresponsive and gooey at some point, lead to flickering or other things that you do not want …
  • Creating a custom workaround using a ResizeObserver
    A custom solution could implement a ResizeObserver on H5P iframes, triggering resizing only when necessary. You can define the “when needed” part much better than H5P Group could, because you know what plugin you are using. This method allows for better control over when resizing occurs, tailored to the specific plugin in use.
  • Creating a custom workaround for panel interactions
    Another custom workaround could involve monitoring panel clicks that lead to size changes and then triggering the H5P iframe to resize. This approach would require customization for the specific accordion plugin being used.

If you’re running H5P on WordPress and run into the aforementioned trouble, there is a plugin named SNORDIAN’s H5P Resize Pulse that lets you chose from one of the three possible workarounds without the need to write any code yourself. You only need to install and configure the plugin.

Even more?

Yes, I could at least think of some more issues, but those should not appear when using the regular H5P integrations such as the H5P plugins for moodle, WordPress or Drupal or on H5P.com. Developers of H5P integrations should be aware of those potential pitfalls however.

The height of the iframe changes for a while

Sometimes, there can be race conditions when H5P core and a custom H5P integration both attempt to resize the H5P iframe. This can lead to inconsistent resizing behavior, with the iframe height changing back and forth momentarily, sometimes by 1 pixel only. If you encounter this, it’s essential to check the H5P integration. It shouldn’t be necessary to manually adjust the iframe height or try to outsmart the resizing process that H5P provides.

Nothing resizes (with an extra iframe)

I have also seen custom H5P integrations that tried to use an additional iframe within their page and then place the H5P iframe within that intermediary iframe. This setup requires the intermediary iframe to have its own resizer script that forwards resize events between the page and the H5P iframe. Without this intermediary script, resizing won’t function correctly.

Final words

I hope I did not miss some other H5P resize issue that you might encounter … If I have, please let me know, and I am going to write an amendment.