diff --git a/packages/next/client/image.tsx b/packages/next/client/image.tsx index 504fcc3e4f5ca..764f2d4181076 100644 --- a/packages/next/client/image.tsx +++ b/packages/next/client/image.tsx @@ -48,10 +48,10 @@ configDeviceSizes.sort((a, b) => a - b) configImageSizes.sort((a, b) => a - b) let cachedObserver: IntersectionObserver -const IntersectionObserver = - typeof window !== 'undefined' ? window.IntersectionObserver : null function getObserver(): IntersectionObserver | undefined { + const IntersectionObserver = + typeof window !== 'undefined' ? window.IntersectionObserver : null // Return shared instance of IntersectionObserver if already created if (cachedObserver) { return cachedObserver @@ -61,20 +61,12 @@ function getObserver(): IntersectionObserver | undefined { if (!IntersectionObserver) { return undefined } - return (cachedObserver = new IntersectionObserver( (entries) => { entries.forEach((entry) => { if (entry.isIntersecting) { let lazyImage = entry.target as HTMLImageElement - if (lazyImage.dataset.src) { - lazyImage.src = lazyImage.dataset.src - } - if (lazyImage.dataset.srcset) { - lazyImage.srcset = lazyImage.dataset.srcset - } - lazyImage.style.visibility = 'visible' - lazyImage.classList.remove('__lazy') + unLazifyImage(lazyImage) cachedObserver.unobserve(lazyImage) } }) @@ -83,6 +75,17 @@ function getObserver(): IntersectionObserver | undefined { )) } +function unLazifyImage(lazyImage: HTMLImageElement): void { + if (lazyImage.dataset.src) { + lazyImage.src = lazyImage.dataset.src + } + if (lazyImage.dataset.srcset) { + lazyImage.srcset = lazyImage.dataset.srcset + } + lazyImage.style.visibility = 'visible' + lazyImage.classList.remove('__lazy') +} + function getDeviceSizes(width: number | undefined): number[] { if (typeof width !== 'number') { return configDeviceSizes @@ -234,6 +237,11 @@ export default function Image({ lazy = true } + if (typeof window !== 'undefined' && !window.IntersectionObserver) { + // Rendering client side on browser without intersection observer + lazy = false + } + useEffect(() => { const target = thisEl.current @@ -246,6 +254,9 @@ export default function Image({ return () => { observer.unobserve(target) } + } else { + //browsers without intersection observer + unLazifyImage(target) } } }, [thisEl, lazy]) diff --git a/test/integration/image-component/basic/pages/lazy.js b/test/integration/image-component/basic/pages/lazy.js index 161b530bc3c3f..d91705249fb49 100644 --- a/test/integration/image-component/basic/pages/lazy.js +++ b/test/integration/image-component/basic/pages/lazy.js @@ -1,5 +1,6 @@ import React from 'react' import Image from 'next/image' +import Link from 'next/link' const Lazy = () => { return ( @@ -45,6 +46,9 @@ const Lazy = () => { height={400} width={1900} > + + observer + ) } diff --git a/test/integration/image-component/basic/pages/missing-observer.js b/test/integration/image-component/basic/pages/missing-observer.js new file mode 100644 index 0000000000000..7def70f2f0c7d --- /dev/null +++ b/test/integration/image-component/basic/pages/missing-observer.js @@ -0,0 +1,25 @@ +import React, { useLayoutEffect } from 'react' +import Image from 'next/image' + +const Lazy = () => { + useLayoutEffect(() => { + IntersectionObserver = null //eslint-disable-line + }) + return ( +
+ This is a page with one lazy-loaded image, to be used in the test for + browsers without intersection observer. +
+