@iamuchejude let me give you more context here and some ideas.
First, please note that Frontity needs Proxy
to run. If the browser doesn’t support Proxy
(old browsers like IE11) we simply don’t hydrate React: https://github.com/frontity/frontity/blob/dev/packages/core/src/client/index.tsx#L8-L60
The site still works because the HTML is there. If users click on a link, they get a new HTML from the server.
For lazy loaded components (images and iframes), we need to support five scenarios:
- JavaScript is disabled.
-
Proxy
is not supported.
-
Proxy
is supported, but neither the Intersection Observer
or native lazy loading are.
-
Proxy
and Intersection Observer
are supported, but native lazy load is not.
-
Proxy
, Intersection Observer
and native lazy load are supported.
|
javascript |
Proxy |
Intersection Observer |
Native lazy load |
1 |
No |
- |
- |
- |
2 |
Yes |
No |
- |
- |
3 |
Yes |
Yes |
No |
- |
4 |
Yes |
Yes |
Yes |
No |
5 |
Yes |
Yes |
Yes |
Yes |
Scenario 1: No JavaScript
The HTML generated in the server (when state.frontity.rendering === "ssr"
) needs to be valid for the first scenario, because JavaScript won’t be able to change it once the browser loads.
The problem is that we don’t want it to load until we are able to control it with JavaScript. So the only way to do so that I know of so far is to use the noscript tag:
<noscript>
<img src="..." ... />
</noscript>
<noscript>
<iframe src="..." ... />
</noscript>
Scenario 2: No Proxy (no Frontity)
The JavaScript required to make it work in scenario 2 must be outside of Frontity, because if Proxy
is not present, we won’t load Frontity.
That can be achieved adding a <script>
tag to the HTML during SRR that only runs if Proxy
is not supported. Let’s call it the no-Proxy script:
<script>
if (!("Proxy" in window)) {
// Do required modifications...
}
</script>
We have two valid tactics here:
<!-- From this -->
<noscript>
<img src="..." />
</noscript>
<!-- to this -->
<img src="..." />
<!-- or from this -->
<img data-src="..." />
<!-- to this -->
<img src="..." />
I like the idea of reusing the noscript tag. It looks more clean, we only have one tag, and maybe we can reuse the no-Proxy script for images, iframes and even scripts if we add the same class to all of them.
<noscript class="no-proxy-load">
<img src="..." />
</noscript>
<noscript class="no-proxy-load">
<iframe src="..." />
</noscript>
<noscript class="no-proxy-load">
<script src="..." />
</noscript>
<noscript class="no-proxy-load">
<script>
// code...
</script>
</noscript>
<script>
if (!("Proxy" in window)) {
document.querySelector(".no-proxy-load");
// remove noscript tags...
}
</script>
Scenario 3: Proxy, but no IO
At this point we can use React instead of plain JavaScript because Frontity will load.
If there is no Intersection Observer, we simply load all the images right away. That means that once we are in the browser (state.frontity.rendering === "csr"
) we either remove the noscript tag or change data-src
and data-srcset
to src
and srcset
(depending on the approach we use).
At this point React also takes control of future pageviews. That means we are going to be generating new images for those pages. In this scenario we simply need to generate the normal images/iframes, no noscript tag or data-src
is needed.
Scenario 4: IO, but no native lazy load
This scenario is similar to the previous one, but here we don’t have to change the images right away, we have to wait until the user scrolls down. We can use the react-intersection-observer hook for that.
In this scenario we need maintain the noscript tag or the data-src
until inView
is true
even for new images generated in client side rendering.
Scenario 5: Native lazy load
Finally, if native lazy load is available, we simply let the browser decide for itself. This means that, if I’m not mistaken, this scenario is the same than the scenario 3 as long as we add the loading
attribute to the images/iframes.
<!-- From this -->
<noscript>
<img src="..." loading="lazy" />
</noscript>
<!-- to this -->
<img src="..." loading="lazy" />
The loading
attribute is ignored by older browsers, so I don’t see a reason why not to add it.
It’s also the same case than scenario 3 for new images/iframes. We add normal ones and let the browser decide when to load them.
Images with no height
There is a problem with images without a height
attribute and native lazy load: as far as I know, they do not lazy load.
To solve that in the current implementation of the Image component we decided to use the intersection observer instead of the native lazy load if height is missing.
That means the scenarios for images would be like this:
|
javascript |
Proxy |
Intersection Observer |
Native lazy load AND height |
1 |
No |
- |
- |
- |
2 |
Yes |
No |
- |
- |
3 |
Yes |
Yes |
No |
- |
4 |
Yes |
Yes |
Yes |
No |
5 |
Yes |
Yes |
Yes |
Yes |
I don’t think this applies to iframes because they have a default height of 150px if the height attribute is missing.
@iamuchejude please add in this topic your progress, questions and findings as you work though this