Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BgSet Can't use multiple backgrounds and there is no resize update event #827

Closed
davejtoews opened this issue Oct 2, 2020 · 7 comments
Closed

Comments

@davejtoews
Copy link

The css background-image property supports a comma separated list of images, but there's no way to make use of lazy sizes along with multiple backgrounds.

In my particular use case I'm pairing an image with a linear gradient, eg background-image: url(foo.jpg), linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.4)). I would like to be able to lazy load and swap out the first part, while maintaining the second.

I'm not sure this particular use case warrants a brand new syntax to be able to pass multiple values however I would like to be able to append the extra background after lazy sizes does it's thing. I can add a 'lazyloaded' event listener to capture the first image load after page load, however when the image is swapped on resize the work here is undone.

A possible solution to this would be to have a 'resizeImageUpdate` event or something of the like, however nothing of the sort currently exists.

I solved my problem with a MutationObserver, but I think there ought to be a better way. Surely an update event would have other potential uses beyond my niche use case.

My solution below where the data-additional-bg attribute on the element holds the rest of the background-image string:

const AdditionalBackgrounds = {
    init: function () {

        const bgElements = document.querySelectorAll('[data-additional-bg]');

        const observer = new MutationObserver((mutations) => {
            mutations.forEach((mutation) => {
                if (mutation.type == "attributes") {
                    this.appendBg(mutation.target);
                }
            });
        });

        bgElements.forEach((element) => {
            this.appendBg(element);
            element.addEventListener('lazyloaded', (evt) => {
                this.appendBg(evt.target);
            });
            observer.observe(element, {
                attributes: true //configure it to listen to attribute changes
            });
        });
    },
    appendBg: function (element) {
        const bg = window.getComputedStyle(element).getPropertyValue('background-image');
        const additionalBg = element.dataset.additionalBg;

        if (!bg.includes('linear-gradient')) {
            element.style.backgroundImage = bg + additionalBg;
        }
    }
};
@aFarkas
Copy link
Owner

aFarkas commented Oct 11, 2020

I haven`t fully understand your use case, but maybe the following change could help:

We currently dispatch the following event:

var event = lazySizes.fire(elem, 'bgsetproxy', {
	src: bg,
	useSrc: regBgUrlEscape.test(bg) ? JSON.stringify(bg) : bg,
});

if(!event.defaultPrevented){
	elem.style.backgroundImage = 'url(' + event.detail.useSrc + ')';
}

Would the following change help?

var event = lazySizes.fire(elem, 'bgsetproxy', {
	src: bg,
	useSrc: regBgUrlEscape.test(bg) ? JSON.stringify(bg) : bg,
});

if(!event.defaultPrevented){
	elem.style.backgroundImage = event.detail.fullSrc || 'url(' + event.detail.useSrc + ')';
}

With this change you could write something like this:

document.addEventListener('bgsetproxy', ({target, detail}) => {
	const additionalBg = targettarget.dataset.additionalBg;
	if (additionalBg) {
		detail.fullSrc = `url(${detail.useSrc}) ${additionalBg}`;
	}
});

From current code you could do something (less/still) ugly like this:

const wait = Promise.resolve();
document.addEventListener('bgsetproxy', async ({target}) => {
	const additionalBg = target.dataset.additionalBg;
	if (!additionalBg) {
		return;
	}
	await wait;
	target.style.backgroundImage += additionalBg;
});

At the end I haven't understood your use case and did no test my code above. But I can say, that I have written bgset to enable responsive images in combination with background-size: cover|contain. This use case is deprecated and should always be solved with object-fit and its polyfills. I really hate the invention of this plugin ;-).

@davejtoews
Copy link
Author

So perhaps what's missing from describing my use case is that my CSS includes background-blend-mode: multiply; for the element in question. I'm using the multiple background images to achieve front-end modification of the image.

There are a lot of neat tricks you can achieve with background images that don't have direct parallels that can be used with <img> tags.

In addition to situations in which backgrounds can be used for things that images can't, I believe background images still have a place even though object-fit makes<img> takes more useful. If an image is a key part of the content, semantically it should be an <img> tag, but sometimes an image is just decoration, an enhancement to an element with other semantic purposes. In that case a css background image still makes sense.

I really appreciate this plugin because I can make those background images responsive, even though I may use object-fit elsewhere in the same project where it makes sense. I do sympathize if this particular plugin is a pain to maintain though.

I haven't tried your suggested fix yet, but it looks promising. Perhaps the bgsetproxy event should be in the docs. Apparently I found it once before as I just realized I opened a very similar ticket almost two years ago and solved the same problem in a completely different way: #585

@aFarkas
Copy link
Owner

aFarkas commented Oct 17, 2020

Your explanation makes totally sense. For me of course interesting is wether the code change outlined in my previous post would make any sense to you? I'm willing to change the code to something like this. And yeah I will then also document bgsetproxy;-).

@aFarkas
Copy link
Owner

aFarkas commented Oct 24, 2020

I just added the fullSrc change to the following branch: https://github.com/aFarkas/lazysizes/tree/feature/bgset/fullsrc-bgsetproxy

It would be nice if you could test the change and confirm that this solves your issue.

@davejtoews
Copy link
Author

Thanks for this, @aFarkas . Hoping I have an opportunity to check this out later this week, or perhaps next week.

@davejtoews
Copy link
Author

This is working for me @aFarkas. Thank you!

aFarkas added a commit that referenced this issue Dec 16, 2020
BGSet: Add `fullSrc` prop to the `bgsetproxy` event (fixes #827)
@solhuang2201
Copy link

This is not so easy to use, I tried to understand the discussion between the two gurus, and documented it.

First, use lazysizes/plugins/bgset/ls.bgset.js before lazysizes.min.js

Modify ls.bgset.js to set the dataset you want to use:

	var proxyLoad = function(e){
		if(!e.target._lazybgset){return;}

		var image = e.target;
		var elem = image._lazybgset;
		var bg = image.currentSrc || image.src;
		var prefix = elem.getAttribute('data-bgprefix'); // Gets prefix string


		if(bg){
			var useSrc = regBgUrlEscape.test(bg) ? JSON.stringify(bg) : bg;
			var event = lazySizes.fire(elem, 'bgsetproxy', {
				src: bg,
				useSrc: useSrc,
				fullSrc: prefix, // Set it
			});
			if(!event.defaultPrevented){
				elem.style.backgroundImage = event.detail.fullSrc || 'url(' + event.detail.useSrc + ')';
			}
		}
	...

Then go back to the html setting data-bgset to preload the image background, then use the custom dataset to set the multiple image settings

Ex:

<div class="d-md-block lazyload" data-bgset="./images.jpg [(min-width: 768px)]" data-bgprefix="url(./images.jpg), linear-gradient(to bottom, #efaf04 0%, #ff9c39 100%)"></div>

Is mean in a browser min-with 768px, the image preload will be executed and the style style="background-image: url(". /images.jpg"), linear-gradient(rgb(239, 175, 4) 0%, rgb(255, 156, 57) 100%);"

I don't know if I'm using fullSrc correctly, but it's executing the preload results I want correctly, and exceeding my expectations, kudos lazysizes!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants