Lazy-load a javascript script?

Viewed 3210

Is there a way I can wrap an external JS script embed with lazy-load behavior to only execute when the embed is in the viewport?

Context: I have an external javascript embed that when run, generates an iframe with a scheduling widget. Works pretty well, except that when the script executes, it steals focus and scrolls you down to the widget when it’s done executing. The vendor has been looking at a fix for a couple weeks, but it’s messing up my pages. I otherwise like the vendor.

Javascript embed call:

<a href=https://10to8.com/book/zgdmlguizqqyrsxvzo/ id="TTE-871dab0c-4011-4293-bee3-7aabab857cfd" target="_blank">See
    Online Booking Page</a>
<script src=https://d3saea0ftg7bjt.cloudfront.net/embed/js/embed.min.js> </script> <script>
    window.TTE.init({
        targetDivId: "TTE-871dab0c-4011-4293-bee3-7aabab857cfd",
        uuid: "871dab0c-4011-4293-bee3-7aabab857cfd",
        service: 1158717
    });
</script>

While I'm waiting for the vendor to fix their js, I wondered if lazy-loading the JS embed may practically eliminate the poor user experience. Warning: I'm a JS/webdev noob, so probably can't do anything complicated. A timer-based workaround is not ideal because users may still be looking at other parts of the page when the timer runs out. Here are the things I’ve tried and what happens:

I tried: What happened:
Add async to one or both of the script declarations above Either only shows the link or keeps stealing focus.
Adding type=”module” to one or both script declarations above Only rendered the link.
Wrapping the above code in an iframe with the appropriate lazy-loading tags When I tried, it rendered a blank space.

Also, I realize it's basically the same question as this, but it didn't get any workable answers.

2 Answers

I actually also speak french but I'll reply in english for everybody.

Your question was quite interesting because I also wanted to try out some lazy loading so I had a play on Codepen with your example (using your booking id).

I used the appear.js library because I didn't really want to spend time trying some other APIs (perhaps lighter so to take in consideration).

The main JS part I wrote is like this:

// The code to init the appear.js lib and add our logic for the booking links.
(function(){
  // Perhaps these constants could be put in the generated HTML. I don't really know
  // where they come from but they seem to be related to an account.
  const VENDOR_LIB_SRC = "https://d3saea0ftg7bjt.cloudfront.net/embed/js/embed.min.js";
  const UUID = "871dab0c-4011-4293-bee3-7aabab857cfd";
  const SERVICE = 1158717;
  
  let vendorLibLoaded = false; // Just to avoid loading several times the vendor's lib.

  appear({
    elements: function() {
      return document.querySelectorAll('a.booking-link');
    },
    appear: function(bookingLink) {
      console.log('booking link is visible', bookingLink);
      
      /**
       * A function which we'll be able to execute once the vendor's
       * script has been loaded or later when we see other booking links
       * in the page.
       */
      function initBookingLink(bookingLink) {
        window.TTE.init({
          targetDivId: bookingLink.getAttribute('id'),
          uuid: UUID,
          service: SERVICE
        });
      }
      
      if (!vendorLibLoaded) {
        // Load the vendor's JS and once it's loaded then init the link.
        let script = document.createElement('script');
        script.onload = function() {
          vendorLibLoaded = true;
          initBookingLink(bookingLink);
        };
        script.src = VENDOR_LIB_SRC;
        document.head.appendChild(script);
      } else {
        initBookingLink(bookingLink);
      }
    },
    
    reappear: false
  });
})();

I let you try my codepen here: https://codepen.io/patacra/pen/gOmaKev?editors=1111

Tell me when to delete it if it contains sensitive data!

Kind regards,

Patrick

This method will Lazy Load HTML Elements only when it is visible to User, If the Element is not scrolled into viewport it will not be loaded, it works like Lazy Loading an Image.

Add LazyHTML script to Head.

<script async src="https://cdn.jsdelivr.net/npm/lazyhtml@1.0.0/dist/lazyhtml.min.js" crossorigin="anonymous" debug></script>

Wrap Element in LazyHTML Wrapper.

<div class="lazyhtml" data-lazyhtml onvisible>
<script type="text/lazyhtml">
      <!--
       <a href=https://10to8.com/book/zgdmlguizqqyrsxvzo/ id="TTE-871dab0c-4011-4293-bee3-7aabab857cfd" target="_blank">See
      Online Booking Page</a>
      <script src=https://d3saea0ftg7bjt.cloudfront.net/embed/js/embed.min.js>
   </script> 
   <script>
      window.TTE.init({
      targetDivId: "TTE-871dab0c-4011-4293-bee3-7aabab857cfd",
      uuid: "871dab0c-4011-4293-bee3-7aabab857cfd",
      service: 1158717
      });
   </script>
   -->
</script>
</div>
Related