KevinBatdorf / liquidslider

A Responsive jQuery Content Slider
161 stars 63 forks source link

Load slides progressively? #101

Closed donalmc closed 10 years ago

donalmc commented 11 years ago

Is it at all possible to load the content of slides progressively? i.e. as they display?

I'm using the slider to display a lot of Vimeo movies and as a result, the page takes ages to load.

http://www.brightwork.com/resources/webcast-recordings.htm

Thanks

EDIT: I think what I am asking about is called lazy loading.

KevinBatdorf commented 11 years ago

Sorry for the late reply. Lots been going on. Anyway, you could try taking this approach:

http://kevinbatdorf.github.io/liquidslider/examples/page2.html#ajax

mark222 commented 10 years ago

I also need to load each slide as the user transitions into it, but the example as referenced does not do that - it responds to a click on something in the first slide to replace the first slide content. I need replace each slide's content as the user transitions through the slides using the normal navigation methods. The original HTML will just have dummy placeholder content. I cannot find an example that does that.

Something (I think) that runs on the "pretransition" event and loads the content of the nextPanel. I cannot find quite the right hook to replace the content of the next panel with dynamically loaded HTML. E.g. like this, but of course nextPanel is just a number... how do I get the content element?

pretransition: function() {
  this.nextPanel.html('<h3>Hello there.</h3>');
  this.transition();
},   

If you can tell me how to make the above work, I can fill in the code to load the HTML that is needed.... thanks!

KevinBatdorf commented 10 years ago

I think that $panelClass (poorly named) is the collection of panels. However, it doesn't include cloned panels from continuous.

this.$panelClass.eq(nextPanel).html('<h3>Hello there.</h3>');

I havent tested it out though, so let me know how it works.

mark222 commented 10 years ago

OK, $panelClass is the key to making it work. I also have to supply a preload() function with nearly identical code since pretransition() does not get called on the first panel. (Suggestion: Define a "preshow" function that gets called before showing a panel, even if there is no transition).

So if a panel contains any anchor link with a class of "lazyload" (see sample HTML below) the entire panel contents are loaded from the anchor's href location the first time the panel is shown (and never reloaded thereafter). The code to make that work is:

    pretransition: function() {
      // Lazy load entire nextPanel content if there is any lazyload link
      var $x = this.$panelClass.eq(this.nextPanel).find("a[class='lazyload']");
      if ($x.length > 0) {
          this.$panelClass.eq(this.nextPanel).load($x.eq(0).attr("href"));
      }
      this.transition();
    },    
    preload: function() {
      // Lazy load first panel if it contains any lazyload link 
      var $x = this.$panelClass.eq(0).find("a[class='lazyload']");
      if ($x.length > 0) {
          this.$panelClass.eq(0).load($x.eq(0).attr("href"));
      }

      this.finalize();
    }

The next problem was that the slider would not resize to fit the dynamically loaded content - some times. I think because the AJAX call is async, the content is not always loaded before the slider takes it's height measurement. I tried calling self.finalize() in the jQuery.load function callback, e.g:

          this.$panelClass.eq(this.nextPanel).load($x.eq(0).attr("href"), function() {
              self.finalize();
          });

but that seemed break things badly. So instead I had to add this before initializing the slider:

  $.ajaxSetup({
    async: false
  });

This is not good style; wish there was a way to do it in the AJAX function callback, but I could not figure it out.

The other change I made is to have the navigation tabs auto-number. I did not want to hard-code the tab names (or numbers) in the HTML; I want to add/remove panels easily without having to renumber all the adjacent panels. Because in my case there may be 15-20 panels, I wanted simple numbering, not descriptive titles. Borrowing from a trick someone else described, I added a panelTitleSelector argument, and a bit more code to the preload() function:

  $('#slider-id').liquidSlider({
    continuous:false,
    slideEaseFunction: "easeInOutCubic",
    hoverArrows: false,
    hideSideArrows: true,
    includeTitle: false,
    dynamicTabs: true,
    panelTitleSelector: ".title-num", 
    preload: function() {
      // ... lazy load ...

      // Auto-number the nav links      
      $.each(
        $((this.navigation)).find('a'),
        function(n) {
            $(this).text(n+1);
        }
      );
      this.finalize();
    }
  });

Now my HTML is dirt simple and I can insert/remove panels by simply adding/removing a single small block of code:

<body>
<div class="liquid-slider" id="slider-id">
     <div>
          <div class="title-num"></div> 
          <a href="introduction.html" class="lazyload"></a>
     </div>
     <div>
          <div class="title-num"></div>
          <a href="intro-supplement.html" class="lazyload"></a>          
     </div>
     <div>
          <div class="title-num"></div>
          <a href="project-overview.html" class="lazyload"></a>           
     </div>
</div>
</body>

The advantage of using anchor tags is that a search engine crawler will understand the HTML and properly deduce the structure of the site and index it appropriately.

Suggestion: Make auto-numbering of the navigation tabs a built-in feature.

Thanks for a great tool, there are dozens of jQuery based sliders out there, this is by far the best I found.

mark222 commented 10 years ago

PS, FWIW, here are some CSS mods that look better for a large set of numbered tabs:

.ls-wrapper .ls-nav a { /* Better look for numbered nav tabs */
  margin: 0px;
  padding: 0px 8px;  
  outline: 0px;
  border-width: 2px;
  border-style: solid;
  border-color: transparent;    
}
.ls-wrapper .ls-nav a:not(.current):hover {
  background: #d8d8d8;
  border-color:#000000;
}

This gives smaller more compact tabs, uses a border for hover highlight (and keep background the same until the tab is selected), and avoids highlighting the already-current tab. Loos like this:

slidernumbertabs

I also wanted to conserve space, so I have the arrows overlay the content... don't recall what I did to get that effect, but it was intentional :-).

mark222 commented 10 years ago

Fixed the requirement to use synchronous AJAX calls (a lot of clunky load event handling, but it seems to work in IE and Firefox correctly). Also consolidated common event code to clean it up (is there a better place for the 2 utility methods other than the global javascript name space?):

$('#slider-id').liquidSlider({
  continuous:false,
  slideEaseFunction: "easeInOutCubic",
  hoverArrows: false,
  hideSideArrows: true,
  includeTitle: false,
  dynamicTabs: true,
  panelTitleSelector: ".title-num", 

  pretransition: function() {
    lazyLoadSliderPanel(this, this.nextPanel);
    this.transition();
  },

  preload: function() {
    // Lazy load first panel if it contains any lazyload link 
    lazyLoadSliderPanel(this, 0);

    // Auto-number the nav links      
    $.each(
      $((this.navigation)).find('a'),
      function(n) {
          $(this).text(n+1);
      }
    );

    this.finalize();
  }
});

function lazyLoadSliderPanel(sliderSelf, panelNum) {
  // If given panel has a lazyload anchor tag, load the href into the panel
  var $anchors = sliderSelf.$panelClass.eq(panelNum).find("a[class='lazyload']");
  if ($anchors.length > 0) {
      sliderSelf.$panelClass.eq(panelNum).load($anchors.eq(0).attr("href"), function() {
          resizeSliderOnLoad(sliderSelf, sliderSelf.$panelClass.eq(panelNum));
      });
  }
}  

function resizeSliderOnLoad(sliderSelf, panel) {
  // Adjust height on current content
  sliderSelf.adjustHeight(true, sliderSelf.getHeight());

  // For any variable sized elements in the panel that may change
  // height when they are loaded, adjust after each one loads.
  panel.find('img, .varsize').load(function() { 
    sliderSelf.adjustHeight(true, sliderSelf.getHeight());
  });
}
KevinBatdorf commented 10 years ago

Yeah, I was going to suggest the adjustHeight() function, but you found it already.

If you don't want to include the <div class="title-num"></div> in your markup, you could add it in with javascript before initializing the slider.

Something like...

$('#slider-id').children().each(function(index) { 
     $(this).append('<div class="title-num">' + index + '</div> ')
});
$('#slider-id').liquidSlider({ //etc...

I didn't test that code yet, but you get the idea.