I'm moving this to web compat for now because this ends up being a site issue and it's my suspicion and hope that the web compat team is more familiar with the nuances of how lazy loading can go wrong between browsers than I am. ## Quick Summary The site appears to explicitly be using lazy loading logic that involves ServiceWorkers serving a 1x1 transparent data URL image initially until the page logic updates the URLS. Disabling ServiceWorkers makes the problem go away because this effectively disables the image lazy loading because requests will always go to the network in that case. The problem seems to be that the image served by the ServiceWorker ends up with the <img> tag having a bounding client rect height of 18.4px instead of the size of the post-load image height, which results in the the image indeed being off screen. The logic appears to be assuming that it would be dealing with an element whose size would be explicitly forced, but there doesn't appear to be styling for this. The image does live in a container that seems to be aspect-ratio sized... presumably that's what they thought they'd be measuring? It appears that this isn't a problem in Chrome because when I refresh the page in Chrome, the page ends up smooth scrolling down to the pre-refresh scrolling position. It doesn't seem to do this for other sites, so maybe sky news is doing the smooth scrolling itself? Or maybe that's a Chrome implemented mitigation for sites that are known to have broken lazy loading? The relevant styling for the image is: ```css .sdc-article-image__item { position: absolute; width: 100%; left: 0; top: 0; } ``` With that being contained in a div with this rule that is what gives us the placeholder on the page with the desired bounding rect height of 499.95: ```css .sdc-article-image__wrapper { position: relative; padding-bottom: 56.25%; } ``` ## Investigation Details Here's the image tag after scrolling down so only the bottom of the image is only partly visible and refreshing: > <img class="sdc-article-image__item" src="https://e3.365dm.com/20/12/768x432/skynews-brexit-billboard-time_5195520.jpg?20201205110204" srcset="https://e3.365dm.com/20/12/384x216/skynews-brexit-billboard-time_5195520.jpg?20201205110204 380w, https://e3.365dm.com/20/12/768x432/skynews-brexit-billboard-time_5195520.jpg?20201205110204 760w, https://e3.365dm.com/20/12/1600x900/skynews-brexit-billboard-time_5195520.jpg?20201205110204 1024w, https://e3.365dm.com/20/12/2048x1152/skynews-brexit-billboard-time_5195520.jpg?20201205110204 2048w" sizes="(min-width: 1024px) 1024px, 100vw" alt="An electronic billboard "> And here's the image tag after scrolling back up so the image is visible: > <img class="sdc-article-image__item" src="https://e3.365dm.com/20/12/768x432/skynews-brexit-billboard-time_5195520.jpg?bypass-service-worker&20201205110204" srcset="https://e3.365dm.com/20/12/384x216/skynews-brexit-billboard-time_5195520.jpg?bypass-service-worker&20201205110204 380w, https://e3.365dm.com/20/12/768x432/skynews-brexit-billboard-time_5195520.jpg?bypass-service-worker&20201205110204 760w, https://e3.365dm.com/20/12/1600x900/skynews-brexit-billboard-time_5195520.jpg?bypass-service-worker&20201205110204 1024w, https://e3.365dm.com/20/12/2048x1152/skynews-brexit-billboard-time_5195520.jpg?bypass-service-worker&20201205110204 2048w" sizes="(min-width: 1024px) 1024px, 100vw" alt="An electronic billboard " data-lazy-loaded="true"> And here are the relevant excerpts from the ServiceWorker at https://news.sky.com/lazy-images-service-worker.js: ```js // if it has the bypass-service-worker parameter do not intercept if (url.match(/bypass-service-worker/)) { interceptRequest = false; } ``` ```js // if interceptRequest is still true return a blank gif instead of going to the network if (interceptRequest) { event.respondWith(fetch('data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==').then(res => res)); } ``` And this is the visibility checking logic from the page after beautification: ```js isInViewPort: function(t) { var e = t.getBoundingClientRect(); return 0 !== e.width && (!(e.bottom < 0) && (!((window.outerHeight || window.innerHeight) - e.top < 50) && (!(e.width + e.left < -50) && !((window.outerWidth || window.innerWidth) - e.left < -50)))) }, ``` It appears that the difference in behavior here is down to t.getBoundingClientRect() The method returns false for the image. The expression ends up (with devtools up) evaluating to, in succession: - Replacement: - 0 !== (888.8) && - (!((-221.9) < 0) && (!(((1447) || (1346)) - (-240.3) < 50) && (!((888.8) + (0) < -50) && !(((2406) || (901)) - (0) < -50)))) - true && (!(true) && (!(1447 - -240.3 < 50)
Bug 1671389 Comment 5 Edit History
Note: The actual edited comment in the bug view page will always show the original commenter’s name and original timestamp.
I'm moving this to web compat for now because this ends up being a site issue and it's my suspicion and hope that the web compat team is more familiar with the nuances of how lazy loading can go wrong between browsers than I am. ## Quick Summary The site appears to explicitly be using lazy loading logic that involves ServiceWorkers serving a 1x1 transparent data URL image initially until the page logic updates the URLS. Disabling ServiceWorkers makes the problem go away because this effectively disables the image lazy loading because requests will always go to the network in that case. The problem seems to be that the image served by the ServiceWorker ends up with the <img> tag having a bounding client rect height of 18.4px instead of the size of the post-load image height, which results in the the image indeed being off screen. The logic appears to be assuming that it would be dealing with an element whose size would be explicitly forced, but there doesn't appear to be styling for this. The image does live in a container that seems to be aspect-ratio sized... presumably that's what they thought they'd be measuring? It appears that this isn't a problem in Chrome because when I refresh the page in Chrome, the page ends up smooth scrolling down to the pre-refresh scrolling position. It doesn't seem to do this for other sites, so maybe sky news is doing the smooth scrolling itself? Or maybe that's a Chrome implemented mitigation for sites that are known to have broken lazy loading? The relevant styling for the image is: ```css .sdc-article-image__item { position: absolute; width: 100%; left: 0; top: 0; } ``` With that being contained in a div with this rule that is what gives us the placeholder on the page with the desired bounding rect height of 499.95: ```css .sdc-article-image__wrapper { position: relative; padding-bottom: 56.25%; } ``` ## Investigation Details Here's the image tag after scrolling down so only the bottom of the image is only partly visible and refreshing: > <img class="sdc-article-image__item" src="https://e3.365dm.com/20/12/768x432/skynews-brexit-billboard-time_5195520.jpg?20201205110204" srcset="https://e3.365dm.com/20/12/384x216/skynews-brexit-billboard-time_5195520.jpg?20201205110204 380w, https://e3.365dm.com/20/12/768x432/skynews-brexit-billboard-time_5195520.jpg?20201205110204 760w, https://e3.365dm.com/20/12/1600x900/skynews-brexit-billboard-time_5195520.jpg?20201205110204 1024w, https://e3.365dm.com/20/12/2048x1152/skynews-brexit-billboard-time_5195520.jpg?20201205110204 2048w" sizes="(min-width: 1024px) 1024px, 100vw" alt="An electronic billboard "> And here's the image tag after scrolling back up so the image is visible: > <img class="sdc-article-image__item" src="https://e3.365dm.com/20/12/768x432/skynews-brexit-billboard-time_5195520.jpg?bypass-service-worker&20201205110204" srcset="https://e3.365dm.com/20/12/384x216/skynews-brexit-billboard-time_5195520.jpg?bypass-service-worker&20201205110204 380w, https://e3.365dm.com/20/12/768x432/skynews-brexit-billboard-time_5195520.jpg?bypass-service-worker&20201205110204 760w, https://e3.365dm.com/20/12/1600x900/skynews-brexit-billboard-time_5195520.jpg?bypass-service-worker&20201205110204 1024w, https://e3.365dm.com/20/12/2048x1152/skynews-brexit-billboard-time_5195520.jpg?bypass-service-worker&20201205110204 2048w" sizes="(min-width: 1024px) 1024px, 100vw" alt="An electronic billboard " data-lazy-loaded="true"> And here are the relevant excerpts from the ServiceWorker at https://news.sky.com/lazy-images-service-worker.js: ```js // if it has the bypass-service-worker parameter do not intercept if (url.match(/bypass-service-worker/)) { interceptRequest = false; } ``` ```js // if interceptRequest is still true return a blank gif instead of going to the network if (interceptRequest) { event.respondWith(fetch('data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==').then(res => res)); } ``` And this is the visibility checking logic from the page after beautification: ```js isInViewPort: function(t) { var e = t.getBoundingClientRect(); return 0 !== e.width && (!(e.bottom < 0) && (!((window.outerHeight || window.innerHeight) - e.top < 50) && (!(e.width + e.left < -50) && !((window.outerWidth || window.innerWidth) - e.left < -50)))) }, ``` As noted above, the visibility check is correct determining things aren't visible because the e.bottom is appropriately negative.
I'm moving this to web compat for now because this ends up being a site issue and it's my suspicion and hope that the web compat team is more familiar with the nuances of how lazy loading can go wrong between browsers than I am. ## Quick Summary The site appears to explicitly be using lazy loading logic that involves ServiceWorkers serving a 1x1 transparent data URL image initially until the page logic updates the URLS. Disabling ServiceWorkers makes the problem go away because this effectively disables the image lazy loading because requests will always go to the network in that case. The problem seems to be that the image served by the ServiceWorker ends up with the <img> tag having a bounding client rect height of 18.4px instead of the size of the post-load image height, which results in the the image indeed being off screen. The logic appears to be assuming that it would be dealing with an element whose size would be explicitly forced, but there doesn't appear to be styling for this. The image does live in a container that seems to be aspect-ratio sized... presumably that's what they thought they'd be measuring? It appears that this isn't a problem in Chrome because when I refresh the page in Chrome, the page ends up smooth scrolling down to the pre-refresh scrolling position. It doesn't seem to do this for other sites, so maybe sky news is doing the smooth scrolling itself? Or maybe that's a Chrome implemented mitigation for sites that are known to have broken lazy loading? The relevant styling for the image is: ```css .sdc-article-image__item { position: absolute; width: 100%; left: 0; top: 0; } ``` With that being contained in a div with this rule that is what gives us the placeholder on the page with the desired bounding rect height of 499.95: ```css .sdc-article-image__wrapper { position: relative; padding-bottom: 56.25%; } ``` ## Investigation Details Here's the image tag after scrolling down so only the bottom of the image is only partly visible and refreshing: > <img class="sdc-article-image__item" src="https://e3.365dm.com/20/12/768x432/skynews-brexit-billboard-time_5195520.jpg?20201205110204" srcset="https://e3.365dm.com/20/12/384x216/skynews-brexit-billboard-time_5195520.jpg?20201205110204 380w, https://e3.365dm.com/20/12/768x432/skynews-brexit-billboard-time_5195520.jpg?20201205110204 760w, https://e3.365dm.com/20/12/1600x900/skynews-brexit-billboard-time_5195520.jpg?20201205110204 1024w, https://e3.365dm.com/20/12/2048x1152/skynews-brexit-billboard-time_5195520.jpg?20201205110204 2048w" sizes="(min-width: 1024px) 1024px, 100vw" alt="An electronic billboard "> And here's the image tag after scrolling back up so the image is visible: > <img class="sdc-article-image__item" src="https://e3.365dm.com/20/12/768x432/skynews-brexit-billboard-time_5195520.jpg?bypass-service-worker&20201205110204" srcset="https://e3.365dm.com/20/12/384x216/skynews-brexit-billboard-time_5195520.jpg?bypass-service-worker&20201205110204 380w, https://e3.365dm.com/20/12/768x432/skynews-brexit-billboard-time_5195520.jpg?bypass-service-worker&20201205110204 760w, https://e3.365dm.com/20/12/1600x900/skynews-brexit-billboard-time_5195520.jpg?bypass-service-worker&20201205110204 1024w, https://e3.365dm.com/20/12/2048x1152/skynews-brexit-billboard-time_5195520.jpg?bypass-service-worker&20201205110204 2048w" sizes="(min-width: 1024px) 1024px, 100vw" alt="An electronic billboard " data-lazy-loaded="true"> And here are the relevant excerpts from the ServiceWorker at https://news.sky.com/lazy-images-service-worker.js: ```js // if it has the bypass-service-worker parameter do not intercept if (url.match(/bypass-service-worker/)) { interceptRequest = false; } ``` ```js // if interceptRequest is still true return a blank gif instead of going to the network if (interceptRequest) { event.respondWith(fetch('data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==').then(res => res)); } ``` And this is the visibility checking logic from the page after beautification: ```js isInViewPort: function(t) { var e = t.getBoundingClientRect(); return 0 !== e.width && (!(e.bottom < 0) && (!((window.outerHeight || window.innerHeight) - e.top < 50) && (!(e.width + e.left < -50) && !((window.outerWidth || window.innerWidth) - e.left < -50)))) }, ``` As noted above, the visibility check is correctly determining things aren't visible because the e.bottom is appropriately negative.