jQuery Possible to Force a CSS background image to be within element?

Let me clarify:

I have a site I’m optimizing that was built with a visual builder where you can add background images to elements. However, the builder doesn’t encode the background image within the HTML. Instead, it’s written as CSS. In order for me to take advantage of making the background images lazyload, I’m wondering if there is an automated script via jQuery that would scan the element, find the assigned background image and then post the image within the element as a data-src. That way I can set it up to be lazyloaded.

Or is this beyond jQuery’s reach?

Thank you.

I only code in Vanilla JavaScript, but I’m 99.9 percent certain in can be done with jQuery. It’s easy to do in Vanilla JavaScript → The following is lazy loading in canvas.

// Function to fetch and load image
const fetchAndLoadImage = async (imagePath) => {
    try {
        let response = await fetch(imagePath);
        let blob = await response.blob();
        let img = new Image();
        img.src = URL.createObjectURL(blob);
        return img;
    } catch (error) {
        console.error(error);
    }
};

I’m sure someone who is more verse in jQuery can help you better, but it isn’t beyond jQuery.

1 Like

Hi @toad78, not sure about jQuery but you can get the background image property with vanilla JS using getComputedStyle()… no need for a manual implementation with data-src or the like any more BTW, you can just set an image to loading="lazy";

const elements = document.querySelectorAll('.css-image')

elements.forEach(element => {
  const value = window
    .getComputedStyle(element)
    .getPropertyValue('background-image')

  const image = document.createElement('img')
  
  image.src = value.replace(/url\(|'|"|\)/g, '')
  image.loading = 'lazy'

  element.style.backgroundImage = 'none'
  element.appendChild(image)
})

Here’s a pen:.

Note that the CSS background image might still get loaded before the JS is getting executed, but you might set it to display: none with CSS to prevent this I suppose, and then set it back to display: block in the JS.

3 Likes

Isn’t changing a background image to a foreground image going to present significant layout problems especially if the image is repeated or stretched and has other content on top of it?

If its only a few big images that are not overlaid with content then I guess you would be ok.

It might be better instead use the intersection observer to load the background image when it gets close to the viewport. In that way you don’t have t change the html.

There’s an article here that explains it better than me :slight_smile:

2 Likes

I had to give it a go Paul.

document.addEventListener("DOMContentLoaded", () => {
  
  /**
   * A more generic handleIntersection function
   * Handles the intersection of observed elements and triggers a callback function when an observed element becomes intersecting.
   * @param {Function} callback - The callback function to be triggered when an observed element becomes intersecting.
   * @returns {Function} - A function that takes in entries and observer parameters and maps through the entries to check if they are intersecting.
   * If an entry is intersecting, it calls the callback function with the entry and observer as arguments.
   */
  const handleIntersection = (callback) => (entries, observer) => {
    entries.map((entry) => {
      if (entry.isIntersecting) {
        callback(entry, observer);
      }
    });
  };

  /**
   * Adds a background image to the given element and makes it visible.
   * @param {IntersectionObserverEntry} entry - The entry object for the observed element.
   * @param {IntersectionObserver} observer - The observer instance.
   * @returns None
   */
  const addBackgroundImage = (entry, observer) => {
    const bgElement = entry.target;

    bgElement.style.backgroundImage = `url('${bgElement.dataset.bgImage}')`;
    // fade in to better illustrate the intersection being handled
    // note: Can also check Network tab -> click on Img
    bgElement.classList.add('visible');
    observer.unobserve(bgElement);
  };

  const bgImages = document.querySelectorAll(".bg-image-container");
  const observer = new IntersectionObserver(
    // pass in addBackgroundImage as the callback function
    handleIntersection(addBackgroundImage),
    { rootMargin: "100px" }
  );

  bgImages.forEach((bgImage) => observer.observe(bgImage));
});
1 Like

It’s not changing the image to a foreground image, it’s posting the image as a background image within the element instead of using CSS. That’s what I need done in order for me to set the image to lazyload. The builder being used uses CSS wheras I need the ability to convert it to be within the element. I’d rather find a method of doing this rather than recoding everything just so I can have the background image lazyload.

Sorry that does not compute in my brain:)

It sounds like you already have a background image (or images) on various elements and you want to remove the background-image property from those elements but instead add the url to those images into a data-src attribute.

I guess you then will write some js that after a time will grab the data-src and inject it inline into the elements inline style.

That seems like a very tricky task to me so I’ll leave you in the hands of the JS experts :slight_smile:

Thank you, PaulOB.

1 Like

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.