It seems parcel does not recognizes the image when "src" path is replaced with "data-src" attribute!
Hope this helps!
This is my HTML code:
Lazy-loading HTML Markup
Javsascript with intersectionObserverAPI:
[Lazy-loading with intersectionObserverAPI][2]
When the image isIntersecting I'm removing the lazy-image class after the "load" event.
[CSS lazy-image class][3]
[2]: https://i.stack.imgur.com/pf1OO.pngenter code here
[3]: https://i.stack.imgur.com/nOoSZ.png
This is JS Code:
/* Lazy Loading Hidden Images of Signin and SignUp Forms */
const imgTarget = document.querySelector(".more__itf__img");
// console.log(imgTargetsArray)
const loadingImage = (entries, observer) => {
const [entry] = entries;
console.log(entry);
if (!entry.isIntersecting) return;
// Replace image "src" attribute with "data-src" attribute.
// Loading of images happens behind the scenes, and once its finished
// loading that image it will emit the "load" event.
entry.target.src = entry.target.dataset.src;
// Listening for "load" event.
entry.target.addEventListener("load", ()=>{
entry.target.classList.remove("lazy-image");
});
observer.unobserve(entry.target);
};
const imgObserver = new IntersectionObserver(loadingImage, {
root: null,
threshold: 0.15,
// loading lazy-images before reaching the threshold value.
rootMargin: "200px",
});
if(imgTarget){
imgObserver.observe(imgTarget);
}
HTML Markup:
<section class="more__itf aos_element">
<img
src="./images/blur_img.svg"
data-src="./images/more_itf_purple.svg"
alt="more__itf"
class="more__itf__img lazy-image"
/>
<h3 class="more__itf__text">more in the future...</h3>
</section>
CSS Style:
/* Lazy-image loading */
.lazy-image {
filter: blur(10px);
}
Related
Render or Load the Lottie file animation then the whole page should be visible because right now when the page loads the lottie file loads with a bit dilay, which makes the lottie file section to collaps which spoils the loading experience, so i want the lottie file to render completly first then make the page visible
here is a similar setup on codepen link
` let homeBannerLottie = bodymovin.loadAnimation({
container: document.querySelector('.h_banner_lottie'),
render: 'svg',
loop: 0,
autoplay: false,
path: './assets/lottie/banner-lottie.json'
});
//! visibility home
let body = document.body;
let home = document.querySelector('.h_banner');
let homeLottie = document.querySelector('.h_banner_lottie');
homeLottie.style.visibility = 'visible';
setTimeout(() => {
body.style.visibility = 'visible';
home.style.visibility = 'visible';
homeBannerLottie.play();
}, 500);`
Yesterday I ran this code and everything worked as intended. The observer loaded in the images when they were intersecting the viewport:
<template>
<div id="gallery" class="gallery">
<div class="gallery-card">
<img src="../../images/1.jpg">
<img src="../../images/ph.png" data-src="../../images/2.jpg">
<img src="../../images/ph.png" data-src="../../images/3.jpg">
<img src="../../images/ph.png" data-src="../../images/4.jpg">
<img src="../../images/ph.png" data-src="../../images/5.jpg">
<img src="../../images/ph.png" data-src="../../images/6.jpg">
</div>
</div>
</template>
<script setup>
import {onMounted} from "vue";
onMounted(() => {
let config = {
rootMargin: '0px 0px 50px 0px',
threshold: 0
};
const observer = new IntersectionObserver(function(entries, self) {
console.log(entries)
entries.forEach(entry => {
if(entry.isIntersecting) {
const img = entry.target
img.src = img.dataset.src
self.unobserve(img);
}})
}, config);
const lazyImages = document.querySelectorAll('[data-src]');
lazyImages.forEach(img => {
console.log(img.src)
observer.observe(img);
});
})
</script>
But today the IntersectionObserver loads in all images at once during initial page load. I've tried to debug this with console.log() and the weird thing is that the correct img element is passed to the observer:
const lazyImages = document.querySelectorAll('[data-src]');
lazyImages.forEach(img => {
console.log(img.src)
observer.observe(img);
});
Output (x5, placeholder image):
http://localhost:3000/images/ph.png?3d03f427893c28791c9e0b8a347a277d
but the observer seems to receive an initial entries object with all isIntersecting properties set to true, which then loads in all images:
const observer = new IntersectionObserver(function (entries, self) {
console.log(entries)
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target
img.src = img.dataset.src
self.unobserve(img);
}
})
}, config);
Output:
Any way I can prevent that from happening?
Maybe a really late response. But anyway I'll share some ideas that could be helpful.
rootMargin: '0px 0px 50px 0px',
threshold: 0
that would be "use the viewport as root, consider a 50px bottom margin below the viewport's bottom (see this image to understand rootMargin).
What can be happening is that all your images are in fact already visible on the initial load, so all images are being intersected.
I had a similar problem with a custom scrollspy implementation but with the !isIntersecting condition. Solved it using a boolean flag that starts on false in order to catch the initial load, and it's set to true when the scrolling begins.
In your case, I think changing the:
if (entry.isIntersecting) {
line to:
if (entry.isIntersecting && startedIntersecting) {
would solve the initial intersecting problem.
This piece of code captures the first scroll, sets the flag to true and stops capturing the scroll event, leaving all the rest to the IntersectionObserver.
For this I used RxJS's fromEvent like this:
startedIntersecting = false;
fromEvent(document, 'scroll').pipe(
debounceTime(300),
distinctUntilChanged(),
takeWhile(() => startedIntersecting !== true)
).subscribe($evt => {
console.log('startedIntersecting', $evt);
startedIntersecting = true;
});
Hope it enlightens things a bit.
Hi I'm trying to show content after the page has been scrolled and revealed.
This is the code, should I make a function to check if the page has been loaded already then take off the animation? Thanks.
const allSections = document.querySelectorAll(".section");
const revealSection = function (entries, observer) {
const [entry] = entries;
if (!entry.isIntersecting) return;
entry.target.classList.remove("section--hidden");
observer.unobserve(entry.target);
};
const sectionObserver = new IntersectionObserver(revealSection, {
root: null,
threshold: 0.15,
});
allSections.forEach(function (section) {
sectionObserver.observe(section);
section.classList.add("section--hidden");
});
This is my first time attempting Lazy Loading, and I wanted to use IntersectionObserver. Got started by following along with this website. I honestly just want the content of the page to wait to load until it is less than 50% of the viewport's width below the viewport. I'm not sure how to observe all content, so I just selected the classes for the widgets that are being displayed, as they are the main content. Unfortunately my code doesn't seem to be doing much. I added a console.log at the beginning to make sure the file was linked correctly, and I tried to add more console.logs to log when the widget moves in and out of the capture area. The first console.log works, but nothing else happens.
Here is what I have:
console.log('file loaded');
const widgets = document.querySelectorAll('.widget');
const config = {
root: null,
rootMargin: '-50px 0px -55% 0px',
threshold: .5
};
let isLeaving = false;
let observer = new IntersectionObserver(function(entries, self) {
entries.forEach(entry => {
if (entry.isIntersecting) {
// we are ENTERING the "capturing frame". Set the flag.
isLeaving = true;
// Do something with entering entry
console.log("Widget loaded.");
self.unobserve(entry.target);
} else if (isLeaving) {
// we are EXITING the "capturing frame"
isLeaving = false;
// Do something with exiting entry
console.log("Widget is exiting.")
}
});
}, config);
widgets.forEach(widget => {
observer.observe(widget);
});
This code is in it's own js file. Where should I be linking it in the HTML file?
I'd appreciate any help I can get! Let me know if I need to provide more information.
EDIT: I have half answered my own question, but still need assistance. Please see Dynamically load HTML from other files onto page as you scroll below.
Original post:
I am working on revamping a site for members that contains an image gallery of photos that can be used for marketing purposes. Previously each category was split into sub-categories, resulting in a couple hundred pages and some pages with one image only. I've ditched the sub-categories and contained all the images on each category page. When you click on a thumbnail it the full size image opens in a lightbox with additional info
This is fine for most categories, but some categories are large and contain a couple hundred images. The problem is that on these pages, most of the time not all the thumbnails will load. Instead of using pagination to separate these large categories into multiple pages I want to have more content load dynamically as you scroll down. I do not know how to do this.
The code snippet on jQuery load more data on scroll looks like it could work, but is there a way to replace
html += '<p class="dynamic">Dynamic Data : This is test data.<br />Next line.</p>';
with something that will put the contents of file1.html in its place, followed by file2.html, and so on?
If not, is there some way of creating some sort of file that would have the thumbnail locations that could be referred to and loaded as a user scrolls down?
This YouTube video seemed like a good starting point, but it requires an AJAX call or some other call. I am not sure what this means and where it's calling to.
So I found a potential solution using IntersectionObserver thanks to deanhume.com. It works beautifully in Chrome and newer versions of Firefox.
Sadly, we still code our site work work with IE 11 as our standard. I don't know why, but we do so I have to deal with that. The problem is this solution does not work with an older version of Firefox I use for validation nor does it work with IE 11. So I want to use a polyfill.
The GitHub page for the polyfill talks about using <script src="https://polyfill.io/v3/polyfill.min.js?features=IntersectionObserver"></script> for older browser support, but it does not seem to work. I also placed the polyfill JavaScript file on the site and referenced it in the page head before any other Javascript, but it also does not seem to work.
What is the proper way to load a polyfill file?
The first line referencing it is: <script src="../pathto/polyfills/intersectionobserver.js"></script>
Then there is some unrelated stuff before getting to the Intersection Observer bit; CSS for the fade in and calling in the lazy-load script:
<style>
.fade-in {
animation-name: fadeIn;
animation-duration: 1.3s;
animation-timing-function: cubic-bezier(0, 0, 0.4, 1);
animation-fill-mode: forwards;
}
#keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
.centered {
display:block;
margin:0 auto;
}
</style>
<script type="module">
import LazyLoad from "../pathto/lazy-load.js";
LazyLoad.init();
</script>
Below is the lazy-load.js file.
const defaults = {
imageLoadedClass: 'js-lazy-image--handled',
imageSelector: '.js-lazy-image',
// If the image gets within 100px in the Y axis, start the download.
rootMargin: '100px 0px',
threshold: 0.01
};
let config,
images,
imageCount,
observer;
/**
* Fetches the image for the given URL
* #param {string} url
*/
function fetchImage(url) {
return new Promise((resolve, reject) => {
const image = new Image();
image.src = url;
image.onload = resolve;
image.onerror = reject;
});
}
/**
* Preloads the image
* #param {object} image
*/
function preloadImage(image) {
const src = image.dataset.src;
if (!src) {
return;
}
return fetchImage(src).then(() => { applyImage(image, src); });
}
/**
* Load all of the images immediately
* #param {NodeListOf<Element>} images
*/
function loadImagesImmediately(images) {
// foreach() is not supported in IE
for (let i = 0; i < images.length; i++) {
let image = images[i];
preloadImage(image);
}
}
/**
* Disconnect the observer
*/
function disconnect() {
if (!observer) {
return;
}
observer.disconnect();
}
/**
* On intersection
* #param {array} entries
*/
function onIntersection(entries) {
// Disconnect if we've already loaded all of the images
if (imageCount === 0) {
disconnect();
return;
}
// Loop through the entries
for (let i = 0; i < entries.length; i++) {
let entry = entries[i];
// Are we in viewport?
if (entry.intersectionRatio > 0) {
imageCount--;
// Stop watching and load the image
observer.unobserve(entry.target);
preloadImage(entry.target);
}
}
}
/**
* Apply the image
* #param {object} img
* #param {string} src
*/
function applyImage(img, src) {
// Prevent this from being lazy loaded a second time.
img.classList.add(config.imageLoadedClass);
img.src = src;
}
let LazyLoad = {
init: (options) => {
config = {...defaults, ...options};
images = document.querySelectorAll(config.imageSelector);
imageCount = images.length;
// If we don't have support for intersection observer, loads the images immediately
if (!('IntersectionObserver' in window)) {
loadImagesImmediately(images);
} else {
// It is supported, load the images
observer = new IntersectionObserver(onIntersection, config);
// foreach() is not supported in IE
for (let i = 0; i < images.length; i++) {
let image = images[i];
if (image.classList.contains(config.imageLoadedClass)) {
continue;
}
observer.observe(image);
}
}
}
};
export default LazyLoad;
And just in case it's needed, a sample of the image code on the page:
<img class="js-lazy-image" data-src="url/members/thumbs/ab_banff_np.jpg" alt="rockies, rocky mountains, trees, summer" aria-describedby="image_g2">
Now you can do lazy loading native on html using tag loading="lazy"
Example:
<img src="image.png" loading="lazy" width="200" height="200">
More information: https://css-tricks.com/native-lazy-loading/
To load more content in Javascript (and using jQuery), you can use the following:
$.ajax("path/to/your/file/file1.html")
.done((data) => {
$('body').append(data);
});
What this does:
Get the content of file1.html
Add the content to the body of your current website
More info:
https://api.jquery.com/Jquery.ajax/
https://developer.mozilla.org/en-US/docs/Web/Guide/AJAX