Trying to randomise a jQuery content slider

Hi everyone,

I’m using a very nice jQuery content slider called Easy Slider on my site that I downloaded from Css Globe.

The script is excellent and does just what I want - except I can’t make it randomise the list, it always scrolls from left to right or right to left!

I’m far from good with JavaScript, so my attempts at solving this have been feeble. Although I’m sure it must be an easy fix!

If anyone wouldn’t mind taking a glance over the script to see if they can spot what I need to change to make it random it would be greatly appreciated!

I’ve tried contacting the original plugin developer but have had no response yet. The comments on the Easy Slider page didn’t bear much fruit either unfortunately.

I’ve pasted the script I’m using on my site below:


/*
 * 	Easy Slider 1.7 - jQuery plugin
 *	written by Alen Grakalic	
 *	http://cssglobe.com/post/4004/easy-slider-15-the-easiest-jquery-plugin-for-sliding
 *
 *	Copyright (c) 2009 Alen Grakalic (http://cssglobe.com)
 *	Dual licensed under the MIT (MIT-LICENSE.txt)
 *	and GPL (GPL-LICENSE.txt) licenses.
 *
 *	Built for jQuery library
 *	http://jquery.com
 *
 */
 
/*
 *	markup example for $("#slider").easySlider();
 *	
 * 	<div id="slider">
 *		<ul>
 *			<li><img src="images/01.jpg" alt="" /></li>
 *			<li><img src="images/02.jpg" alt="" /></li>
 *			<li><img src="images/03.jpg" alt="" /></li>
 *			<li><img src="images/04.jpg" alt="" /></li>
 *			<li><img src="images/05.jpg" alt="" /></li>
 *		</ul>
 *	</div>
 *
 */

(function($) {

	$.fn.easySlider = function(options){
	  
		// default configuration properties
		var defaults = {			
			prevId: 		'prevBtn',
			prevText: 		'Previous',
			nextId: 		'nextBtn',	
			nextText: 		'Next',
			controlsShow:	true,
			controlsBefore:	'',
			controlsAfter:	'',	
			controlsFade:	true,
			firstId: 		'firstBtn',
			firstText: 		'First',
			firstShow:		false,
			lastId: 		'lastBtn',	
			lastText: 		'Last',
			lastShow:		false,				
			vertical:		false,
			speed: 			800,
			auto:			false,
			pause:			7000,
			continuous:		false, 
			numeric: 		false,
			numericId: 		'controls'
		}; 
		
		var options = $.extend(defaults, options);  
				
		this.each(function() {  
			var obj = $(this); 				
			var s = $("li", obj).length;
			var w = $("li", obj).width(); 
			var h = $("li", obj).height(); 
			var clickable = true;
			obj.width(w); 
			obj.height(h); 
			obj.css("overflow","hidden");
			var ts = s-1;
			var t = 0;
			$("ul", obj).css('width',s*w);			
			
			if(options.continuous){
				$("ul", obj).prepend($("ul li:last-child", obj).clone().css("margin-left","-"+ w +"px"));
				$("ul", obj).append($("ul li:nth-child(2)", obj).clone());
				$("ul", obj).css('width',(s+1)*w);
			};				
			
			if(!options.vertical) $("li", obj).css('float','left');
								
			if(options.controlsShow){
				var html = options.controlsBefore;				
				if(options.numeric){
					html += '<ol id="'+ options.numericId +'"></ol>';
				} else {
					if(options.firstShow) html += '<span id="'+ options.firstId +'"><a href=\\"javascript:void(0);\\">'+ options.firstText +'</a></span>';
					html += ' <span id="'+ options.prevId +'"><a href=\\"javascript:void(0);\\">'+ options.prevText +'</a></span>';
					html += ' <span id="'+ options.nextId +'"><a href=\\"javascript:void(0);\\">'+ options.nextText +'</a></span>';
					if(options.lastShow) html += ' <span id="'+ options.lastId +'"><a href=\\"javascript:void(0);\\">'+ options.lastText +'</a></span>';				
				};
				
				html += options.controlsAfter;						
				$(obj).after(html);										
			};
			
			if(options.numeric){									
				for(var i=0;i<s;i++){						
					$(document.createElement("li"))
						.attr('id',options.numericId + (i+1))
						.html('<a rel='+ i +' href=\\"javascript:void(0);\\">'+ (i+1) +'</a>')
						.appendTo($("#"+ options.numericId))
						.click(function(){							
							animate($("a",$(this)).attr('rel'),true);
						}); 												
				};							
			} else {
				$("a","#"+options.nextId).click(function(){		
					animate("next",true);
				});
				$("a","#"+options.prevId).click(function(){		
					animate("prev",true);				
				});	
				$("a","#"+options.firstId).click(function(){		
					animate("first",true);
				});				
				$("a","#"+options.lastId).click(function(){		
					animate("last",true);				
				});				
			};
			
			function setCurrent(i){
				i = parseInt(i)+1;
				$("li", "#" + options.numericId).removeClass("current");
				$("li#" + options.numericId + i).addClass("current");
			};
			
			function adjust(){
				if(t>ts) t=0;		
				if(t<0) t=ts;	
				if(!options.vertical) {
					$("ul",obj).css("margin-left",(t*w*-1));
				} else {
					$("ul",obj).css("margin-left",(t*h*-1));
				}
				clickable = true;
				if(options.numeric) setCurrent(t);
			};
			
			function animate(dir,clicked){
				if (clickable){
					clickable = false;
					var ot = t;				
					switch(dir){
						case "next":
							t = (ot>=ts) ? (options.continuous ? t+1 : ts) : t+1;						
							break; 
						case "prev":
							t = (t<=0) ? (options.continuous ? t-1 : 0) : t-1;
							break; 
						case "first":
							t = 0;
							break; 
						case "last":
							t = ts;
							break; 
						default:
							t = dir;
							break; 
					};	
					var diff = Math.abs(ot-t);
					var speed = diff*options.speed;						
					if(!options.vertical) {
						p = (t*w*-1);
						$("ul",obj).animate(
							{ marginLeft: p }, 
							{ queue:false, duration:speed, complete:adjust }
						);				
					} else {
						p = (t*h*-1);
						$("ul",obj).animate(
							{ marginTop: p }, 
							{ queue:false, duration:speed, complete:adjust }
						);					
					};
					
					if(!options.continuous && options.controlsFade){					
						if(t==ts){
							$("a","#"+options.nextId).hide();
							$("a","#"+options.lastId).hide();
						} else {
							$("a","#"+options.nextId).show();
							$("a","#"+options.lastId).show();					
						};
						if(t==0){
							$("a","#"+options.prevId).hide();
							$("a","#"+options.firstId).hide();
						} else {
							$("a","#"+options.prevId).show();
							$("a","#"+options.firstId).show();
						};					
					};				
					
					if(clicked) clearTimeout(timeout);
					if(options.auto && dir=="next" && !clicked){;
						timeout = setTimeout(function(){
							animate("next",false);
						},diff*options.speed+options.pause);
					};
			
				};
				
			};
			// init
			var timeout;
			if(options.auto){;
				timeout = setTimeout(function(){
					animate("next",false);
				},options.pause);
			};		
			
			if(options.numeric) setCurrent(0);
		
			if(!options.continuous && options.controlsFade){					
				$("a","#"+options.prevId).hide();
				$("a","#"+options.firstId).hide();				
			};				
			
		});
	  
	};

})(jQuery);


Many thanks again!

Alec

Before we take a look at it, how do you want the randomness to work?

Random numbers can be like this:
1, 4, 3, 2, 2, 2, 5, 4, 3, 3, 2, 2, 2, 6, 1

Are you instead wanting to use a random bag instead, where all values are picked once at random? That is the better way to do things but means additional coding work to implement.

Hi pmw57, thanks for your reply.

I guess I don’t mind how technically random it is, whichever is simplest. It would be nice though if we had some prevention for it displaying the same <li> twice in a row.

This is a slider which appears in the header on every page of my site Honda Civic Forum. All I really need is for the user not to see the slider starting from the same place on every page load. I’d like to give equal prominence to each promo in the slider.

Thanks for your help!

Then a random bag is not a suitable solution.

Using the standard random function will ensure that equal prominence is given to the starting location of the slider.

I don’t think it’s a good idea to go sifting through the easySlider plugin monkey-patching its functionality. Instead, it might be better to simply shuffle the list-items before calling the plugin, like:


$('#slider').shuffle('li').easySlider();

The “shuffle” plugin:


jQuery.fn.shuffle = (function(){
    
    function fisherYatesShuffle(arr) {
        
        // Fisher-Yates shuffle has been proven
        // to be more random than the conventional
        // arr.sort(function(){return Math.random()-.5})
        // http://www.robweir.com/blog/2010/02/microsoft-random-browser-ballot.html
        
        var i = arr.length,
            r, tempI, tempR;
        
        if ( i === 0 ) {
            return arr;
        }
        
        while ( i-- ) {
            
            // Generate random index:
            r = Math.floor(Math.random() * (i + 1));
            
            // Shuffle:
            tempI = arr[i];
            tempR = arr[r];
            arr[i] = tempR;
            arr[r] = tempI;
            
        }
        
        return arr;
        
    }
    
    return function(s) {
        
        return this.each(function(){

            var container = jQuery(this);
            
            // Append shuffled children
            container.append(
                fisherYatesShuffle(container.find(s))
            );
            
        });
        
    };

})();

Ahh, you want to randomize the direction. Sorry. Ignore my previous post (I’ll keep it up anyhow, for anyone visiting this thread from Google)…

OK, that looks like the best solution. Is that easy to implement into the plugin I have, or do I need to create a separate plugin as JimmyP suggests?

Yes, I’d like to randomise the unordered list so my members don’t see the same content in the same order on every page. Is that not what your plugin does?

Thanks for your help!

What do you think about this idea then.

Randomise the unordered list on page load where you would loop through the list once for each list item, picking one from an ever-decreasing set, and move it to the bottom.

First time through, random item is picked from 0 to 6 and moved to the bottom.
Second time through, random item is picked from 0 to 5 and moved to the bottom.
Third time through, random item is picked from 0 to 4 and moved to the bottom.

Repeat until end.

Yes, that sounds like exactly what I’m after!

All right then.

$(‘#slider ul li’) gets the list items, and you will want to use a for loop that counts from the number of items down to 1.

Within the for loop you can use the :lt() to get all of the list items that are less than the loop index, with perhaps $(‘#slider ul li:lt(’+i+‘)’)
Then you can use Math.floor and [url=“https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Math/random”]Math.random(i) times the same loop index to get the index of a random item, and then move it [url=“http://api.jquery.com/after/”]after the last item in the list.

Unfortunately, I’ve fallen at the very first hurdle!

Like I say, I am an absolute beginner with JavaScript and wouldn’t know where to begin to create this ‘for loop’.

Could you point me in the right direction of some more explanatory articles or give me some advice on how I should go about writing this?

Many thanks!

Ahh, well hen okay.

I’ll see if I can get something together for you in the morning.

Thanks so for much for your time Paul.

The quickest simple solution to achieve what I’m after will do!

Something like the following should do the trick.


$(function () {
    var items = '#slider ul li';
    $(items).each(function (i) {
        var randomItem = Math.floor(Math.random() * ($(items).length - i));
        $(':eq(' + randomItem + ')', items)
            .after($(':last', items));
    });
});

Thanks, that looks like it should do the trick!

Shall I include this in the page as a separate plugin or can I put it in the same Easy Slider script?

Don’t include it with the easy slider script, because in 6 months when you (or someone else) updates to a more recent version, you’ll then find that the page no longer behaves as expected.

Place the script in a separate javascript file, and load that script from the end of the body, just before the </body> tag.

Right. This is now live on my site, but it seems to have removed the contents of each <li>!

It’s loading the script you wrote for me at the bottom of the page and the script is located here:

http://www.hondacivicforum.co.uk/styles/prosilver/template/slider-randomizer.js

However viewing the slider in action in Firebug shows the contents of each <li> having been removed by the randomizer.

Any ideas?

Oh no you didn’t did you? Get that untested code off of your live web site right now!

I shall investigate later on when it’s not 1:40 AM

:shifty: wasn’t sure how else you’d see the problem unless it was live.

I’ve commented out the JavaScript reference for now.

Head to bed!

Paul, that works absolutely great!

Thanks so much for your help. I feel like I’ve implemented a good solution to my problem now.

If you’re interested, see it live here.