Roots Discourse

How to Use Lozad.js in Sage


#1

Originally published at: https://roots.io/guides/how-to-use-lozad-js-in-sage/

Lozad.js is a lightweight lazy-loading library that’s just 535 bytes minified & gzipped. It is written with an aim to lazy load images, iframes, ads, videos or any other element using the recently added Intersection Observer API with tremendous performance benefits. Install yarn add lozad Import and add JS Open resources/assets/scripts/routes/common.js and add the import…


#2

Excellent - thank you.


#3

Thanks. If you’re also using the srcset attribute your image tag will look like this:

<img class="lozad" data-src="path-to-src.jpg" data-srcset="..." />

#4

Hi,

A few days ago I decided to use lodaz in a wordpress site i’m developping, and while I was asking myself what could make a great placeholder before images load, I tought it would be nice to leverage wordpress thumbnails feature for that.

The idea is to register a low resolution thumbnail size, in my case 64x48 (around 1~2kb per image), and display a blurred version of it before lodaz does its job and replace it with full quality image version. Then fade the blur out. It would make a kind of Medium-style lazy load.

The final result looks like this:

So first register the low res thumbnail size:

add_image_size('low-res', 64, 48, false);

Then, I use a regex filter function to replace original image codes:

function lazy_images($content)
{
    //-- Change src/srcset to data attributes, and replace img src with its low res version
    $content = preg_replace_callback(
        '/<img(.*?)(src=)\"(.*?)\"(.*?)(srcset=)\"(.*?)\"(.*?)>/i',
        function ($matches) {
            return "<img$matches[1]$matches[2]" .low_res_image_src($matches[3]) . " " . "data-$matches[2]$matches[3]$matches[4]data-$matches[5]\"$matches[6]\"$matches[7]>";
        },
        $content
    );
    // wrap images in a .lazy-wrap div for clean and unblurred borders. Transfer alignment class to wrapping div
    $content = preg_replace('/(<img|<iframe)(.*?)(align\w*)([^>]*>)/i', '<div class="lazy-wrap $3">$1$2$4</div>', $content);

    //-- Add .lazy class to each image that already has a class.
    $content = preg_replace('/(<img|<iframe)(.*?)class=\"(.*?)\"(.*?)>/i', '$1$2class="$3 lazy"$4>', $content);

    //-- Add .lazy class to each image that doesn't already have a class.
    $content = preg_replace('/(<img|<iframe)((?:(?!class=).)*?)>/i', '$1 class="lazy"$2>', $content);
    return $content;
}

This filter is applied to content, widgets, and thumbnails:

add_filter('the_content', 'lazy_images');
add_filter('widget_text', 'lazy_images');
add_filter('post_thumbnail_html', 'lazy_images'); 

What we need next is the low_res_image_src function that will return the image low res version url:

function low_res_image_src($image_url)
{
    //Call the get_attachment_id that will return the id based on image url. Here a slight adjustment may be required to get rid of domain in url
    $iid = get_attachment_id(WP_HOME . $image_url);
    return wp_get_attachment_image_src($iid, 'low-res')[0];
}

function get_attachment_id($url)
{
    $attachment_id = 0;

    $dir = wp_upload_dir();

    if (false !== strpos($url, $dir['baseurl'] . '/')) { // Is URL in uploads directory?
        $file = basename($url);

        $query_args = array(
            'post_type'   => 'attachment',
            'post_status' => 'inherit',
            'fields'      => 'ids',
            'meta_query'  => array(
                array(
                    'value'   => $file,
                    'compare' => 'LIKE',
                    'key'     => '_wp_attachment_metadata',
                ),
            )
        );

        $query = new WP_Query($query_args);

        if ($query->have_posts()) {
            foreach ($query->posts as $post_id) {
                $meta = wp_get_attachment_metadata($post_id);

                $original_file       = basename($meta['file']);
                $cropped_image_files = wp_list_pluck($meta['sizes'], 'file');

                if ($original_file === $file || in_array($file, $cropped_image_files)) {
                    $attachment_id = $post_id;
                    break;
                }
            }
        }
    }

    return $attachment_id;
}

Add the required CSS (you might want to inline it in your HTML):

.lazy-wrap {
  overflow: hidden;
  width: fit-content;
}

img.lazy {
  transition: filter 0.3s ease;
  filter: blur(10px);
  //we could also add "transform : scale(1.1)" to avoid white borders around blurred image
}

img.lazy.is-loaded {
  filter: blur(0);
}

And finally, we can initialize our lodaz script:

 lozad(".lazy", {
         rootMargin: "500px 0px",
         loaded: function (el) {
         el.classList.add("is-loaded");
     }
 }).observe();

#5

Should this be modified to work for srcset attributes output by wp_get_attachment_image?

Edit, like:

    if ($attr['src']) {
        $attr['data-src'] = $attr['src'];
        unset($attr['src']);
    }

    if ($attr['srcset']) {
        $attr['data-srcset'] = $attr['srcset'];
        unset($attr['srcset']);
    }

#6

Did someone already found a solution to filter the output from Gutenberg?
The filter wp_get_attachment_image_attributes does not seem to work here.