Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add fetchpriority=low to occluded initial-viewport images #1482

Draft
wants to merge 5 commits into
base: add/embed-optimizer-min-height-reservation
Choose a base branch
from

Conversation

westonruter
Copy link
Member

@westonruter westonruter commented Aug 19, 2024

Important

This is a follow-up PR (sub-PR) to #1373 which is branched off of add/embed-optimizer-min-height-reservation. Do not merge this PR here before that other PR has been merged into trunk, at which time this PR's target branch automatically update to trunk as well.

Fixes #1309.

When an image is in the initial viewport (i.e. its boundingClientRect.top is less than window.innerHeight) and yet its intersectionRatio is 0.0, at present such images are getting loading=lazy incorrectly. Such images are likely part of subsequent carousel slides and getting loading=lazy could result in them not being loaded by the time the carousel slide is displayed. Acccording to the Web.dev article on fetch priority, such images should actually get fetchpriority=low. This is what is implemented by this PR. In particular, see #1309 (comment) where this PR implements these points:

  • If an element has an intersectionRatio of zero and yet its boundingClientRect.top is less than the viewport height, then it should also get fetchpriority=low.
  • Alternatively, if an element has an intersectionRatio of zero and its boundingClientRect.top is greater than the viewport height (or if all of the values in boundingClientRect are zero, indicating a display:none element), it should get loading=lazy.

It does not implement the following points, which will come in a subsequent PR:

  • If an element has a non-zero intersectionRatio and the element has a computed style of visibility:hidden or opacity:0, then it should get fetchpriority=low.
  • If an element has an intersectionRatio of zero and its boundingClientRect indicates that it is in the negative area of the viewport to the top, left or right, then it should get fetchpriority=low since it may be part of an off-screen submenu that gets displayed.

Given the Ultimate Blocks plugin's Carousel block with markup as follows, with simply 3 images added as carousel slides:

Block Markup
<!-- wp:ub/image-slider {
  "blockID": "1427237e-b7fb-48da-9ca5-8e8021466dc0",
  "pics": [
    {
      "sizes": {
        "thumbnail": {
          "height": 150,
          "width": 150,
          "url": "http://localhost:8888/wp-content/uploads/2024/06/bison1-150x150.jpg",
          "orientation": "landscape"
        },
        "medium": {
          "height": 196,
          "width": 300,
          "url": "http://localhost:8888/wp-content/uploads/2024/06/bison1-300x196.jpg",
          "orientation": "landscape"
        },
        "large": {
          "height": 668,
          "width": 1024,
          "url": "http://localhost:8888/wp-content/uploads/2024/06/bison1-1024x668.jpg",
          "orientation": "landscape"
        },
        "full": {
          "url": "http://localhost:8888/wp-content/uploads/2024/06/bison1-scaled.jpg",
          "height": 1670,
          "width": 2560,
          "orientation": "landscape"
        }
      },
      "mime": "image/jpeg",
      "type": "image",
      "subtype": "jpeg",
      "id": 13,
      "url": "http://localhost:8888/wp-content/uploads/2024/06/bison1-scaled.jpg",
      "alt": "",
      "link": "http://localhost:8888/2024/06/10/bison/bison1/",
      "caption": ""
    },
    {
      "sizes": {
        "thumbnail": {
          "height": 150,
          "width": 150,
          "url": "http://localhost:8888/wp-content/uploads/2024/06/bison2-150x150.jpg",
          "orientation": "landscape"
        },
        "medium": {
          "height": 197,
          "width": 300,
          "url": "http://localhost:8888/wp-content/uploads/2024/06/bison2-300x197.jpg",
          "orientation": "landscape"
        },
        "large": {
          "height": 673,
          "width": 1024,
          "url": "http://localhost:8888/wp-content/uploads/2024/06/bison2-1024x673.jpg",
          "orientation": "landscape"
        },
        "full": {
          "url": "http://localhost:8888/wp-content/uploads/2024/06/bison2-scaled.jpg",
          "height": 1684,
          "width": 2560,
          "orientation": "landscape"
        }
      },
      "mime": "image/jpeg",
      "type": "image",
      "subtype": "jpeg",
      "id": 12,
      "url": "http://localhost:8888/wp-content/uploads/2024/06/bison2-scaled.jpg",
      "alt": "",
      "link": "http://localhost:8888/2024/06/10/bison/bison2/",
      "caption": ""
    },
    {
      "sizes": {
        "thumbnail": {
          "height": 150,
          "width": 150,
          "url": "http://localhost:8888/wp-content/uploads/2024/06/bison3-150x150.jpg",
          "orientation": "landscape"
        },
        "medium": {
          "height": 200,
          "width": 300,
          "url": "http://localhost:8888/wp-content/uploads/2024/06/bison3-300x200.jpg",
          "orientation": "landscape"
        },
        "large": {
          "height": 683,
          "width": 1024,
          "url": "http://localhost:8888/wp-content/uploads/2024/06/bison3-1024x683.jpg",
          "orientation": "landscape"
        },
        "full": {
          "url": "http://localhost:8888/wp-content/uploads/2024/06/bison3-scaled.jpg",
          "height": 1708,
          "width": 2560,
          "orientation": "landscape"
        }
      },
      "mime": "image/jpeg",
      "type": "image",
      "subtype": "jpeg",
      "id": 11,
      "url": "http://localhost:8888/wp-content/uploads/2024/06/bison3-scaled.jpg",
      "alt": "",
      "link": "http://localhost:8888/2024/06/10/bison/bison3/",
      "caption": ""
    }
  ],
  "descriptions": [
    {
      "id": 13,
      "text": "",
      "link": ""
    },
    {
      "id": 12,
      "text": "",
      "link": ""
    },
    {
      "id": 11,
      "text": "",
      "link": ""
    }
  ],
  "sliderHeight": 432,
  "paginationType": "bullets"
} /-->

With this Carousel being the only block in a post where the initial slide's image is the LCP element:

image

The Prettier-formatted rendered markup is as follows without the changes in this PR:

<div
  class="ub_image_slider swiper-container wp-block-ub-image-slider"
  id="ub_image_slider_2fbf05fe-0c97-41d7-84d1-891c40278575"
  data-swiper-data='{"speed":300,"spaceBetween":20,"slidesPerView":1,"loop":true,"pagination":{"el": ".swiper-pagination" , "type": "bullets", "clickable":true}
            ,"navigation": {"nextEl": ".swiper-button-next", "prevEl": ".swiper-button-prev"}, "keyboard": { "enabled": true },
            "effect": "slide","simulateTouch":false}'
>
  <div class="swiper-wrapper">
    <figure class="swiper-slide">
      <img
        decoding="async"
        src="http://localhost:8888/wp-content/uploads/2024/06/bison1-scaled.jpg"
        alt=""
      />
      <figcaption class="ub_image_slider_image_caption"></figcaption>
    </figure>
    <figure class="swiper-slide">
      <img
        decoding="async"
        src="http://localhost:8888/wp-content/uploads/2024/06/bison2-scaled.jpg"
        alt=""
      />
      <figcaption class="ub_image_slider_image_caption"></figcaption>
    </figure>
    <figure class="swiper-slide">
      <img
        decoding="async"
        src="http://localhost:8888/wp-content/uploads/2024/06/bison3-scaled.jpg"
        alt=""
      />
      <figcaption class="ub_image_slider_image_caption"></figcaption>
    </figure>
  </div>
  <div class="swiper-pagination"></div>
  <div class="swiper-button-prev"></div>
  <div class="swiper-button-next"></div>
</div>

Notice in particular that Ultimate Blocks does not add width and height attributes to the images in the carousel, so WordPress core's server-side heuristics does not add fetchpriority=high to the initial image. Additionally, note the priorities of these images in the DevTools network log:

All three images have an initial priority of medium and the third carousel image is then elevated by Chrome to a high priority even though it is not even displayed (and it is not the LCP element).

image

Compare this with when the changes in this PR are applied and URL Metrics have been gathered. In the network log, now the LCP image element now has an initial and final priority of high and the subsequent slides both have initial/final priorities of low.

image

The Prettier-formatted rendered markup is as follows:

<div
  class="ub_image_slider swiper-container wp-block-ub-image-slider"
  id="ub_image_slider_2fbf05fe-0c97-41d7-84d1-891c40278575"
  data-swiper-data='{"speed":300,"spaceBetween":20,"slidesPerView":1,"loop":true,"pagination":{"el": ".swiper-pagination" , "type": "bullets", "clickable":true}
            ,"navigation": {"nextEl": ".swiper-button-next", "prevEl": ".swiper-button-prev"}, "keyboard": { "enabled": true },
            "effect": "slide","simulateTouch":false}'
>
  <div class="swiper-wrapper">
    <figure class="swiper-slide">
      <img
        data-od-added-fetchpriority
        fetchpriority="high"
        decoding="async"
        src="http://localhost:8888/wp-content/uploads/2024/06/bison1-scaled.jpg"
        alt=""
      />
      <figcaption class="ub_image_slider_image_caption"></figcaption>
    </figure>
    <figure class="swiper-slide">
      <img
        data-od-added-fetchpriority
        fetchpriority="low"
        decoding="async"
        src="http://localhost:8888/wp-content/uploads/2024/06/bison2-scaled.jpg"
        alt=""
      />
      <figcaption class="ub_image_slider_image_caption"></figcaption>
    </figure>
    <figure class="swiper-slide">
      <img
        data-od-added-fetchpriority
        fetchpriority="low"
        decoding="async"
        src="http://localhost:8888/wp-content/uploads/2024/06/bison3-scaled.jpg"
        alt=""
      />
      <figcaption class="ub_image_slider_image_caption"></figcaption>
    </figure>
  </div>
  <div class="swiper-pagination"></div>
  <div class="swiper-button-prev"></div>
  <div class="swiper-button-next"></div>
</div>

Note the inclusion of fetchpriority=high on the initial carousel slide img and fetchpriority=low on the subsequent ones.

@westonruter westonruter added [Type] Enhancement A suggestion for improvement of an existing feature [Plugin] Optimization Detective Issues for the Optimization Detective plugin [Plugin] Image Prioritizer Issues for the Image Prioritizer plugin (dependent on Optimization Detective) labels Aug 19, 2024
@westonruter westonruter added this to the image-prioritizer n.e.x.t milestone Aug 19, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Plugin] Image Prioritizer Issues for the Image Prioritizer plugin (dependent on Optimization Detective) [Plugin] Optimization Detective Issues for the Optimization Detective plugin [Type] Enhancement A suggestion for improvement of an existing feature
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant