1

I want to show a subsection of some projects on my webpage. I have an array of project names and some corresponding data in html files, like in the example below. I have a button with some preview-content in it, and if a user clicks on that it opens up the full container with all the project details.

To do this I though to loop through a sliced array in reverse and prepend the code to a div element on my webpage. The content often loads fine, but sometimes when I refresh the page it will display the previews in a different order than as defined in the array. Also, sometimes "previewtitle" and "previewcontent" return undefined, but not all the time. What am I doing wrong here?

I want to prepend the projects dynamically in the same order on every refresh.

var $projectNames = [ "coolprojecthere", "andanotherone", "moreprojects", "lastone" ];

projects/coolprojecthere.html looks like this:

<div id="title">Project title here</div>
<div id="content">Content html here</div>

projects/previews/coolprojecthere.html looks like this:

<div id="title">Project title here</div>
<div id="content">Content text here</div>

I loop through them like this to prepend them:

$projectNames = $projectNames.slice(0, 7);
    $.each($projectNames.reverse(), function (index, projectname) {
        var previewtitle, previewcontent;
        $.get("projects/previews/" + projectname + ".html", function (prevdata) {
            previewtitle = $(prevdata).filter('#title').text();
            previewcontent = $(prevdata).filter('#content').text();
        });
        $.get("projects/" + projectname + ".html", function (data) {
            var project = "<section id=\"project\" class=\"\"> \
                                        <t>" + previewtitle + "</t><br /> \
                                        <p>" + previewcontent + "</p> \
                                </div> \
                                <div class=\"content\"> \
                                    <h5>" + $(data).filter('#title').text() + "</h5> \
                                    <div class=\"content-container\">"
                                        + $(data).filter('#content').html() +
                                    "</div> \
                                </div> \
                            </section>"
            $('#main-container').prepend(project);
        });
appel
  • 517
  • 2
  • 7
  • 19
  • Ajax is **asynchronous**. You are making multiple requests at the same time and the responses may return in any order. See http://stackoverflow.com/questions/23667086/why-is-my-variable-undefined-after-i-modify-it-inside-of-a-function-asynchron – Felix Kling May 24 '14 at 08:55

2 Answers2

0

From the jQuery Documentation you must specify async option to be set to false to get a synchronous AJAX request.

$.ajax({ url: 'projects/previews/" + projectname + ".html', 
  async: false,
  dataType: 'json',
  success: function(data) {
     // your code here
    }
});

By default, all requests are sent asynchronously (i.e. this is set to true by default). If you need synchronous requests, set this option to false.

Otherwise, you could also do the jQuery's AJAX setup, and set it in synchronous mode by calling once (above your initial code):

jQuery.ajaxSetup({async:false});

It will perform all your further AJAX calls in synchronous mode. You can continue using jQuery.get(); method as you did before.

Note: Please read Felix Kling's answer, about when synchronous calls might be a bad idea.

Community
  • 1
  • 1
Ilia Ross
  • 13,086
  • 11
  • 53
  • 88
  • Thank you so much!! I was struggling with this for a while now and I had no idea why it kept changing the order. – appel May 24 '14 at 09:37
  • 1
    You should note that making a synchronous call is a bad idea in must cases, *especially* when multiple calls are involved. The browser will become unusable during the duration of all the calls. – Felix Kling May 24 '14 at 17:12
0

Making the Ajax calls synchronous is a bad idea. Instead, you should chain and nest them, so that each one is executed after each other.

Lets organize your code a little more, using promises:

function getPreviewData(projectName) {
  return $.get(
    "projects/previews/" + projectname + ".html"
  ).then(function(prevdata) {
    return {
      title: $(prevdata).filter('#title').text(), 
      content:  $(prevdata).filter('#content').text()
    };
  );
}

function getProjectHTML(projectName, previewTitle, previewContent) {
  return $.get("projects/" + projectname + ".html").then(function (data) {
    // ...
  });
}

$projectNames = $projectNames.slice(0, 7);
var head = new $.Deferred();
var chain = head.promise();

$.each($projectNames.reverse(), function (index, projectName) {
  chain = chain.then(function() {
    return getPreviewData(projectName).then(function(previewData) {
      return getProjectHTML(projectName, previewData.title,  previewData.content);
    });
  });
});

head.resolve();
Community
  • 1
  • 1
Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
  • Thanks for the info, I've updated my code accordingly. Though I think the bit `var chain = chain.promise();` should be `var chain = head.promise();` right? – appel May 25 '14 at 08:55
  • Yep... tossed some variable names around and missed that one. – Felix Kling May 25 '14 at 08:56