Dynamic jQuery tabs, closing and opening UL mid list

Hi all,

I am looking to create a gallery using an unordered list. The gallery is created dynamically via a CMS, so the number of images will vary.

What I wanted to do was:

Display the first 9 images in the gallery (this will be styled so there are 3 images per row, so 3 columns 3 rows)
If there are more than 9 images, I wanted to, via jQuery, close the UL after the 9th image, then reopen the UL
Then again via jQuery, on load hide all but the first UL, then use Next/Previous buttons to cycle through the other ULs (the remaining images in the gallery)

I have set this up, but the problem I am having is the way the closing ul and opening ul is reordered (basically swapped round to an opening/closing ul)

My HTML is as follows:


<ul>
	<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
	<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
	<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
	<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
	<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
	<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
	<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
	<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
	<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
	<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
	<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
	<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
	<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
	<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
</ul>

The jQuery I am using:


$('</ul><ul>').insertAfter( $('#images li:nth-child(9n)') );

The problem is it is formatting as:


<ul>
	<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
	<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
	<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
	<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
	<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
	<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
	<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
	<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
	<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
	<ul></ul>
	<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
	<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
	<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
	<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
	<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
</ul>

Can anybody please help?

Many thanks

You may need to do this in a more manual manner, by looping through the list and adding elements to a different ul list in groups of nine.

This might be done efficiently by detatching the list from the DOM, looping througn the list moving elements from the 10th index to a different list until no more items exist at the 10th, and then reattacting both lists to back to the DOM.

Hi Paul,

Thanks for this. Do you have any examples of any code which I could see so I can try this out? Sorry, this method is unfamiliar to me.

No examples of code come to mind (read: I’m not writing it for you, it’s bedtime) - but if you put your hand to it, we can help to refine your technique or deal with any issues that occur

Not asking anyone to do it for me, just pretty new to jQuery that’s all, so no idea where to even start with detaching items from the DOM. Thanks for the info though, I will have a search around.

4 minutes until midnight. Let’s see what we can do.

If you create a variable that is a document fragment, you can then use the [url=“http://api.jquery.com/appendTo/”]appendTo command to move the in-page elements off of the page and in to the document fragment instead.

Night…

Gobbledygook! Haha. Ok, that will get me started… reading up on .detach() too. Thanks Paul, g’night.

Would this work?

$('#images li:nth-child(9n)').html($('#images li:nth-child(9n)').html() + '</ul><ul>');

That didn’t work for me unfortunately

Okay, the following works for me:

<html>
	<head>
		<script type="text/javascript" src="http://code.jquery.com/jquery-1.7.2.min.js"></script>
		<script type="text/javascript">
			$(function()
			{
				var MAX_ITEMS_IN_EACH_GROUP = 9
				var items = $('#images li');
				if (items.length > MAX_ITEMS_IN_EACH_GROUP)
				{
					do
					{
						var newList = $('<ul></ul>');
						newList.appendTo($('#images').parent());
						
						for (i = MAX_ITEMS_IN_EACH_GROUP; i < items.length && i < MAX_ITEMS_IN_EACH_GROUP * 2; i++)
						{
							items.eq(i).appendTo(newList);
						}
						
						items = $('#images li');
					} while(items.length > MAX_ITEMS_IN_EACH_GROUP);
				}
			});
		</script>
	</head>
	<body>
		<ul id="images">
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
		</ul>
	</body>
</html>

Wow, cpradio - that is absolutely fantastic! Thank you so much for doing that!

It works perfectly, gonna spend some time going through it line by line to learn what each bit does, do you mind if I post back if I am unsure what certain bits?

Sure, just let me know what information you want to know and I’ll be glad to answer it.

@Sikwondo,

I’ve cleaned up the code a bit (did some refactoring of the jQuery objects too) and turned it into a plugin (so calling it is a tad bit easier).

<html>
	<head>
		<script type="text/javascript" src="http://code.jquery.com/jquery-1.7.2.min.js"></script>
		<script type="text/javascript">
			$(function()
			{
				// Call the splitList on the #image ul object
				$('#images').splitList({ maxItems: 9 });
				$('.demo2').splitList({ maxItems: 5 });
			});
			
			// Starting from here to the end of the script tag, this can be put into its own file
			(function($) {
				$.fn.splitList = function(options)
				{
					// Loop through the jQuery object that splitList is being called on
					return this.each(function()
						{
							// Default MAX_ITEMS value
							var MAX_ITEMS_IN_EACH_GROUP = 9;
							
							// Check if the User overrode the MAX_ITEMS default
							if (options.maxItems != undefined)
							{
								MAX_ITEMS_IN_EACH_GROUP = options.maxItems;
							}
							
							// Set this to the current jQuery object being executed
							var currentElement = $(this);
							var insertAfterElement = currentElement;
							// Find the li tags for the current ul object
							var items = currentElement.find('li');
							
							// Check if there are more li tags than the defined MAX_ITEMS
							if (items.length > MAX_ITEMS_IN_EACH_GROUP)
							{
								// Loop through the li tags, so long as there are more li tags than the defined MAX_ITEMS
								do
								{
									// Create a new list
									var newList = $('<ul></ul>');
									// Insert the new list onto the page
									newList.insertAfter(insertAfterElement);
									
									// Loop through the li tags starting at the point the MAX_ITEMS was exceeded and going through to end of the list
									// or to the next point where MAX_ITEMS is exceeded
									for (i = MAX_ITEMS_IN_EACH_GROUP; i < items.length && i < MAX_ITEMS_IN_EACH_GROUP * 2; i++)
									{
										// Append the li tag to the new list
										items.eq(i).appendTo(newList);
									}
									
									// Redefine the li tag items on the current ul object so the while loop behaves
									items = currentElement.find('li');
									
									// Set the insertAfterElement to newList so if a third list is built, it will be added after this one
									insertAfterElement = newList;
								} while(items.length > MAX_ITEMS_IN_EACH_GROUP);
							}
						});
				};
			})(jQuery);
		</script>
	</head>
	<body>
		Demo 1:
		<ul id="images">
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
		</ul>
		<hr />
		Demo 2: First Set
		<ul class="demo2">
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
		</ul>
		
		Demo 2: Second Set
		<ul class="demo2">
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
			<li><a href="#"><img src="/img/image.jpg" alt="" /></a></li>
		</ul>
	</body>
</html>

Hi cpradio… I don’t fully understand the code, I made some comments on the jQuery code… if you have a chance, could you take a look and let me know what the specific lines I commented on mean?

No problem if you don’t have time, just trying to get a better understanding of the code.

Many thanks


var maxImages = 9, // Set a variable wit ha value of 9
			items = $('#images li'); // Set a variable for the list items within #images

		if (items.length > maxImages) { // If the number of li items is more than maxImages (9)
			do {
				var newList = $('<ul></ul>'); // Variable for a new unordered list
				newList.appendTo($('#images').parent()); // Add this new UL to the #images div (appended - at the end)

				for (i = maxImages; i < items.length && i < maxImages * 2; i++) // Could you please explain this line?
				{
					items.eq(i).appendTo(newList); // Could you please explain this line?
				}

				items = $('#images li');
			} while(items.length > maxImages); // Could you please explain this line?
		}

Wow, thank you :slight_smile:

I will use this new code… thanks again. Really do appreciate the help, have been banging my head against a wall and this has helped me so much.

Not a problem, let me know if my comments need better clarification in the new code, and I’ll gladly explain them better :slight_smile:

Thank you :slight_smile:

I pretty much follow most of the code… but I get a little lost at the following:


for (i = maxItems; i < items.length && i < maxItems * 2; i++)
							{
								// Append the li tag to the new list
								items.eq(i).appendTo(newList);
							}
							
							// Redefine the li tag items on the current ul object so the while loop behaves
							items = currentElement.find('li');
							
							// Set the insertAfterElement to newList so if a third list is built, it will be added after this one
							insertAfterElement = newList;
						} while(items.length > maxItems);
					}

Could you explain what this but does… sorry to be a pain.

Not a problem,

The for loop, starts at the first point the original list exceeds the maximum number of items you want shown in each list. So if there are 21 items in the original list and you only want each list to contain 9 items, it starts at position 9.

It then cycles through the remaining items in the list (or up until the next maximum number of items would be reached). So since there are 21 items in the original list, and we started at position 9, it will run from position 9 to position 18 (9 items, as that is defined as our maximum).

It then takes each item it is looping through and moves it to the New List that was created immediately before the for loop. So now the original list has 12 items and the new list it just created has 9 items.

However, because jQuery caches information, I have to run the currentElement.find(‘li’) statement again and store it back into items so that jQuery now realizes there are only 12 items in the original list, not 21 items.

Then to ensure the 3rd list will show up after the new list of 9 items we just created, I update insertAfterElement to be the list that was just created (instead of the original list of items).

To see the affects of each of these, is quite simple, if you remove the code “&& i < maxItems * 2” from the for loop, you will see the second list it creates (when there are 21 items in the original list) will contain 12 items. So you will have 1 list with 9 items, and a second with 12 items (no third list is created).

To see how jQuery caches the items (just an FYI, this causes an infinite loop), comment out the line “items = currentElement.find(‘li’);”. The browser will continuously spin and likely lock up (or ask you to kill the script) because the do-while loop will never exit as jQuery thinks there is always 21 items in the list.

To see how the insertAfterElement plays into account, comment out the line “insertAfterElement = newList;” and you will see the final output being 3 lists, the first containing 9 items, the second, contains 3 items, and the last contains 9 items. Whereas, the expected output is 9 items, 9 items, then 3 items.

Hope this helps.

That’s really cool… thanks so much :slight_smile:

I may be pushing it a bit asking for more help so tell me where to go if so :slight_smile: but I am trying to add previous and next buttons, and only show one UL at a time, and cycle through them

I have started to code this together, but the problem I am having is how to update the value of curGal which I thought could be assigned to the current gallery.

I wanted to set it so that when the user is on the first UL, the previous button is dimmed and not clickable (but not sure how to do this so have just tried to set it up as hidden. THen, then the user is on the last gallery, the next is dimmed and unclickable (or hidden).

This is the code I have so far:

The paginate buttons:


<div class="paginate"><button class="prev" data-dir="prev">Previous page</button> / <button class="next" data-dir="next">Next page</button></div>


// Hide all of the Gallery ULs
$('ul.gallery').hide();

// Set variable for the first Gallery and show it
var startGal = $('ul.gallery:first').show();


// Find out the number of Galley ULs
var numberGal = $('ul.gallery').length,
	curGal = $('ul.gallery:first');  // Keep track of which gallery the user is on

// Listen for when one of the buttons is clicked
$('.paginate').show().find('button').on('click', function() {
	var direction = $(this).data('dir');

	// Update current gallery value when clicked
	( direction === 'next' ) ? curGal.next() : curGal.prev();

	// if first gallery
	if ( curGal === 1 ) {
		prev.hide();
	}

	if ( curGal === numberGal ) {
		next.hide();
	}

});

var next = $('button.next'),
	prev = $('button.prev');

next.on('click', function() {
	curGal.fadeOut(200);
	curGal.next().delay(200).fadeIn(200);
}); // Show the buttons (hidden for users with JS disabled)

prev.on('click', function() {
	curGal.fadeOut(200);
	curGal.prev().delay(200).fadeIn(200);
});