Search code examples
javascriptjqueryruby-on-railstwitter-bootstrapoembed

Lazy load content of slides inside carousel (content is oEmbeds/iframes)


I am loading in ajax a modal/popin with a Bootstrap carousel in which each slide is (not an image as in many question about lazy loading) but an iframe from oEmbed from various social networks (facebook, instagram, twitter, ...).

The issue is that when I click the button that laods the modal, ALL the slides content get loaded, that is to say 15 to 20 oembeds(each of them loading content text, image and javascript...).

I would like to be clever about it and only "lazy load" slide by slide or even smarter 3 slides by 3 slides.

I am just also mentioning for the sake of information that I am using scrollMonitor and Hubspot Messenger. But i'd rather use Bootstrap slide events to trigger the apparition/load of each slide or any suggestion you would have.

I'm using ruby on rails as back end language

The url of the oEmbed programmatically change as they are inputed from a Admin Backoffice and change on each Article/page but you'll find below an example :

Page.html

//button to click to make modal appear
<a class="btn btn-primary" onclick="loadModal()" id="socialStoryModal">
      load modal
</a>

Load Modal with Hubspot Messenger in page.js

function loadModal() {  
      var msg;
      msg = Messenger().post({
        message:  'modal.html.erb',/see below the carousel
        showCloseButton: true,
        hideAfter: false
      }); 
    }

modal.html.erb = Modal with the carousel and the social embeds (here can be up to 50 of them)

<div id="embeds-carousel" class="carousel slide" data-ride="carousel" data-interval="false">

    <div class="carousel-inner" role="listbox">    

         <div class="item active" id="item1" >
    <script>
var url = « https://api.instagram.com/oembed?url=https://www.instagram.com/p/5456544654565/";
embed_request = {
      url: url,
      dataType: "jsonp",
      cache: false,
      success: function (data) {
        try {
          var embed_html = data.html;
          $( "div#item1").html(embed_html);
        } catch (err) {
          console.log(err);
        }
      }
    }; 
$.ajax(embed_request);
</script>
</div>

  <div class="item" id="item2" >
    <script>
var url = "https://publish.twitter.com/oembed?url=https://twitter.com/coca/status/546664465342324"; 

embed_request = {
      url: url,
      dataType: "jsonp",
      cache: false,
      success: function (data) {
        try {
           var embed_html = data.html;
          $( "div#item2").html(embed_html);
        } catch (err) {
          console.log(err);
        }
      }
    };  
$.ajax(embed_request);
</script>
</div>

<div class="item" id="item3" >
    <script>
var url = "https://publish.twitter.com/oembed?url=https://twitter.com/muse/status/65353453F"; 

embed_request = {
      url: url,
      dataType: "jsonp",
      cache: false,
      success: function (data) {
        try {
           var embed_html = data.html;
          $( "div#item3").html(embed_html);
        } catch (err) {
          console.log(err);
        }
      }
    }; 
$.ajax(embed_request);
</script>
</div>


<div class="item" id="item4" >
    <script>
var url = « https://api.instagram.com/oembed?url=https://www.instagram.com/p/cftzezeker5/";
embed_request = {
      url: url,
      dataType: "jsonp",
      cache: false,
      success: function (data) {
        try {
          var embed_html = data.html;
          $( "div#item4").html(embed_html);
        } catch (err) {
          console.log(err);
        }
      }
    };  
 $.ajax(embed_request);
 </script>
</div>

<div class="item" id="item5" >
    <script>
var url = « var url = "https://www.facebook.com/plugins/post/oembed.json/?url=https://www.facebook.com/cocacola/posts/fdgyez556Yds";
";
embed_request = {
      url: url,
      dataType: "jsonp",
      cache: false,
      success: function (data) {
        try {
          var embed_html = data.html;
          $( "div#item5").html(embed_html);
        } catch (err) {
          console.log(err);
        }
      }
    };  
 $.ajax(embed_request);
</script>
</div>

and so on…
 </div>
  <!-- Controls -->
  <a class="left carousel-control" href="#embeds-carousel" role="button" data-slide="prev">
    <i class="fa chevron fa-chevron-left "></i>
    <span class="sr-only">Previous</span>
  </a>
  <a class="right carousel-control" href="#embeds-carousel" role="button" data-slide="next">
    <i class="fa chevron fa-chevron-right "></i>
    <span class="sr-only">Next</span>
  </a>


</div>

I've seen tons of libraries to lazy load images and even sometimes scripts/iframes but they all need to have directly add certain classes I n the block, which is of no help for me as I use oembed above and I have nowhere to put these lazy classes.

I need to make it work with these oEmbeds iframes.


Solution

  • To lazy load the embeds you need to add them to an array rather than rendering them in the DOM, and once all of them have been loaded (successfully or not) then you can use that array to only render in the DOM the current slide.

    Here's an example:

    var oEmbedUrls = [
      'https://api.instagram.com/oembed?url=https://www.instagram.com/p/5456544654565/',
      'https://publish.twitter.com/oembed?url=https://twitter.com/coca/status/546664465342324',
      'https://publish.twitter.com/oembed?url=https://twitter.com/muse/status/65353453F',
      'https://api.instagram.com/oembed?url=https://www.instagram.com/p/cftzezeker5/',
      'https://www.facebook.com/plugins/post/oembed.json/?url=https://www.facebook.com/cocacola/posts/fdgyez556Yds'
    ];
    
    var oEmbedsHtml = [];
    var doneCount = 0;
    var currentSlideIndex;
    
    $.each(oEmbedUrls, function(i, url){
        $.ajax({
          url: url,
          dataType: "jsonp",
          cache: false,
          success: function (data) {
            var itemIndex = oEmbedsHtml.length;
            oEmbedsHtml.push(data.html);
            $('#embeds-carousel .carousel-inner').append('<div class="item" id="item' + itemIndex + '"></div>');
            oEmbedUrlResponded();
          },
          error: function(){
            console.warn('oEmbed URL could not be loaded: ', url);
            oEmbedUrlResponded();
          }
        }
    });
    
    function renderEmbedSlide(index){
      currentSlideIndex = index;
      $('#item' + index).html(oEmbedsHtml[index]);
    }
    
    function oEmbedUrlResponded(){
      doneCount ++;
    
      // Check if all the URLs have been loaded (successfully or not) when we can now render the carousel and the first slide as well
      if(doneCount == urls.length){
        renderEmbedSlide(0); // Render the first embed
        /*** CALL HERE THE CODE TO ACTIVATE THE CAROUSEL COMPONENT ***/
      }
    }
    

    Finally, you'lll need to add a hook to your carousel component so that renderEmbedSlide(index) is called whenever the slide is changed by the carousel, being sure that the index (zero-based) of the new slide is passed as a parameter to the function.

    You may also need to add a default min-width and min-height to the .carousel-inner .item CSS class to allow the carousel component know the dimensions beforehand, as the slides (embeds) will be lazy loaded thereafter.