FIX: correctly account for onebox height when lazy loading images

Page used to jitter when oneboxes loaded images lazily.

Previously we inserted the the "shadow" loading image before the "real" image.

This meant that certain styling with `firstChild` CSS selectors would apply
incorrectly to the shadow image.

Additionally we had special case code for onebox and quoted images that was
not really needed due to this fix.

We had an old fix that used computed style for image height and width in
specific scenarios, we now run it all the time.

On slow devices there was a possibility that the cache fetch after amending
src at the end of the process would cause a flash, this is avoided using a
new onload handler.
This commit is contained in:
Sam Saffron 2020-01-03 15:15:42 +11:00
parent 2050238d0c
commit 0d757814e5

View File

@ -40,7 +40,18 @@ function show(image) {
image.srcset = copyImg.srcset; image.srcset = copyImg.srcset;
} }
image.classList.remove("d-lazyload-hidden"); image.classList.remove("d-lazyload-hidden");
if (image.onload) {
// don't bother fighting with existing handler
// this can mean a slight flash on mobile
image.parentNode.removeChild(copyImg); image.parentNode.removeChild(copyImg);
} else {
image.onload = () => {
image.parentNode.removeChild(copyImg);
image.onload = null;
};
}
copyImg.onload = null; copyImg.onload = null;
}; };
@ -50,43 +61,23 @@ function show(image) {
copyImg.srcset = imageData.srcset; copyImg.srcset = imageData.srcset;
} }
// width of image may not match, use computed style which
// is the actual size of the image
const computedStyle = window.getComputedStyle(image);
const actualWidth = parseInt(computedStyle.width, 10);
const actualHeight = parseInt(computedStyle.height, 10);
copyImg.style.position = "absolute"; copyImg.style.position = "absolute";
copyImg.style.top = `${image.offsetTop}px`; copyImg.style.top = `${image.offsetTop}px`;
copyImg.style.left = `${image.offsetLeft}px`; copyImg.style.left = `${image.offsetLeft}px`;
copyImg.style.width = `${actualWidth}px`;
copyImg.style.height = `${actualHeight}px`;
copyImg.className = imageData.className; copyImg.className = imageData.className;
let inOnebox = false; // insert after the current element so styling still will
let inQuote = false; // apply to original image firstChild selectors
for (let element = image; element; element = element.parentElement) { image.parentNode.insertBefore(copyImg, image.nextSibling);
if (element.tagName === "ARTICLE" && element.dataset.postId) {
break;
}
if (element.classList.contains("onebox")) {
inOnebox = true;
}
if (element.tagName === "BLOCKQUOTE") {
inQuote = true;
}
}
if (!inOnebox) {
copyImg.style.width = `${imageData.width}px`;
copyImg.style.height = `${imageData.height}px`;
}
if (inQuote && imageData.width && imageData.height) {
const computedStyle = window.getComputedStyle(image);
const width = parseInt(computedStyle.width, 10);
const height = width * (imageData.height / imageData.width);
image.width = width;
image.height = height;
copyImg.style.width = `${width}px`;
copyImg.style.height = `${height}px`;
}
image.parentNode.insertBefore(copyImg, image);
} else { } else {
image.classList.remove("d-lazyload-hidden"); image.classList.remove("d-lazyload-hidden");
} }