From b754a6f99de1ef4cae6b0fa3ccd05d4164a18de9 Mon Sep 17 00:00:00 2001 From: qsm-odoo Date: Mon, 9 May 2022 13:41:08 +0000 Subject: [PATCH] [FIX] website: ensure lazy loaded images are loaded on zoomed Chrome Before this commit, some images were never appearing on Chrome, especially when using the 90% zoom. Steps to reproduce (on a 1920x1080 screen and using Chrome): 1. Edit the homepage 2. Add a "Cover" block and setup a 50% min-height on it 3. Add 2 "Columns" blocks below the "Cover" block 4. Set the width of the first "Columns" snippet's first image to the recommended size (690px) 5. Save 6. As a public user, set the browser zoom to 90% and go to the page (you may need to reload the page again so images are in cache) => The first image of the second "Columns" block never appears. opw-2731507 closes odoo/odoo#91075 X-original-commit: e9ef21b6cd5ae58322fa9102f9f34f06835f0485 Signed-off-by: Romain Derie (rde) Signed-off-by: Quentin Smetz (qsm) --- .../src/js/content/snippets.animation.js | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/addons/website/static/src/js/content/snippets.animation.js b/addons/website/static/src/js/content/snippets.animation.js index beb0e205fa4..12a514d1fa0 100644 --- a/addons/website/static/src/js/content/snippets.animation.js +++ b/addons/website/static/src/js/content/snippets.animation.js @@ -13,6 +13,7 @@ const dom = require('web.dom'); var mixins = require('web.mixins'); var publicWidget = require('web.public.widget'); var utils = require('web.utils'); +const wUtils = require('website.utils'); var qweb = core.qweb; @@ -1353,6 +1354,73 @@ registry.WebsiteAnimate = publicWidget.Widget.extend({ }, }); +/** + * The websites, by default, use image lazy loading via the loading="lazy" + * attribute on elements. However, this does not work great on all + * browsers. This widget fixes the behaviors with as less code as possible. + */ +registry.ImagesLazyLoading = publicWidget.Widget.extend({ + selector: '#wrapwrap', + + /** + * @override + */ + start() { + // For each image on the page, force a 1px min-height so that Chrome + // understands the image exists on different zoom sizes of the browser. + // Indeed, without this, on a 90% zoom, some images were never loaded. + // Once the image has been loaded, the 1px min-height is removed. + // Note: another possible solution without JS would be this CSS rule: + // ``` + // [loading="lazy"] { + // min-height: 1px; + // } + // ``` + // This would solve the problem the same way with a CSS rule with a + // very small priority (any class setting a min-height would still have + // priority). However, the min-height would always be forced even once + // the image is loaded, which could mess with some layouts relying on + // the image intrinsic min-height. + const imgEls = this.$target[0].querySelectorAll('img[loading="lazy"]'); + for (const imgEl of imgEls) { + // Write initial min-height on the dataset, so that it can also + // be properly restored on widget destroy. + imgEl.dataset.lazyLoadingInitialMinHeight = imgEl.style.minHeight; + imgEl.style.minHeight = '1px'; + wUtils.onceAllImagesLoaded($(imgEl)).then(() => { + if (this.isDestroyed()) { + return; + } + this._restoreImage(imgEl); + }); + } + return this._super(...arguments); + }, + /** + * @override + */ + destroy() { + this._super(...arguments); + const imgEls = this.$target[0].querySelectorAll('img[data-lazy-loading-initial-min-height]'); + for (const imgEl of imgEls) { + this._restoreImage(imgEl); + } + }, + + //-------------------------------------------------------------------------- + // Private + //-------------------------------------------------------------------------- + + /** + * @private + * @param {HTMLImageElement} imgEl + */ + _restoreImage(imgEl) { + imgEl.style.minHeight = imgEl.dataset.lazyLoadingInitialMinHeight; + delete imgEl.dataset.lazyLoadingInitialMinHeight; + }, +}); + return { Widget: publicWidget.Widget, Animation: Animation,