How to Create a Slideshow with Ajax
See Ajax: Tips and Tricks for similar articles.Google Maps (http://maps.google.com) was one of the applications that brought so much attention to Ajax. One of the cool things about it is it allows the user to drag maps around the screen seamlessly loading new sections. It does this by preloading the sections around the map that the user is likely to drag on to the screen. This same concept can be applied to other applications, such as slideshows. Create a slideshow with Ajax in the following steps.
For these examples to work, more files are required than are available in this how-to. To see these examples in action, take the Ajax training course.
- First, take a look at the slideshow shown below.
When the user clicks the Previous or Next buttons, the page makes an
XMLHttpRequest
to the server, which returns XML as shown below: - The callback function creates the next slide from this XML. The code is shown below.
<!DOCTYPE HTML> <html> <head> <meta charset="UTF-8"> <title>Slide Show</title> <link href="SlideShow.css" type="text/css" rel="stylesheet"> <script type="text/javascript" src="lib.js"></script> <script type="text/javascript"> function prevSlide() { var curSlide = document.getElementById("CurSlideNum").innerHTML; getSlide(curSlide - 1); } function nextSlide() { var curSlide = Number(document.getElementById("CurSlideNum").innerHTML); getSlide(curSlide + 1); } function getSlide(curSlide) { var xmlhttp = new XMLHttpRequest(); var btnPrev = document.getElementById("PrevButton"); var btnNext = document.getElementById("NextButton"); btnPrev.disabled = true; btnNext.disabled = true; xmlhttp.open("get", "SlideShow?Slide=" + curSlide, true); xmlhttp.onreadystatechange = function() { if (xmlhttp.readyState == 4 && xmlhttp.status == 200) { changeSlide(xmlhttp); } } xmlhttp.send(null); function changeSlide(xmlhttp) { //Callback function creates slide var docElem = xmlhttp.responseXML.documentElement; var slideText = document.getElementById("SlideText"); var slideImage = document.getElementById("SlideImage"); var numSlides = document.getElementById("TotalSlideNum").innerHTML; var name, years; removeWhitespace(docElem, true); name = docElem.firstChild.firstChild.nodeValue; years = docElem.childNodes[1].firstChild.nodeValue; slideText.innerHTML = name + "<br>" + years; slideImage.src = "Slides/" + docElem.childNodes[2].firstChild.nodeValue; slideImage.alt = name; document.getElementById("CurSlideNum").innerHTML = curSlide; if (curSlide != 1) { btnPrev.disabled = false; } if (curSlide != numSlides) { btnNext.disabled = false; } } } observeEvent(window, "load", function() { var btnPrev = document.getElementById("PrevButton"); var btnNext = document.getElementById("NextButton"); var totalSlides = 10; observeEvent(btnPrev, "click", prevSlide); observeEvent(btnNext, "click", nextSlide); document.getElementById("TotalSlideNum").innerHTML = totalSlides; getSlide(1); }); </script> </head> <body> <h1>First 10 Presidents</h1> <div id="Slide"> <img id="SlideImage"> <div id="SlideText"></div> <hr> <button id="PrevButton">Previous</button> Slide <span id="CurSlideNum">1</span> of <span id="TotalSlideNum"></span> <button id="NextButton">Next</button> <hr> <div id="SlideMessage"></div> </div> </body> </html>
- Notice how the
changeSlide()
callback function changes the text of the slide and the image src and alt value based on the XML returned. Although this is pretty cool in and of itself, it can be made better by preloading the preceding and following images, so the user experiences no delay when navigating from slide to slide. In this case, the server-side script needs to return more data. Our script, the SlideShow response route from AjaxApplications/Demos/server.js, is shown below; the response is rendered via AjaxApplications/Demos/SlideShow.xml.jade
Notice that the SQL query will return records for the chosen president, the preceding president, and the following president. The resulting XML will look something like this:app.get('/SlideShow', function(req, res) { presidentsdb.serialize(function() { var Slide = req.param('Slide'); var sql = "SELECT FirstName, LastName, StartYear, EndYear, ImagePath FROM Presidents WHERE PresidentID=" + Slide; res.setHeader('Content-type', 'text/xml'); presidentsdb.all(sql, function(err, row) { if(err !== null) { res.status(500).send("An error has occurred -- " + err); } else { res.render('SlideShow.xml.jade', {presidents: row}, function(err, xml) { res.status(200).send(xml); }); } }); }); });
- Now we need to change the code to handle the preloaded slides and change the HTML to have hidden locations for the preloaded data. The code below shows how this is done.
Notice these two<!DOCTYPE HTML> <html> <head> <meta charset="UTF-8"> <title>Slide Show</title> <link href="SlideShow.css" type="text/css" rel="stylesheet"> <script type="text/javascript" src="lib.js"></script> <script type="text/javascript"> function prevSlide() { var curSlide = document.getElementById("CurSlideNum").innerHTML; getSlide(curSlide - 1, "prev"); } function nextSlide() { var curSlide = Number(document.getElementById("CurSlideNum").innerHTML); getSlide(curSlide + 1, "next"); } function getSlide(curSlide, slideSource) { var slideText = document.getElementById("CurSlideText"); var slideImage = document.getElementById("CurSlideImage"); var curSlideNum = document.getElementById("CurSlideNum"); var btnPrev = document.getElementById("PrevButton"); var btnNext = document.getElementById("NextButton"); var xmlhttp, url; btnPrev.disabled = true; btnNext.disabled = true; if (slideSource == "prev") { slideText.innerHTML = document.getElementById("PrevSlideText").innerHTML; slideImage.src = document.getElementById("PrevSlideImage").src; slideImage.alt = document.getElementById("PrevSlideImage").alt; curSlideNum.innerHTML = curSlide; message("Loading from Prev"); } else if (slideSource == "next") { slideText.innerHTML = document.getElementById("NextSlideText").innerHTML; slideImage.src = document.getElementById("NextSlideImage").src; slideImage.alt = document.getElementById("NextSlideImage").alt; curSlideNum.innerHTML = curSlide; message("Loading from Next"); } xmlhttp = new XMLHttpRequest(); url = "SlideShow-preloaded?Slide=" + curSlide; xmlhttp.open("get", url, true); xmlhttp.onreadystatechange = function() { if (xmlhttp.readyState == 4 && xmlhttp.status == 200) { changeSlide(xmlhttp); } } xmlhttp.send(null); function changeSlide(xmlhttp) { //Callback function creates slide var docElem = xmlhttp.responseXML.documentElement; var numSlides = document.getElementById("TotalSlideNum"); var prevSlidePos = (curSlide == 1) ? null : 0; var curSlidePos = (curSlide == 1) ? 0 : 1; var nextSlidePos = (curSlide == 1) ? 1 : (curSlide == 10) ? null : 2; removeWhitespace(docElem, true); if (prevSlidePos !== null) { prevSlideNode = docElem.childNodes[prevSlidePos]; loadSlide("Prev", prevSlideNode); btnPrev.disabled = false; } if (nextSlidePos !== null) { nextSlideNode = docElem.childNodes[nextSlidePos]; loadSlide("Next", nextSlideNode); btnNext.disabled = false; } if (slideSource == "ajax") { curSlideNode = docElem.childNodes[curSlidePos]; loadSlide("Cur", curSlideNode); message("Loading from Ajax"); } curSlideNum.innerHTML = curSlide; } } function loadSlide(slide, node) { var slideElem = document.getElementById(slide + "Slide"); var slideText = slideElem.getElementsByTagName("div")[0]; var slideImage = slideElem.getElementsByTagName("img")[0]; var name = node.firstChild.firstChild.nodeValue; var years = node.childNodes[1].firstChild.nodeValue; slideText.innerHTML = name + "<br>" + years; slideImage.src = "Slides/" + node.childNodes[2].firstChild.nodeValue; slideImage.alt = name; } function message(msg) { var output = document.getElementById("SlideMessage"); output.innerHTML = msg; } observeEvent(window, "load", function() { var btnPrev = document.getElementById("PrevButton"); var btnNext = document.getElementById("NextButton"); var totalSlides = 10; observeEvent(btnPrev, "click", prevSlide); observeEvent(btnNext, "click", nextSlide); document.getElementById("TotalSlideNum").innerHTML = totalSlides; getSlide(1, "ajax"); }); </script> </head> <body> <h1>First 10 Presidents</h1> <div id="PrevSlide"> <img id="PrevSlideImage"> <div id="PrevSlideText"></div> </div> <div id="CurSlide"> <img id="CurSlideImage"> <div id="CurSlideText"></div> <hr> <button id="PrevButton">Previous</button> Slide <span id="CurSlideNum">1</span> of <span id="TotalSlideNum"></span> <button id="NextButton">Next</button> <hr> <div id="SlideMessage"></div> </div> <div id="NextSlide"> <img id="NextSlideImage"> <div id="NextSlideText"></div> </div> </body> </html>
div
s in the HTML body.
These<div id="PrevSlide"> <img id="PrevSlideImage"/> <div id="PrevSlideText"></div> </div> <div id="NextSlide"> <img id="NextSlideImage"/> <div id="NextSlideText"></div> </div>
div
s are simply there to hold the incoming data. SlideShow.css has a commented out line that sets thedisplay
property of thesediv
s to"none"
, so the page appears as below: : - If you were to remove the comment marks, the previous and next slide
div
s would be hidden. The images in the upper corners are preloaded (by being in the browser cache, so that new slides load seamlessly). We do it this way for demonstrative purposes only. You could just as easily store the preloaded images in JavaScript variables. - In the JavaScript code, we have to know the source of the current slide, in other words, from where to get the data for the current slide:
- When the page first loads, we'll get the data using an Ajax call.
- When the Next button is clicked, we'll get the data from the "NextSlide"
div
. - When the Previous button is clicked, we'll get the data from the "PrevSlide"
div
.
- So now, when we call
getSlide()
, we will pass in the slide source:- "ajax" when the page first loads.
- "next" when the Next button is clicked.
- "prev" when Previous button is clicked.
- If the slide source is "prev" or "next," we load the current slide from the data in either the "Next" or "Previous"
div
based on which button was pushed:
Even if we do load the current slide from the "cache", we still need to make our Ajax call, to repopulate the "caching"if (slideSource == "prev") { slideText.innerHTML = document.getElementById("PrevSlideText").innerHTML; slideImage.src = document.getElementById("PrevSlideImage").src; slideImage.alt = document.getElementById("PrevSlideImage").alt; curSlideNum.innerHTML=curSlide; message("Loading from Prev"); } else if (slideSource == "next") { slideText.innerHTML = document.getElementById("NextSlideText").innerHTML; slideImage.src = document.getElementById("NextSlideImage").src; slideImage.alt = document.getElementById("NextSlideImage").alt; curSlideNum.innerHTML=curSlide; message("Loading from Next"); }
div
s. In thechangeSlide() callback
function, we do the following: - Determine the position of each slide within the XML nodes returned. Usually, three nodes get returned and the first (position 0 in JavaScript) is the data for the previous slide, the second (position 1) is the data for the current slide, and the third (position 2) is the data for the next slide. But this isn't the case, when the current slide is the first slide or the last slide. In either of these cases, only two XML nodes are returned. Our code has to know which slides they match:
var prevSlidePos = (curSlide == 1) ? null : 0; var curSlidePos = (curSlide == 1) ? 0 : 1; var nextSlidePos = (curSlide == 1) ? 1 : (curSlide == 10) ? null : 2;
- We then populate the "caching"
div
s if their position (as assigned above) is notnull
:if (prevSlidePos !== null) { prevSlideNode = docElem.childNodes[prevSlidePos]; loadSlide("Prev",prevSlideNode); btnPrev.disabled=false; } if (nextSlidePos !== null) { nextSlideNode = docElem.childNodes[nextSlidePos]; loadSlide("Next",nextSlideNode); btnNext.disabled=false; }
- We then only populate the current slide in the callback function if the slide source is "ajax." Otherwise, it was already populated from "cache":
if (slideSource == "ajax") { curSlideNode = docElem.childNodes[curSlidePos]; loadSlide("Cur",curSlideNode); message("Loading from Ajax"); }
Related Articles
- How to Make a Cross-origin Ajax Request
- How to Create a Login Form with Ajax
- How to Set Up Automatic Session Timeout with Ajax
- How to Use the Callback Function in Ajax
- How to Develop a Web Application with Ajax
- How to Make GET, POST, and HEAD Requests Using Ajax
- How to Use the jQuery ajax() Method
- How to Create a Lookup Form with Ajax
- How to Create a Slideshow with Ajax (this article)
- How to Handle the Response from the Server in Ajax
- How to Set Up for Ajax Training on Windows
- Inline Editing Using Ajax
- How to Create a Navigable Table with Ajax