Vanilla-lazyload: Feature request: lazily execute script block + `auto_unobserve` fix

Created on 6 May 2020  路  13Comments  路  Source: verlok/vanilla-lazyload

I'm looking to remove our dependency on jQuery.

We currently use Lazy Load XT and rely on its Lazy Widget feature to run script blocks and trigger css animations when divs with data-lazy-widget enter the viewport.

Would you be able to add such a feature?

For scripts it executes code in comments that match data-lazy-widget value.

For css animations I look for a class that's added to the div when lazy loading has occurred.

Question

All 13 comments

Hello @LeaTark ,
thank you for opening this.

I understood what you're doing and what you're asking, and I think you can already do that using the current version of LazyLoad.
If you can comment reporting a piece of code that you would like to execute when an element enters the viewport, I could give you a very specific advice.

Here's an example where social media widget scripts aren't loaded until their placeholders enter the viewport.

A placeholder div

<div data-lazy-widget="lazy_e21e9fc5-e10b-4f58-acdc-8fb8e7018cd3_PLLIbdjyY6pjZ5IXI51WWYB">

...

Then some script that ececutes when it comes into the viewport, immediately if it's in viewport on page load.

<div id="lazy_e21e9fc5-e10b-4f58-acdc-8fb8e7018cd3_PLLIbdjyY6pjZ5IXI51WWYB">
<!--
    <script>
        // Check for social media cookie acceptance
        if (isCookieAccepted('social')) {
            // Add now.
            window['addYoutube_e21e9fc5-e10b-4f58-acdc-8fb8e7018cd3_PLLIbdjyY6pjZ5IXI51WWYB']();
        } else {
            // Queue for later
            window.ccQ = window.ccQ || [];
            window.ccQ['social'] = window.ccQ['social'] || [];
            window.ccQ['social'].push(window['addYoutube_e21e9fc5-e10b-4f58-acdc-8fb8e7018cd3_PLLIbdjyY6pjZ5IXI51WWYB']);
        }
    </script>
-->
</div>

Hey @LeaTark ,
thank you for your patience.

You can easily to that (and do it better) with this LazyLoad library!

In your HTML, you could separate the "lazy" word from the YouTube ID.

<div data-youtube-id="e21e9fc5-e10b-4f58-acdc-8fb8e7018cd3_PLLIbdjyY6pjZ5IXI51WWYB" class="lazy-js"></div>

Then in your JS you can use lazyload to select all the elements with the lazy-js class, so when they enter the viewport, a function called addYouTube_ + the YouTube ID is called. Like this.

var lazyScripts = new LazyLoad({
  elements_selector: ".lazy-js",
  callback_enter: function (lazyJsElement) {
    // Get the youtube ID from the lazy JS element that entered the viewport
    var youtubeId = lazyJsElement.getAttribute("data-youtube-id");
    // Check for social media cookie acceptance
    if (isCookieAccepted("social")) {
      // Add now.
      window["addYoutube_" + youtubeId]();
    } else {
      // Queue for later
      window.ccQ = window.ccQ || [];
      window.ccQ["social"] = window.ccQ["social"] || [];
      window.ccQ["social"].push(window["addYoutube_" + youtubeId]);
    }
  }
});

To make and show you this this works, I've created this Pen.

Let me know if this solved your issue!

By the way, if you really want to improve your code, instead of having multiple function named each one addYoutube_ + the Youtube ID, like your current addYoutube_e21e9fc5-e10b-4f58-acdc-8fb8e7018cd3_PLLIbdjyY6pjZ5IXI51WWYB, you could have a single addYoutube function and pass the ID as a parameter.

I will add a recipe to lazily execute Javascript in the README. Thanks for your inspiration.

That is interesting, thank you.
My one concern would be that the callback applies this same function to all .lazy-js so it can't be used to run different scripts for each div without also instantiating lazyload for each. Is that right?

Am I right in thinking, though, that I could do it with a single instance of lazyload monitoraing .lazy-js and extract a function name from a data value on the div tag? Although I'm unclear how much this would impact the primary objective of minimising the amount of JavaScript that has to be evaluated prior to the div entering viewport.

Thanks also for the suggestion about YouTube ID's. I agree that it would be better to pass the ID as a parameter but each addYoutube_ also contains the player's configuration settings and the GUIDs change with each page load.

I needed it to be more generic and reusable so this is what I ended up with.

<!-- Placeholder div -->
<div data-lazy-js="myFunc"></div>

<!-- Lazy JavaScript -->
<script>
    window['myFunc'] = function () {
        alert('Lazy Test');
    }
</script>



<script src="/js/vanilla-lazyload/15.2.0/lazyload.min.js"></script>
<script>
    var lazyScripts = new LazyLoad({
        elements_selector: "[data-lazy-js]",
        threshold: 0,
        callback_enter: function (el) {
            var f = el.getAttribute("data-lazy-js");
            if (typeof window[f] == 'function') {
                try {
                    window[f]();
                }
                catch (ex) {
                    // Handle Exception
                }
            }
        }
    });
</script>

Still tweaking.

Added
el.classList.add('loaded');

How do I call removeEventListeners so each lazy script is only executed on its first entry into the viewport?

@LeaTark you're right, the auto_unobserve option (default: true) should do that, but it's not working in the current version. I'll fix that.

I鈥檓 working on it on a branch I鈥檝e recently pushed: feature/unobserveFix. I鈥檒l release the update in the next days under a new major version (16.0.0).

Check out version 16!

You should solve that by using the brand new unobserve_entered option.
I've also created a new recipe and a new demo to call lazy functions.

See the changelog for more details, and the upgrade guide if you need to upgrade from a previous version.

Absolutely fantastic! It is my pleasure to buy you a coffee.

I'm glad you solved, and thank you, @LeaTark !

Was this page helpful?
0 / 5 - 0 ratings