{"id":1970,"date":"2011-10-28T11:39:10","date_gmt":"2011-10-28T10:39:10","guid":{"rendered":"http:\/\/www.simonbattersby.com\/blog\/?page_id=1970"},"modified":"2013-07-18T14:07:55","modified_gmt":"2013-07-18T13:07:55","slug":"jquery-image-and-caption-slider","status":"publish","type":"page","link":"https:\/\/www.simonbattersby.com\/blog\/jquery-image-and-caption-slider\/","title":{"rendered":"jQuery image and caption slider"},"content":{"rendered":"<p>Been thinking for a while about putting together a slider where the image animates left to right and the accompanying text animates up and down. I wanted a slider that would allow the following:<\/p>\r\n<ul class=\"bulletlist\">\r\n<li>Allow the html to be easily added &#8211; image and then caption as you would naturally add it<\/li>\r\n<li>Allow more than one paragraph in the caption<\/li>\r\n<li>Nicely degrading if javascript is disabled<\/li>\r\n<\/ul>\r\n<p>So here it is&#8230; <\/p>\r\n\r\n<h3>The demo<\/h3>\r\n<div id=\"slider\" class=\"shadow roundbox\">\r\n\r\n\t\t<img loading=\"lazy\" decoding=\"async\" class=\"active\" src=\"\/demos\/images\/img1.jpg\" alt=\"Dingle Peninsula, Ireland\" width=\"500\" height=\"150\" \/>\r\n\t\t<div class=\"caption active\"><p>Dingle Peninsula, County Kerry, looking northward from Clogher Head<\/p>\r\n\t\t<\/div>\r\n\r\n\t\t<img loading=\"lazy\" decoding=\"async\" src=\"\/demos\/images\/img4.jpg\" alt=\"Sunset on beach\" width=\"500\" height=\"150\" \/>\r\n\t\t<div class=\"caption\"><p>Sunset on the waves, near Fanore, County Clare<\/p>\r\n\t\t<\/div>\r\n\r\n\t\t<img loading=\"lazy\" decoding=\"async\" src=\"\/demos\/images\/img2.jpg\" alt=\"Bee\" width=\"500\" height=\"150\" \/>\r\n\t\t<div class=\"caption\"><p>A bee.<\/p>\r\n                <p>Managed to get the focus just right on the wing and head.<\/p>\r\n\t\t<\/div>\r\n\r\n\t\t<img loading=\"lazy\" decoding=\"async\" src=\"\/demos\/images\/img3.jpg\" alt=\"Sparrowhawk\" width=\"500\" height=\"150\" \/>\r\n\t\t<div class=\"caption\"><p>A sparrowhawk in our front garden.<\/p>\r\n\t\t<p>Just out of shot is the racing pigeon she&#8217;s just caught and killed.<\/p>\r\n\t\t<\/div>\r\n\t\t\r\n\r\n\t\t\r\n\t\t<img loading=\"lazy\" decoding=\"async\" src=\"\/demos\/images\/img5.jpg\" alt=\"St Ives at sunset\" width=\"500\" height=\"150\" \/>\r\n\t\t<div class=\"caption\"><p>Sunset over St Ives, Cornwall<\/p>\r\n\t\t<\/div>\r\n\r\n\t\t<img loading=\"lazy\" decoding=\"async\" src=\"\/demos\/images\/img6.jpg\" alt=\"Frog\" width=\"500\" height=\"150\" \/>\r\n\t\t<div class=\"caption\"><p>A frog in our garden pond.<\/p>\r\n\t\t<\/div>\r\n<\/div>\r\n<p>Here is a <a href=\"\/demos\/image_caption_demo.htm\">standalone demo<\/a> too.<\/p>\r\n<h3>The code<\/h3>\r\n<p>This needs a fair amount of css, in part to make sure the page displays OK in the absence of javascript. Amend the width and heights to suit your own images.<\/p>\r\n<pre>#slider{position:relative;padding:10px;height:150px;width:760px}\r\n#slider img,#slider div{display:none}\r\n#slider img.active,#slider #slider_box{display:block;position:absolute;left:10px;top:10px;width:500px;height:150px}\r\n#slider div.active, #slider #caption_box{display:block;position:absolute;left:560px;top:10px;width:170px;height:150px;}\r\n#slider img{position:absolute;}\r\n.caption p{margin-top:0}\r\n\r\n#slider #slider_box{overflow:hidden}\r\n#slider #slider_images{position:absolute;left:0;top:0;display:block}\r\n#slider_images img{display:block}\r\n\r\n#slider #caption_box{overflow:hidden}\r\n#slider #slider_captions{display:block;position:absolute;left:0;top:0}\r\n#slider #slider_captions div{display:block;height:150px}<\/pre>\r\n\r\n<p>And here&#8217;s the javascript. With a lot of images, you could call this using <code>$(window).load()<\/code> just to make sure all the images have loaded before the first animation occurs, but with a few small images it should be OK anyway.<\/p>\r\n\r\n<pre>\r\nvar imageWidth = $('#slider img:first').width();\r\nvar captionHeight = $('#slider img:first').height();\r\n\r\n<span class=\"code_comment\">\/\/wrap the first image in divs for the slider\r\n<\/span>$('#slider img:first').wrap('&lt;div id=\"slider_box\"&gt;&lt;div id=\"slider_images\"&gt;').removeClass('active');\r\nvar countImages = 1;<span class=\"code_comment\">\/\/initialise an image counter\r\n<\/span><span class=\"code_comment\">\/\/for each remaining image, move it into the slider_images div, stacked up left to right, and unhide it\r\n<\/span>$('#slider &gt; img').each(function(){\r\n\t$(this).insertAfter('#slider_images img:last').css({'left':imageWidth*countImages+'px'}).show();\r\n\tcountImages++;\r\n});\t\r\n<span class=\"code_comment\">\/\/clone the last image and put it at the front\r\n<\/span>$('#slider_images img:last').clone().prependTo('#slider_images').css({'left':-1*imageWidth+'px'});\r\n<span class=\"code_comment\">\/\/clone the first image (now the second image after the previous operation) and put it at the end\r\n<\/span>$('#slider_images img:first').next().clone().appendTo('#slider_images').css({'left':countImages*imageWidth+'px'});\r\n\r\n<span class=\"code_comment\">\/\/wrap all the captions into divs for the slider - use wrapAll now as the captions are now adjacent in the DOM\r\n<\/span><span class=\"code_comment\">\/\/we don't need to position the captions absolutely as they'll naturally stack up vertically\r\n<\/span>$('.caption').wrapAll('&lt;div id=\"caption_box\"&gt;&lt;div id=\"slider_captions\"&gt;');\r\n$('div').removeClass('active');\r\n<span class=\"code_comment\">\/\/clone the last caption and put it at the front\r\n<\/span>$('#slider_captions .caption:last').clone().prependTo('#slider_captions');\r\n<span class=\"code_comment\">\/\/move the slider_captions div up so that the first caption is still visible\r\n<\/span>$('#slider_captions').css({'top':-1*captionHeight+'px'});\r\n<span class=\"code_comment\">\/\/clone the first caption (now the second after the previous operation) and put it at the end \r\n<\/span>$('#slider_captions .caption:first').next().clone().appendTo('#slider_captions');\r\n\r\n<span class=\"code_comment\">\/\/find the height of the content of each caption and apply a top padding to the first element so the caption is vertically centred\r\n<\/span>$('.caption').each(function(){\r\n\tvar contentHeight=0;\r\n\t$(this).children().each(function(){\r\n\t\tcontentHeight += $(this).outerHeight(true);\r\n\t});\r\n\t$(this).children().first().css({'paddingTop':0.5*(captionHeight-contentHeight)+'px'});\r\n});\t\r\n\r\ndoSlide = function(){<span class=\"code_comment\">\/\/recursive function that carries out the animation\r\n<\/span>\t\r\n\tvar newLeft = $('#slider_images').position().left-(1*imageWidth);<span class=\"code_comment\">\/\/calculate the new position which is the current position minus the width of one image\r\n<\/span>\t$('#slider_images').animate({'left':newLeft+'px'},'slow',function(){<span class=\"code_comment\">\/\/slide to the new position...\r\n<\/span>\t if (Math.abs(newLeft) == ((countImages)*imageWidth)) <span class=\"code_comment\">\/\/...and if the slider is displaying the last image, which is the clone of the first image...\r\n<\/span>\t\t{\r\n\t\t$('#slider_images').css({'left':0});<span class=\"code_comment\">\/\/...reset the slider back to the first image without animating \r\n<\/span>\t\t}\r\n\t });\r\n\t \r\n\tvar newTop = $('#slider_captions').position().top-(1*captionHeight);<span class=\"code_comment\">\/\/calculate the new position which is the current position minus the height of one caption\r\n<\/span>\t$('#slider_captions').animate({'top':newTop+'px'},'slow',function(){<span class=\"code_comment\">\/\/slide to the new position...\r\n<\/span>\t if (Math.abs(newTop) == ((countImages+1)*captionHeight)) <span class=\"code_comment\">\/\/...and if the slider is displaying the last caption, which is the clone of the first image...\r\n<\/span>\t\t{\r\n\t\t$('#slider_captions').css({'top':-1*captionHeight+'px'});<span class=\"code_comment\">\/\/...reset the slider back to the first caption without animating \r\n<\/span>\t\t}\r\n\t });\t\t \r\n\tsetTimeout(doSlide,4000);\t\r\n};\r\n\r\nsetTimeout(doSlide,4000);\r\n\t\r\n<\/pre>\r\n<h3>The logic<\/h3>\r\n<p>On load, only the image and the caption with class of <code>active<\/code> are displayed. Then I&#8217;m starting the code off by manipulating the html to stack all the images in a div, absolutely positioned so that they display in a horizontal row, and similarly with the captions to stack them in a column. Then I&#8217;m cloning the first and last images and the first and last captions and adding them back in to the html so that both appear to slide endlessly &#8211; I&#8217;ve used precisely the same approach as in the basic <a href=\"\/blog\/jquery-endless-slider\/\" title=\"Endless slider\">endless slider<\/a> . Then I&#8217;m simply animating both the image and the caption via <code>setTimeout<\/code><\/p>\r\n<p>I&#8217;ve tested in FF3\/5\/7, Opera 11, Chrome, Safari 5 and IE6\/7\/8\/9, all on Windows 7.<\/p>\r\n<h3>Can I use this on my site?<\/h3>\r\n<p>Probably. See my <a href=\"\/blog\/use-of-code-from-this-site\/\">policies on using code from this site<\/a>.<\/p>\r\n\r\n<script type=\"text\/javascript\" src=\"\/javascript\/image_caption_slider.js\"><\/script>\r\n\r\n\r\n","protected":false},"excerpt":{"rendered":"<p>Been thinking for a while about putting together a slider where the image animates left to right and the accompanying text animates up and down. I wanted a slider that would allow the following: Allow the html to be easily added &#8211; image and then caption as you would naturally add it Allow more than [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"open","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-1970","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/www.simonbattersby.com\/blog\/wp-json\/wp\/v2\/pages\/1970","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.simonbattersby.com\/blog\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/www.simonbattersby.com\/blog\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/www.simonbattersby.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.simonbattersby.com\/blog\/wp-json\/wp\/v2\/comments?post=1970"}],"version-history":[{"count":7,"href":"https:\/\/www.simonbattersby.com\/blog\/wp-json\/wp\/v2\/pages\/1970\/revisions"}],"predecessor-version":[{"id":2864,"href":"https:\/\/www.simonbattersby.com\/blog\/wp-json\/wp\/v2\/pages\/1970\/revisions\/2864"}],"wp:attachment":[{"href":"https:\/\/www.simonbattersby.com\/blog\/wp-json\/wp\/v2\/media?parent=1970"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}