Hi Andrea, I believe I am missing something here.
I am using following initialization set on v8.7.1:
elements_selector: '.is-lazy',
class_loaded: 'is-lazy-loaded'
Class equal to the class_loaded property is not being added to the element with data-src being set. style and data-was-processed attributes are created and set properly, as in your demo.
callback_load function doesn't fire as well, if set.
I'd like to use an additional class in order to be able to control the appearance entirely on the CSS side.
Class is being added for conventional images, also using srcset.
Thank you!
Unfortunately there is no way of detecting if an image has been loaded if it's a background-image, and I believe this is why lazyload works in the way it does in cases like this.
I faced a similar problem and wrote a workaround. My solution involves placing an img as a direct child of the element I want the background-image on, making the img visually hidden and listening for it to enter viewport, then setting the background-image and a --loaded modifier class on the parent.
The code is much simpler than the explanation, haha:
<div class="lazy-image">
<img class="lazy-image__trigger lazyload" data-src="path/to/image" />
</div>
const lazyImages = new LazyLoad({
elements_selector: '.lazyload',
class_loaded: '--loaded',
callback_load: function(el) {
const imgArea = el.parentNode
const imgSrc = el.getAttribute('data-src')
imgArea.style.backgroundImage = `url('${imgSrc}')`
imgArea.classList.add('--loaded')
}
})
Pretty easy to modify this concept to fit your needs. Hope it helps!
Hey and thank you for the response! This is a cool workaround I'm aware of, thank you!
I've switched to LazyLoad from this one: https://github.com/eisbehr-/jquery.lazy
Looks like Eisbehr figured it out without using an <img> as a middleman.
Well since shipping is today, I'll have to implement this solution. @verlok please keep us updated on this in case you come there one day! Oh and maybe give us a hint in docs :)
Ah! Cool! :)
Hijacking thread to discuss possible implementations of this straight into lazyload:
Check if the selected element lacks src/srcset support, then create an img and use that as the trigger. Maybe include some init option to use this method instead of the defaut one.
Any thoughts @verlok?
Hi.
I've been worried about the same problem.
And I modified the 8.7.1 version myself.
This is the modified part of the source.
First, the _reveal function
if (element.tagName === "IMG" || element.tagName === "IFRAME" || getData(element, settings.data_src)) {
element.addEventListener("load", loadCallback);
element.addEventListener("error", errorCallback);
addClass(element, settings.class_loading);
}
And, setSources function
if (elementSrc) {
var image = new Image();
image.onload = function() {
element.style.backgroundImage = 'url("' + image.src + '")';
element.dispatchEvent(new Event('load'));
}
image.onerror = function() {
element.dispatchEvent(new Event('error'));
}
image.src = elementSrc;
}
It is a modified file.
vanilla-lazyload-8.7.1.txt
Duplicate of #198, #151, #136.
Hey there everyone,
thank you for the questions and the ideas.
Since you鈥檙e not the first asking about adding/removing classes or triggering events when a background image is loaded, I might think about adding the feature you鈥檙e suggesting inside my script.
But I have a question to ask beforehand.
Why do you use background images instead of proper imgs for your content images? Isn鈥檛 the img best for usability (users can click and save images) and accessibility (screen readers can treat the images like images, and real the alt content instead)?
@verlok sure,
Using 'background image + new Image ()' I see that there are issues with performance and accessibility.
But I think.
The background image has the advantage of being easier to create using 'CSS background-size'.
You can also include IR (image replacement) text for SEO and accessibility.
@pooledge I tried that jquery lazy.
There are many features. It is very good.
However, the more DOM elements, the worse the performance will be.
Use it well.
@MrTamagotchi It's a very good idea.
@verlok Thanks for this! I think it would be a great addition. Images are always preferably but in certain circumstances background images definitely need to be used and would be super nice to lazyload them in and apply a loaded class for aesthetic purposes.
Although thinking about it... an img tag with object-fit:cover is probably just as supported where necessary these days (https://caniuse.com/#feat=object-fit) especially since IE11 came out 2013 and now Edge is preferred.
@richgcook that鈥檚 right!
There isn鈥檛 a real motivation to use a background image for content images in 2018.
And _real_ background images can be loaded lazily, but rarely you need to check when they鈥檙e loaded.
Anyway I鈥檓 thinking about developing an example of how to use LazyLoad鈥檚 callbacks to control the loading of the background images.
Anyway I鈥檓 thinking about developing an example of how to use LazyLoad鈥檚 callbacks to control the loading of the background images.
Did it! I've updated this demo, now it uses LazyLoad's callback_enter callback to create a DOM fragment containing a real image, then attaches the load event listener to it, in order to know then the image is loaded. It also adds and removes the loading class from the DOM element addressed by the background images.
Here's the code:
function logElementEvent(eventName, element) {
console.log(new Date().getTime(), eventName, element.getAttribute('data-src'));
}
function logEvent(eventName, elementsLeft) {
console.log(new Date().getTime(), eventName, elementsLeft + " images left");
}
function createImageFragment(srcUrl) {
var imageFragment = document.createElement('img');
imageFragment.setAttribute('src', srcUrl);
return imageFragment;
}
ll = new LazyLoad({
elements_selector: "a",
callback_enter: function (element) {
function callback_load(event) {
element.classList.remove('loading');
logElementEvent("LOADED", element);
imageFragment.removeEventListener('load', callback_load);
}
var imageFragment = createImageFragment(element.getAttribute('data-src'));
imageFragment.addEventListener('load', callback_load);
element.classList.add('loading');
logElementEvent("ENTERED", element);
},
callback_set: function (element) {
logElementEvent("SET", element);
},
callback_error: function(element) {
logElementEvent("ERROR", element);
element.src = "https://placeholdit.imgix.net/~text?txtsize=21&txt=Fallback%20image&w=220&h=280";
}
});
Thus, a simple lookalike to jquery-lazyload would be smth like that:
function createImageFragment(srcUrl) {
var imageFragment = document.createElement('img');
imageFragment.setAttribute('src', srcUrl);
return imageFragment;
}
function modifyClassUponLoad(element) {
element.classList.add('is-lazy-loaded');
}
new LazyLoad({
elements_selector: '.is-lazy',
callback_load: function(element) {
if (element.tagName === 'IMG') {
modifyClassUponLoad(element);
}
},
callback_enter: function(element) {
if (element.tagName !== 'IMG') {
function callback_load(event) {
imageFragment.removeEventListener('load', callback_load);
modifyClassUponLoad(element);
}
var imageFragment = createImageFragment(element.getAttribute('data-src'));
imageFragment.addEventListener('load', callback_load);
}
}
});
Thank you @verlok for your time on this!
Re-opening this one. Any update on why the loaded class is not added when a background-image has been loaded? Thank you!
@Danetag because it can鈥檛. The browser doesn鈥檛 throw any event when a background image is loaded.
@Danetag Just use img with object-fit. The support is pretty great https://caniuse.com/#feat=object-fit
@verlok I didn't look into the code to see exactly how an image gets loaded, but how do you handle the background-image loading exactly? I seems weird that no events would be triggered after an image is loaded...?
@richgcook Regarding the object-fit property, it's a working solution but it looks more like a workaround, it is not the optimal one in terms of semantic. I would like to avoid using an<img> tag when it's not supposed to be one.
Thank you again guys for your amazing work! :)
Why do you use background images instead of proper
imgs for your content images? Isn鈥檛 theimgbest for usability (users can click and save images) and accessibility (screen readers can treat the images like images, and real thealtcontent instead)?
As @verlok said earlier... I think img is always more optimal
That's exactly for this reason the background-image property exists. A background-image excludes the image from the rest of the DOM -- Don't get me wrong, I'm pro accessibility.
By design, a background-image is used because the image doesn't represent anything semantically, the image would generate noise in the DOM and for the screen readers.
When to use CSS background-image
- Images Purely Used to Design.
- No Relation With Content.
- Small Images which we can play with CSS3.
- Repeating Images ( In blog author icon , date icon will be repeated for each article etc.,).
@Danetag And because of such it doesn't throw an event so we can't tell if it's loaded.
But if you load the image using a new Image(), and at the end inject the background-image property... What would be the issue? (I don't know how you actually preload an image)
But if you load the image using a
new Image(), and at the end inject the background-image property... What would be the issue? (I don't know how you actually preload an image)
What image? There is no <img />... it's a background-image.
@Danetag the question is: why would you need to know _when_ a background image is loaded, if it's only a background image so it's not meaningful to the users? And if it's meaningful to the users, shouldn't it be a proper img?
Also, LazyLoad doesn't create any DOM fragment images to understand when images are loaded, it only listens for the loaded events of the original DOM elements.
This is because:
<iframe>, <video> and <audio> tagsbackground-positionIf you want, you can create your own workaround, as shown on this comment.
@verlok The promise of lazy loading is to preload an image visible in the user's viewport, whatever it's a background-image or an image... No? A background-image, even not meaningful, has still a file size to consider and could be worth preloading.
It seems to be more like a dev philosophy (listening only to events of the original DOM elements), which I could totally understand!
Thank you for your note and comment (it basically preloads an image using an new Image() instance by leveraging the Intersection Observer callbacks... Which is why I was asking why not having this approach baked in for the background-image. If you're open to it, I could work on a PR to propose a solution :) )
Thank you for you awesome work 馃挴
The promise of lazy loading is to preload an image visible in the user's viewport, whatever it's a background-image or an image... No? A background-image, even not meaningful, has still a file size to consider and could be worth preloading.
Correct! And Lazy Load does it, it just cannot notify you developer when it has finished doing it if the image is a background image. And the workaround is in the link I provided you. PS: Technically, lazy loading is the exact opposite of preloading, but I get what you mean.
It seems to be more like a dev philosophy (listening only to events of the original DOM elements), which I could totally understand!
It is, and it's not for the other 2 reasons I put in the bullet list.
If you're open to it, I could work on a PR to propose a solution :)
I'm always open to PRs, but keep in mind that LazyLoad must work also with other tags and with multiple background images. It won't be easy to make it work for all the cases keeping the script lean and small.
Thank you!
Thank you for taking the time to reply! 馃憤
Hey @Danetag,
things changed on version 15.
I found a way to manage background images's load/error event, so now background images are now getting the loaded or error class and calling callback_loaded and callback_error.
Take a look at the changelog and to the upgrading guide if you're interested.
Have a nice day.
Most helpful comment
Unfortunately there is no way of detecting if an image has been loaded if it's a
background-image, and I believe this is why lazyload works in the way it does in cases like this.I faced a similar problem and wrote a workaround. My solution involves placing an
imgas a direct child of the element I want thebackground-imageon, making theimgvisually hidden and listening for it to enter viewport, then setting thebackground-imageand a--loadedmodifier class on the parent.The code is much simpler than the explanation, haha:
Pretty easy to modify this concept to fit your needs. Hope it helps!