mirror of
https://github.com/angular/angular
synced 2026-05-24 09:28:37 +00:00
fix(common): execute checks and remove placeholder when image is already loaded (#55444)
With this commit, we're now able to perform checks even when the image has already been loaded (e.g., from the browser cache), and its `load` event would never be triggered. We use the [complete](https://html.spec.whatwg.org/#dom-img-complete) property, as specified, which indicates that the image state is fully available when the user agent has retrieved all the image data. This approach effectively triggers checks, as we no longer solely rely on the `load` event and consider that the image may already be loaded. This will not remove the placeholder until the `load` event fires (and it won't fire if the image is already "there"). This prevents memory leaks in development mode, as `load` and `error` event listeners are still attached to the image element. PR Close #55444
This commit is contained in:
parent
1549afe10e
commit
c3115b882e
1 changed files with 30 additions and 4 deletions
|
|
@ -708,6 +708,8 @@ export class NgOptimizedImage implements OnInit, OnChanges, OnDestroy {
|
|||
|
||||
const removeLoadListenerFn = this.renderer.listen(img, 'load', callback);
|
||||
const removeErrorListenerFn = this.renderer.listen(img, 'error', callback);
|
||||
|
||||
callOnLoadIfImageIsLoaded(img, callback);
|
||||
}
|
||||
|
||||
/** @nodoc */
|
||||
|
|
@ -1025,7 +1027,7 @@ function assertNoImageDistortion(
|
|||
img: HTMLImageElement,
|
||||
renderer: Renderer2,
|
||||
) {
|
||||
const removeLoadListenerFn = renderer.listen(img, 'load', () => {
|
||||
const callback = () => {
|
||||
removeLoadListenerFn();
|
||||
removeErrorListenerFn();
|
||||
const computedStyle = window.getComputedStyle(img);
|
||||
|
|
@ -1118,7 +1120,9 @@ function assertNoImageDistortion(
|
|||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const removeLoadListenerFn = renderer.listen(img, 'load', callback);
|
||||
|
||||
// We only listen to the `error` event to remove the `load` event listener because it will not be
|
||||
// fired if the image fails to load. This is done to prevent memory leaks in development mode
|
||||
|
|
@ -1128,6 +1132,8 @@ function assertNoImageDistortion(
|
|||
removeLoadListenerFn();
|
||||
removeErrorListenerFn();
|
||||
});
|
||||
|
||||
callOnLoadIfImageIsLoaded(img, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1173,7 +1179,7 @@ function assertNonZeroRenderedHeight(
|
|||
img: HTMLImageElement,
|
||||
renderer: Renderer2,
|
||||
) {
|
||||
const removeLoadListenerFn = renderer.listen(img, 'load', () => {
|
||||
const callback = () => {
|
||||
removeLoadListenerFn();
|
||||
removeErrorListenerFn();
|
||||
const renderedHeight = img.clientHeight;
|
||||
|
|
@ -1189,13 +1195,17 @@ function assertNonZeroRenderedHeight(
|
|||
),
|
||||
);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const removeLoadListenerFn = renderer.listen(img, 'load', callback);
|
||||
|
||||
// See comments in the `assertNoImageDistortion`.
|
||||
const removeErrorListenerFn = renderer.listen(img, 'error', () => {
|
||||
removeLoadListenerFn();
|
||||
removeErrorListenerFn();
|
||||
});
|
||||
|
||||
callOnLoadIfImageIsLoaded(img, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1338,6 +1348,22 @@ function assertPlaceholderDimensions(dir: NgOptimizedImage, imgElement: HTMLImag
|
|||
}
|
||||
}
|
||||
|
||||
function callOnLoadIfImageIsLoaded(img: HTMLImageElement, callback: VoidFunction): void {
|
||||
// https://html.spec.whatwg.org/multipage/embedded-content.html#dom-img-complete
|
||||
// The spec defines that `complete` is truthy once its request state is fully available.
|
||||
// The image may already be available if it’s loaded from the browser cache.
|
||||
// In that case, the `load` event will not fire at all, meaning that all setup
|
||||
// callbacks listening for the `load` event will not be invoked.
|
||||
// In Safari, there is a known behavior where the `complete` property of an
|
||||
// `HTMLImageElement` may sometimes return `true` even when the image is not fully loaded.
|
||||
// Checking both `img.complete` and `img.naturalWidth` is the most reliable way to
|
||||
// determine if an image has been fully loaded, especially in browsers where the
|
||||
// `complete` property may return `true` prematurely.
|
||||
if (img.complete && img.naturalWidth) {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
function round(input: number): number | string {
|
||||
return Number.isInteger(input) ? input : input.toFixed(2);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue