jQuery Tools Scrollable – Stop Scrolling Past the End

jQuery Tools is an amazing library, and the Scrollable control is extremely useful. However, it doesn’t work well by default for situations where you want to display more than one item in your list at a time, and scroll through them one by one (instead of five at a time such as in this example). It took a bit of time for me to figure this out, so I thought I’d share it with everyone. Credit goes to neogisme and soon7 from this post for getting me half-way there.

Simply change your Scrollable initiation Javascript to include the following code:

jQuery(function() {

  // Initialize the Scrollable control
  jQuery(".scrollable").scrollable();

  // Get the Scrollable control
  var scrollable = jQuery(".scrollable").data("scrollable");

  // Set to the number of visible items
  var size = 3;

  // Handle the Scrollable control's onSeek event
  scrollable.onSeek(function(event, index) {

    // Check to see if we're at the end
    if (this.getIndex() >= this.getSize() - size) {

      // Disable the Next link
      jQuery("a.next").addClass("disabled");

    }

  });

  // Handle the Scrollable control's onBeforeSeek event
  scrollable.onBeforeSeek(function(event, index) {

    // Check to see if we're at the end
    if (this.getIndex() >= this.getSize() - size) {
      
      // Check to see if we're trying to move forward
      if (index > this.getIndex()) {

        // Cancel navigation
        return false;

      }

    }

  });

});

The code simply hooks into the Scrollable control’s onBeforeSeek event, and stops the control from moving forward if at the end. This will prevent the user from scrolling past the end not only for clicking the forward and backward links, but also for arrow key and mouse wheel input (if mouse wheel support is turned on via the initialization settings for the control). The previous solutions I found from neogisme and soon7 would scroll past and then scroll back and/or disable the forward and backward links, but they didn’t properly prevent the user from scrolling past the end for all forms of input.

Here’s an example of it in action:



If you end up using this (or have something useful to add to it), please drop me a line below. :)

Update (March 28th, 2011):

Steve Rucker was kind enough to send this to me as a way to make the solution data-driven and more dynamic. If you want simplicity, use the above code, but if you’re looking for it to be more dynamic, dive into Steve’s code below:

Your blog – jQuery Tools Scrollable – Stop Scrolling Past the End – was a huge help! Your code saved me a lot of time – the client wanted to always show 4 items in the scroller, and this worked perfectly.

However, I’m working on a large site that’s pulling dynamic info from a database, so I needed to modify the script to allow for an undetermined number of scrollers.

Here’s what I came up with:

$('.scrollableProducts').each(function (i) {  // loop through scrollers
    var thisCarousel = $('.scrollableProducts:eq(' + i + ')');  // var this scroller
    var size = 4;
    var bucketCount = $(thisCarousel).find('.productLineBucket').length; // get number of buckets in this scroller
    var thisNext = $(thisCarousel).parent().find('a.next'); // var this next controller
    if (bucketCount < size + 1) { // hide next controller if less than size
        thisNext.addClass('disabled');
    }
    $(thisCarousel).scrollable();
    var scrollable = $(thisCarousel).data("scrollable");
    scrollable.onSeek(function () {
        if (this.getIndex() >= this.getSize() - size) {
            thisNext.addClass('disabled');
        }
    });
    scrollable.onBeforeSeek(function (event, index) {
        if (this.getIndex() >= this.getSize() - size) {
            if (index > this.getIndex()) {
                return false;
            }
        }
    });
});

Thanks again, Steve!

28 thoughts on “jQuery Tools Scrollable – Stop Scrolling Past the End

  1. Reissy

    Hi Jason, Great method works great however… I have a scrollable within a scrollable and currenlty using my workaround to get the scrollable to scroll without the main scroller scrolling.

    jQuery(&quot;a.backward&quot;).click(function() { jQuery(this).parent().find(&quot;.scrollable&quot;).data(&quot;scrollable&quot;).prev(); 
    });
    
    jQuery(&quot;a.forward&quot;).click(function() { jQuery(this).parent().find(&quot;.scrollable&quot;).data(&quot;scrollable&quot;).next(); 
    });
    

    Any solution to get this to work with your method ?

    Cheers
    Reiss

    Reply
  2. Jason Carr Post author

    Reissy, I haven’t tried it (yet) but I think the trick should be to use different class (or ID) names for the parent vs. the child scrollable. I can’t tell if you’re already doing that from your sample code. Give it a shot with different class names (replace all instances of “.scrollable” in either the parent or the child javascript, and change the class=”scrollable” in the markup to something different for one of the two.

    Does that fix it? If not, post more of your code (or show me where I can see it online) and I’ll help you further. It should work fine unless the Scrollable control has inherent issues with being nested.

    Reply
  3. Reissy

    It works, slides one at a time however it does not disable the next button when it gets to the end.

    I have 4 items but it will scroll one by one to the 6th item, doesn’t seem to disable the next button when it gets to the 4th item… not sure why it keeps going as i only have 4 items.

    I am using my work around code in my first post to get around the next and previous buttons controlling the main scrollable as i have a scrollable within a scrollable.

    Any Ideas ?

    Reply
  4. Jason Carr Post author

    Did you give the parent and the child scrollables different class names? That’s imperative to solving the issue, I believe (if you have not done it yet). If you have, great; we’ll look into it further. Otherwise, let me know if you need help changing the class name.

    Reply
  5. Reissy

    Yup my classes are both different.

    /* Scrollable Pages */
    jQuery(&quot;#main&quot;).scrollable({				   
    
    // Main navigator (thumbnail images)
    }).navigator(&quot;#nav-items&quot;);
    
    // Initialize the scrollable
    jQuery(&quot;.subpages&quot;).scrollable();
    var subpages = jQuery(&quot;.subpages&quot;).data(&quot;scrollable&quot;);
    
    // Number of visible items
    var size = 3;
    
    subpages.onSeek(function (event, index) {
    	if (this.getIndex() &amp;gt;= this.getSize() - size) {
    		// Disable the Next link
    		jQuery(&quot;a.forward&quot;).addClass(&quot;disabled&quot;);
    	}
    });
    
    subpages.onBeforeSeek(function (event, index) {
    	if (this.getIndex() &amp;gt;= this.getSize() - size) {
    		if (index &amp;gt; this.getIndex()) {
    			return false;
    		}
    	}
    });
    
    // Sub scrollable next and previous hack
    jQuery(&quot;a.backward&quot;).click(function() { jQuery(this).parent().find(&quot;.subpages&quot;).data(&quot;scrollable&quot;).prev(); 
    });
    jQuery(&quot;a.forward&quot;).click(function() { jQuery(this).parent().find(&quot;.subpages&quot;).data(&quot;scrollable&quot;).next(); 
    });
    
    Reply
  6. Jason Carr Post author

    Cool…that helps. Can you post your markup as well, though? Or a watered-down version? I need to be able to debug the javascript and it would be a lot easier with the markup included. If you’d like you can email me instead of posting it online: jasondavidcarr@gmail.com.

    Reply
  7. Jason Carr Post author

    To update everyone on Reissy’s issue, we were able to solve the problems by adding unique ID tags to both of the child scrollable controls, and their associated previous and next links. The issue was that the controls were conflicting with each other because they were being referenced in the javascript by duplicate class names. We changed the javascript to use the unique ID tags instead of the class names, and added “prev” and “next” settings to the scrollable to specify which links to use. Here’s the resulting javascript:

    jQuery("#main").scrollable({				   
    // main page navigator (thumbnail images)
    }).navigator("#nav-items");
    
    // initialize the scrollable subpages
    jQuery("#subpages1").scrollable({ next: "#forward1", prev: "#backward1" });
    jQuery("#subpages2").scrollable({ next: "#forward2", prev: "#backward2" });
    
    var subpages1 = jQuery("#subpages1").data("scrollable");
    var subpages2 = jQuery("#subpages2").data("scrollable");
    
    // number of visible items
    var size = 3;
    
    subpages1.onSeek(function (event, index) {
    	if (this.getIndex() >= this.getSize() - size) {
    		// disable the next link
    		jQuery("a#forward1").addClass("disabled");
    	}
    });
    
    subpages1.onBeforeSeek(function (event, index) {
    	if (this.getIndex() >= this.getSize() - size) {
    		if (index > this.getIndex()) {
    			return false;
    		}
    	}
    });
    
    subpages2.onSeek(function (event, index) {
    	if (this.getIndex() >= this.getSize() - size) {
    		// disable the next link
    		jQuery("a#forward2").addClass("disabled");
    	}
    });`
    
    subpages2.onBeforeSeek(function (event, index) {
    	if (this.getIndex() >= this.getSize() - size) {
    		if (index > this.getIndex()) {
    			return false;
    		}
    	}
    });
    

    Please keep in mind, though, that this code was only to resolve Reissy’s specific issue (nesting and conflicting scrollables). Use it as an example if you’re implementing more than one scrollable on a page, but otherwise you can use the javascript in the post, instead.

    Reply
  8. Damjan

    Hi!

    Great solution. I’m working on similar solution: three at once, one by one move but i want first box to be on center, not on left.. here you can check what i try to do: http://dev.widesec.com:81/_sliding/ Box with “aaaaaaaaaaaa” should be in the center. It’s possible to do that?

    Thanks,

    Damjan

    Reply
  9. Mark

    Just wanted to say thank you for posting, I had the exact same issue and was looking for the right events to call to make this happen, saved me a lot of headache!

    Reply
  10. Jeff Shain

    Do you have any idea how I can make the scroller move to a particular list index on page load? Say if you show 4 items at a time, and the 7th item is the selected index, have the scroller move to the 7th item on load.

    Reply
  11. Arthur

    Hi guys,
    I’ve found another solution, by hacking the core.
    First you need to deminify the code, so it would be more clear of what you want to edit.
    I have built in functionality in Netbeans, but if you don’t have it you can use this service :
    http://www.formatjavascript.com/ or search for some other.
    Then I add one option to the configurations list (conf object) — visible:1 This option states for the number of visible blocks in your gallery. The default value is 1. With this default value, the plugin will work as before.
    You can add it for example after the last options line, so It would look like this:
    wheelSpeed:0,
    visible:1
    Then change seekTo function line 98
    We add -e.visible
    if(!e.circular&&bf.getSize()-e.visible||b<-1)return f;

    and the last thing we change is the line 150.

    a.isDefaultPrevented()||(n.toggleClass(e.disabledClass,b=f.getSize()-e.visible))
    here we also just replace -1 with -e.visible

    Note that the line number is after I’ve added the ‘visible’ property. Also the version of the plugin si 1.2.6 I haven’t downloaded any additional libraries only scrollable –without Autoscroll plugin or Navigator plugin

    There is also a problem in this plugin that it scrolls five items at once and we only have 1 or 2 items at the end of the stack, it will scroll them all the way it can, like 5 before that, which is also not really cute (

    Hope this helps.

    Reply
    1. Arthur

      Oh forgot to tell that you need to initializ th eplugin with the nuber of visible pictures. So if I want to scroll images one by one, and I have 5 visible images in the gallery, I will initialize the plugin as follows:
      $(‘.scrollable’).scrollable({
      visible:5
      });

      Reply
  12. Michael J Salo

    Thanks to Jason for this helpful solution to scrolling one item at a time. I’ve implemented it and notice just a minor remaining issue. The timing of the style change of the Next button differs from the Prev button: The Prev disables before-seek, while the Next disables after-seek.

    I don’t see a way to straighten it out as that style change doesn’t seem to stick when it’s in an onSeek event.

    Reply
    1. Jason Carr Post author

      Yeah, true. Didn’t notice that. But for what it’s worth, it doesn’t seem to break anything by clicking on the next link before it has been disabled, so it’s only a very minor visual setback (one that almost no one will notice).

      Reply
  13. Glenn

    @Jason Carr, I think this is the more generic solution than yours and even Steve Rucker’s.

    $(‘.scrollable’)
    .scrollable({
    size: 5, //change accordingly
    onSeek: function () {
    //fix scrollable next button bug
    var conf = this.getConf();
    if (this.getIndex() >= this.getSize() – conf.size) {
    this.getNaviButtons().filter(conf.next).addClass(conf.disabledClass);
    }

    }
    })
    //on initial load of scrollable(s), fix next button bug too in all scrollables if total number of scrollable items is equal or less than scrollable size
    .each(function () {
    var api = $(this).data(‘scrollable’),
    conf = api.getConf();
    if (api.getSize() <= conf.size) {
    api.getNaviButtons().filter(conf.next).addClass(conf.disabledClass);
    }
    });

    Reply

Leave a Reply