← home
Using the Enhanced Lazy Image Component
Using the Enhanced Lazy Image Component
Overview
The enhanced lazy-image.html component now supports WebP format with responsive images, providing optimal performance across all devices and browsers.
Basic Usage
In your blog posts or pages, include images like this:
<div class="lazy-image-wrapper">
<picture>
<source
type="image/webp"
data-srcset="/assets/images/posts/boracay-beach-640w.webp 640w,
/assets/images/posts/boracay-beach-1280w.webp 1280w,
/assets/images/posts/boracay-beach-1920w.webp 1920w"
sizes="(max-width: 640px) 640px, (max-width: 1280px) 1280px, 1920px"
/>
<source
type="image/jpeg"
data-srcset="/assets/images/posts/boracay-beach-640w.jpg 640w,
/assets/images/posts/boracay-beach-1280w.jpg 1280w,
/assets/images/posts/boracay-beach-1920w.jpg 1920w"
sizes="(max-width: 640px) 640px, (max-width: 1280px) 1280px, 1920px"
/>
<img
data-src="/assets/images/posts/boracay-beach./assets/images/posts/boracay-beach"
alt="White sand beach in Boracay with crystal clear turquoise water"
class="lazy-image "
loading="lazy"
width="1920"
height="1440"
/>
</picture>
<noscript>
<picture>
<source
type="image/webp"
srcset="/assets/images/posts/boracay-beach-640w.webp 640w,
/assets/images/posts/boracay-beach-1280w.webp 1280w,
/assets/images/posts/boracay-beach-1920w.webp 1920w"
sizes="(max-width: 640px) 640px, (max-width: 1280px) 1280px, 1920px"
/>
<img
src="/assets/images/posts/boracay-beach./assets/images/posts/boracay-beach"
srcset="/assets/images/posts/boracay-beach-640w.jpg 640w,
/assets/images/posts/boracay-beach-1280w.jpg 1280w,
/assets/images/posts/boracay-beach-1920w.jpg 1920w"
sizes="(max-width: 640px) 640px, (max-width: 1280px) 1280px, 1920px"
alt="White sand beach in Boracay with crystal clear turquoise water"
class=""
width="1920"
height="1440"
/>
</picture>
</noscript>
</div>
<script>
(function() {
if (!window.lazyImageObserver) {
window.lazyImageObserver = {
init: function() {
var images = document.querySelectorAll('img.lazy-image[data-src]');
var sources = document.querySelectorAll('source[data-srcset]');
// Function to load an image and its sources
var loadImage = function(img) {
// Load source elements first
var picture = img.closest('picture');
if (picture) {
var pictureSources = picture.querySelectorAll('source[data-srcset]');
pictureSources.forEach(function(source) {
if (source.dataset.srcset) {
source.srcset = source.dataset.srcset;
source.removeAttribute('data-srcset');
}
});
}
// Load img element
if (img.dataset.src) {
img.src = img.dataset.src;
img.removeAttribute('data-src');
img.classList.add('lazy-loaded');
if (window.performance && window.performance.mark) {
performance.mark('image-loaded');
}
}
};
if ('loading' in HTMLImageElement.prototype) {
// Native lazy loading support
images.forEach(function(img) {
loadImage(img);
});
} else if ('IntersectionObserver' in window) {
// IntersectionObserver fallback
var imageObserver = new IntersectionObserver(function(entries, observer) {
entries.forEach(function(entry) {
if (entry.isIntersecting) {
var img = entry.target;
loadImage(img);
observer.unobserve(img);
}
});
}, {
rootMargin: '50px'
});
images.forEach(function(img) {
imageObserver.observe(img);
});
} else {
// Fallback for older browsers - load everything
images.forEach(function(img) {
loadImage(img);
});
}
}
};
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', window.lazyImageObserver.init);
} else {
window.lazyImageObserver.init();
}
}
})();
</script>
Important Notes:
srcshould NOT include the file extension (.jpg)alttext is REQUIRED for accessibilitywidthandheightshould match the original image dimensions- These prevent layout shift (CLS) and improve Core Web Vitals
How It Works
The component automatically generates this HTML:
<picture>
<!-- WebP sources for modern browsers -->
<source type="image/webp"
srcset="/assets/images/posts/boracay-beach-640w.webp 640w,
/assets/images/posts/boracay-beach-1280w.webp 1280w,
/assets/images/posts/boracay-beach-1920w.webp 1920w"
sizes="(max-width: 640px) 640px, (max-width: 1280px) 1280px, 1920px" />
<!-- JPEG fallback for older browsers -->
<source type="image/jpeg"
srcset="/assets/images/posts/boracay-beach-640w.jpg 640w,
/assets/images/posts/boracay-beach-1280w.jpg 1280w,
/assets/images/posts/boracay-beach-1920w.jpg 1920w"
sizes="(max-width: 640px) 640px, (max-width: 1280px) 1280px, 1920px" />
<!-- Main image tag -->
<img src="/assets/images/posts/boracay-beach.jpg"
alt="White sand beach in Boracay with crystal clear turquoise water"
loading="lazy"
width="1920"
height="1440" />
</picture>
What Gets Loaded
On a Modern Desktop (Chrome, Firefox, Edge, Safari)
- Viewport > 1280px:
boracay-beach-1920w.webp(397KB) - Viewport 640-1280px:
boracay-beach-1280w.webp(184KB) - Total savings vs original: 75% smaller
On Mobile (iPhone, Android)
- Viewport < 640px:
boracay-beach-640w.webp(47KB) - Total savings vs original: 97% smaller!
On Older Browsers (IE11, older Safari)
- Falls back to JPEG versions
- Still gets responsive sizing benefits
- Loads
boracay-beach-640w.jpg(108KB) on mobile
Custom Sizes
You can override the default sizes attribute:
<div class="lazy-image-wrapper">
<picture>
<source
type="image/webp"
data-srcset="/assets/images/posts/photo-640w.webp 640w,
/assets/images/posts/photo-1280w.webp 1280w,
/assets/images/posts/photo-1920w.webp 1920w"
sizes="(max-width: 768px) 100vw, 50vw"
/>
<source
type="image/jpeg"
data-srcset="/assets/images/posts/photo-640w.jpg 640w,
/assets/images/posts/photo-1280w.jpg 1280w,
/assets/images/posts/photo-1920w.jpg 1920w"
sizes="(max-width: 768px) 100vw, 50vw"
/>
<img
data-src="/assets/images/posts/photo./assets/images/posts/photo"
alt="Description"
class="lazy-image "
loading="lazy"
width="1920"
height="1080"
/>
</picture>
<noscript>
<picture>
<source
type="image/webp"
srcset="/assets/images/posts/photo-640w.webp 640w,
/assets/images/posts/photo-1280w.webp 1280w,
/assets/images/posts/photo-1920w.webp 1920w"
sizes="(max-width: 768px) 100vw, 50vw"
/>
<img
src="/assets/images/posts/photo./assets/images/posts/photo"
srcset="/assets/images/posts/photo-640w.jpg 640w,
/assets/images/posts/photo-1280w.jpg 1280w,
/assets/images/posts/photo-1920w.jpg 1920w"
sizes="(max-width: 768px) 100vw, 50vw"
alt="Description"
class=""
width="1920"
height="1080"
/>
</picture>
</noscript>
</div>
<script>
(function() {
if (!window.lazyImageObserver) {
window.lazyImageObserver = {
init: function() {
var images = document.querySelectorAll('img.lazy-image[data-src]');
var sources = document.querySelectorAll('source[data-srcset]');
// Function to load an image and its sources
var loadImage = function(img) {
// Load source elements first
var picture = img.closest('picture');
if (picture) {
var pictureSources = picture.querySelectorAll('source[data-srcset]');
pictureSources.forEach(function(source) {
if (source.dataset.srcset) {
source.srcset = source.dataset.srcset;
source.removeAttribute('data-srcset');
}
});
}
// Load img element
if (img.dataset.src) {
img.src = img.dataset.src;
img.removeAttribute('data-src');
img.classList.add('lazy-loaded');
if (window.performance && window.performance.mark) {
performance.mark('image-loaded');
}
}
};
if ('loading' in HTMLImageElement.prototype) {
// Native lazy loading support
images.forEach(function(img) {
loadImage(img);
});
} else if ('IntersectionObserver' in window) {
// IntersectionObserver fallback
var imageObserver = new IntersectionObserver(function(entries, observer) {
entries.forEach(function(entry) {
if (entry.isIntersecting) {
var img = entry.target;
loadImage(img);
observer.unobserve(img);
}
});
}, {
rootMargin: '50px'
});
images.forEach(function(img) {
imageObserver.observe(img);
});
} else {
// Fallback for older browsers - load everything
images.forEach(function(img) {
loadImage(img);
});
}
}
};
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', window.lazyImageObserver.init);
} else {
window.lazyImageObserver.init();
}
}
})();
</script>
This tells the browser:
- On mobile: use full viewport width
- On desktop: use 50% of viewport width
Additional CSS Classes
Add custom classes for styling:
<div class="lazy-image-wrapper">
<picture>
<source
type="image/webp"
data-srcset="/assets/images/posts/photo-640w.webp 640w,
/assets/images/posts/photo-1280w.webp 1280w,
/assets/images/posts/photo-1920w.webp 1920w"
sizes="(max-width: 640px) 640px, (max-width: 1280px) 1280px, 1920px"
/>
<source
type="image/jpeg"
data-srcset="/assets/images/posts/photo-640w.jpg 640w,
/assets/images/posts/photo-1280w.jpg 1280w,
/assets/images/posts/photo-1920w.jpg 1920w"
sizes="(max-width: 640px) 640px, (max-width: 1280px) 1280px, 1920px"
/>
<img
data-src="/assets/images/posts/photo./assets/images/posts/photo"
alt="Description"
class="lazy-image rounded shadow-lg"
loading="lazy"
width="1920"
height="1080"
/>
</picture>
<noscript>
<picture>
<source
type="image/webp"
srcset="/assets/images/posts/photo-640w.webp 640w,
/assets/images/posts/photo-1280w.webp 1280w,
/assets/images/posts/photo-1920w.webp 1920w"
sizes="(max-width: 640px) 640px, (max-width: 1280px) 1280px, 1920px"
/>
<img
src="/assets/images/posts/photo./assets/images/posts/photo"
srcset="/assets/images/posts/photo-640w.jpg 640w,
/assets/images/posts/photo-1280w.jpg 1280w,
/assets/images/posts/photo-1920w.jpg 1920w"
sizes="(max-width: 640px) 640px, (max-width: 1280px) 1280px, 1920px"
alt="Description"
class="rounded shadow-lg"
width="1920"
height="1080"
/>
</picture>
</noscript>
</div>
<script>
(function() {
if (!window.lazyImageObserver) {
window.lazyImageObserver = {
init: function() {
var images = document.querySelectorAll('img.lazy-image[data-src]');
var sources = document.querySelectorAll('source[data-srcset]');
// Function to load an image and its sources
var loadImage = function(img) {
// Load source elements first
var picture = img.closest('picture');
if (picture) {
var pictureSources = picture.querySelectorAll('source[data-srcset]');
pictureSources.forEach(function(source) {
if (source.dataset.srcset) {
source.srcset = source.dataset.srcset;
source.removeAttribute('data-srcset');
}
});
}
// Load img element
if (img.dataset.src) {
img.src = img.dataset.src;
img.removeAttribute('data-src');
img.classList.add('lazy-loaded');
if (window.performance && window.performance.mark) {
performance.mark('image-loaded');
}
}
};
if ('loading' in HTMLImageElement.prototype) {
// Native lazy loading support
images.forEach(function(img) {
loadImage(img);
});
} else if ('IntersectionObserver' in window) {
// IntersectionObserver fallback
var imageObserver = new IntersectionObserver(function(entries, observer) {
entries.forEach(function(entry) {
if (entry.isIntersecting) {
var img = entry.target;
loadImage(img);
observer.unobserve(img);
}
});
}, {
rootMargin: '50px'
});
images.forEach(function(img) {
imageObserver.observe(img);
});
} else {
// Fallback for older browsers - load everything
images.forEach(function(img) {
loadImage(img);
});
}
}
};
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', window.lazyImageObserver.init);
} else {
window.lazyImageObserver.init();
}
}
})();
</script>
Finding Image Dimensions
If you don’t know the width and height of your image:
# On macOS
sips -g pixelWidth -g pixelHeight assets/images/posts/photo.jpg
# Or use ImageMagick
identify -format "%wx%h" assets/images/posts/photo.jpg
Optimization Workflow
- Add new image to repository
cp ~/Downloads/new-photo.jpg assets/images/posts/ - Optimize the image
bash tools/optimize-images.sh assets/images/posts/new-photo.jpg - Check what was generated
ls -lh assets/images/posts/new-photo* - Get dimensions
sips -g pixelWidth -g pixelHeight assets/images/posts/new-photo.jpg - Use in your post ```liquid
## Accessibility Best Practices
### Good Alt Text Examples
✅ **Good**: "Aerial view of Manhattan skyline at dusk with illuminated buildings"
✅ **Good**: "Developer typing code on laptop with multiple monitors"
✅ **Good**: "Graph showing 40% increase in website performance"
### Bad Alt Text Examples
❌ **Bad**: "Image of city"
❌ **Bad**: "Picture of someone coding"
❌ **Bad**: "DSC_1234.jpg"
❌ **Bad**: "" (empty - should only be used for purely decorative images)
### Complex Images
For charts, diagrams, or complex images, provide context in surrounding text:
```markdown
The chart below shows the dramatic improvement in page load times after
implementing image optimization...
<div class="lazy-image-wrapper">
<picture>
<source
type="image/webp"
data-srcset="/assets/images/posts/performance-chart-640w.webp 640w,
/assets/images/posts/performance-chart-1280w.webp 1280w,
/assets/images/posts/performance-chart-1920w.webp 1920w"
sizes="(max-width: 640px) 640px, (max-width: 1280px) 1280px, 1920px"
/>
<source
type="image/jpeg"
data-srcset="/assets/images/posts/performance-chart-640w.jpg 640w,
/assets/images/posts/performance-chart-1280w.jpg 1280w,
/assets/images/posts/performance-chart-1920w.jpg 1920w"
sizes="(max-width: 640px) 640px, (max-width: 1280px) 1280px, 1920px"
/>
<img
data-src="/assets/images/posts/performance-chart./assets/images/posts/performance-chart"
alt="Bar chart comparing page load times before and after optimization"
class="lazy-image "
loading="lazy"
width="1920"
height="1080"
/>
</picture>
<noscript>
<picture>
<source
type="image/webp"
srcset="/assets/images/posts/performance-chart-640w.webp 640w,
/assets/images/posts/performance-chart-1280w.webp 1280w,
/assets/images/posts/performance-chart-1920w.webp 1920w"
sizes="(max-width: 640px) 640px, (max-width: 1280px) 1280px, 1920px"
/>
<img
src="/assets/images/posts/performance-chart./assets/images/posts/performance-chart"
srcset="/assets/images/posts/performance-chart-640w.jpg 640w,
/assets/images/posts/performance-chart-1280w.jpg 1280w,
/assets/images/posts/performance-chart-1920w.jpg 1920w"
sizes="(max-width: 640px) 640px, (max-width: 1280px) 1280px, 1920px"
alt="Bar chart comparing page load times before and after optimization"
class=""
width="1920"
height="1080"
/>
</picture>
</noscript>
</div>
<script>
(function() {
if (!window.lazyImageObserver) {
window.lazyImageObserver = {
init: function() {
var images = document.querySelectorAll('img.lazy-image[data-src]');
var sources = document.querySelectorAll('source[data-srcset]');
// Function to load an image and its sources
var loadImage = function(img) {
// Load source elements first
var picture = img.closest('picture');
if (picture) {
var pictureSources = picture.querySelectorAll('source[data-srcset]');
pictureSources.forEach(function(source) {
if (source.dataset.srcset) {
source.srcset = source.dataset.srcset;
source.removeAttribute('data-srcset');
}
});
}
// Load img element
if (img.dataset.src) {
img.src = img.dataset.src;
img.removeAttribute('data-src');
img.classList.add('lazy-loaded');
if (window.performance && window.performance.mark) {
performance.mark('image-loaded');
}
}
};
if ('loading' in HTMLImageElement.prototype) {
// Native lazy loading support
images.forEach(function(img) {
loadImage(img);
});
} else if ('IntersectionObserver' in window) {
// IntersectionObserver fallback
var imageObserver = new IntersectionObserver(function(entries, observer) {
entries.forEach(function(entry) {
if (entry.isIntersecting) {
var img = entry.target;
loadImage(img);
observer.unobserve(img);
}
});
}, {
rootMargin: '50px'
});
images.forEach(function(img) {
imageObserver.observe(img);
});
} else {
// Fallback for older browsers - load everything
images.forEach(function(img) {
loadImage(img);
});
}
}
};
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', window.lazyImageObserver.init);
} else {
window.lazyImageObserver.init();
}
}
})();
</script>
As shown, the optimized pages load 73% faster on average.
Verifying It Works
After adding images to your site:
- Build locally
bundle exec jekyll serve - Open DevTools (Network tab)
- On desktop: Look for
.webpfiles being loaded - On mobile: Check that smaller sizes are loaded
- On desktop: Look for
- Test responsive behavior
- Resize browser window
- Check which image sizes are loaded at different breakpoints
- Validate Core Web Vitals
- Use Lighthouse in Chrome DevTools
- Check for CLS (Cumulative Layout Shift) - should be < 0.1
- Check for LCP (Largest Contentful Paint) - should be < 2.5s
Performance Impact
Before Optimization
- Desktop loads 1.5MB JPEG
- Mobile loads same 1.5MB JPEG
- Total page weight: ~3MB with 2 images
After Optimization
- Desktop loads 397KB WebP
- Mobile loads 47KB WebP
- Total page weight: ~500KB with 2 images
- 83% reduction in page weight
Troubleshooting
Images not lazy-loading
- Check browser console for JavaScript errors
- Verify
loading="lazy"attribute is present in rendered HTML - Some browsers require polyfill for older versions
WebP not loading
- Check file actually exists:
ls assets/images/posts/*.webp - Verify browser supports WebP (all modern browsers do)
- Check DevTools Network tab to see what’s actually loading
Wrong image size loading
- Inspect rendered HTML and check
srcsetattribute - Use DevTools responsive mode to test different viewport sizes
- Verify
sizesattribute matches your layout
Layout shift occurring
- Ensure
widthandheightattributes are set - Check CSS isn’t overriding aspect ratio
- Verify dimensions match actual image dimensions
Examples from Real Posts
<!-- Hero image at top of post -->
<div class="lazy-image-wrapper">
<picture>
<source
type="image/webp"
data-srcset="/assets/images/posts/manila-night-640w.webp 640w,
/assets/images/posts/manila-night-1280w.webp 1280w,
/assets/images/posts/manila-night-1920w.webp 1920w"
sizes="(max-width: 640px) 640px, (max-width: 1280px) 1280px, 1920px"
/>
<source
type="image/jpeg"
data-srcset="/assets/images/posts/manila-night-640w.jpg 640w,
/assets/images/posts/manila-night-1280w.jpg 1280w,
/assets/images/posts/manila-night-1920w.jpg 1920w"
sizes="(max-width: 640px) 640px, (max-width: 1280px) 1280px, 1920px"
/>
<img
data-src="/assets/images/posts/manila-night./assets/images/posts/manila-night"
alt="Manila skyline at night with illuminated skyscrapers reflected in water"
class="lazy-image "
loading="lazy"
width="1920"
height="1436"
/>
</picture>
<noscript>
<picture>
<source
type="image/webp"
srcset="/assets/images/posts/manila-night-640w.webp 640w,
/assets/images/posts/manila-night-1280w.webp 1280w,
/assets/images/posts/manila-night-1920w.webp 1920w"
sizes="(max-width: 640px) 640px, (max-width: 1280px) 1280px, 1920px"
/>
<img
src="/assets/images/posts/manila-night./assets/images/posts/manila-night"
srcset="/assets/images/posts/manila-night-640w.jpg 640w,
/assets/images/posts/manila-night-1280w.jpg 1280w,
/assets/images/posts/manila-night-1920w.jpg 1920w"
sizes="(max-width: 640px) 640px, (max-width: 1280px) 1280px, 1920px"
alt="Manila skyline at night with illuminated skyscrapers reflected in water"
class=""
width="1920"
height="1436"
/>
</picture>
</noscript>
</div>
<script>
(function() {
if (!window.lazyImageObserver) {
window.lazyImageObserver = {
init: function() {
var images = document.querySelectorAll('img.lazy-image[data-src]');
var sources = document.querySelectorAll('source[data-srcset]');
// Function to load an image and its sources
var loadImage = function(img) {
// Load source elements first
var picture = img.closest('picture');
if (picture) {
var pictureSources = picture.querySelectorAll('source[data-srcset]');
pictureSources.forEach(function(source) {
if (source.dataset.srcset) {
source.srcset = source.dataset.srcset;
source.removeAttribute('data-srcset');
}
});
}
// Load img element
if (img.dataset.src) {
img.src = img.dataset.src;
img.removeAttribute('data-src');
img.classList.add('lazy-loaded');
if (window.performance && window.performance.mark) {
performance.mark('image-loaded');
}
}
};
if ('loading' in HTMLImageElement.prototype) {
// Native lazy loading support
images.forEach(function(img) {
loadImage(img);
});
} else if ('IntersectionObserver' in window) {
// IntersectionObserver fallback
var imageObserver = new IntersectionObserver(function(entries, observer) {
entries.forEach(function(entry) {
if (entry.isIntersecting) {
var img = entry.target;
loadImage(img);
observer.unobserve(img);
}
});
}, {
rootMargin: '50px'
});
images.forEach(function(img) {
imageObserver.observe(img);
});
} else {
// Fallback for older browsers - load everything
images.forEach(function(img) {
loadImage(img);
});
}
}
};
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', window.lazyImageObserver.init);
} else {
window.lazyImageObserver.init();
}
}
})();
</script>
<!-- Smaller inline image -->
<div class="lazy-image-wrapper">
<picture>
<source
type="image/webp"
data-srcset="/assets/images/posts/school-songs-640w.webp 640w,
/assets/images/posts/school-songs-1280w.webp 1280w,
/assets/images/posts/school-songs-1920w.webp 1920w"
sizes="(max-width: 640px) 640px, (max-width: 1280px) 1280px, 1920px"
/>
<source
type="image/jpeg"
data-srcset="/assets/images/posts/school-songs-640w.jpg 640w,
/assets/images/posts/school-songs-1280w.jpg 1280w,
/assets/images/posts/school-songs-1920w.jpg 1920w"
sizes="(max-width: 640px) 640px, (max-width: 1280px) 1280px, 1920px"
/>
<img
data-src="/assets/images/posts/school-songs./assets/images/posts/school-songs"
alt="Sheet music showing traditional school songs"
class="lazy-image border"
loading="lazy"
width="1500"
height="1000"
/>
</picture>
<noscript>
<picture>
<source
type="image/webp"
srcset="/assets/images/posts/school-songs-640w.webp 640w,
/assets/images/posts/school-songs-1280w.webp 1280w,
/assets/images/posts/school-songs-1920w.webp 1920w"
sizes="(max-width: 640px) 640px, (max-width: 1280px) 1280px, 1920px"
/>
<img
src="/assets/images/posts/school-songs./assets/images/posts/school-songs"
srcset="/assets/images/posts/school-songs-640w.jpg 640w,
/assets/images/posts/school-songs-1280w.jpg 1280w,
/assets/images/posts/school-songs-1920w.jpg 1920w"
sizes="(max-width: 640px) 640px, (max-width: 1280px) 1280px, 1920px"
alt="Sheet music showing traditional school songs"
class="border"
width="1500"
height="1000"
/>
</picture>
</noscript>
</div>
<script>
(function() {
if (!window.lazyImageObserver) {
window.lazyImageObserver = {
init: function() {
var images = document.querySelectorAll('img.lazy-image[data-src]');
var sources = document.querySelectorAll('source[data-srcset]');
// Function to load an image and its sources
var loadImage = function(img) {
// Load source elements first
var picture = img.closest('picture');
if (picture) {
var pictureSources = picture.querySelectorAll('source[data-srcset]');
pictureSources.forEach(function(source) {
if (source.dataset.srcset) {
source.srcset = source.dataset.srcset;
source.removeAttribute('data-srcset');
}
});
}
// Load img element
if (img.dataset.src) {
img.src = img.dataset.src;
img.removeAttribute('data-src');
img.classList.add('lazy-loaded');
if (window.performance && window.performance.mark) {
performance.mark('image-loaded');
}
}
};
if ('loading' in HTMLImageElement.prototype) {
// Native lazy loading support
images.forEach(function(img) {
loadImage(img);
});
} else if ('IntersectionObserver' in window) {
// IntersectionObserver fallback
var imageObserver = new IntersectionObserver(function(entries, observer) {
entries.forEach(function(entry) {
if (entry.isIntersecting) {
var img = entry.target;
loadImage(img);
observer.unobserve(img);
}
});
}, {
rootMargin: '50px'
});
images.forEach(function(img) {
imageObserver.observe(img);
});
} else {
// Fallback for older browsers - load everything
images.forEach(function(img) {
loadImage(img);
});
}
}
};
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', window.lazyImageObserver.init);
} else {
window.lazyImageObserver.init();
}
}
})();
</script>