1

I'd like to produce a list of links for a sidebar by querying a DB for unique properties in one of the columns of a table. I intend to make these links then query for all entries sharing that value.

I am using the $.getJSON() method to acquire the data and edit the DOM. The only way I know to work with a JSON array within the $.getJSON() method is to run an $.each() on it, since for(i=0;i<object.key.length();i++) statements don't seem to work within the anonymous callback function.

My JSON object is structured as follows

{"M" : [
  {"category" : "value1"},
  {"category" : "value2"},
  {"category" : "value3"}],
"Q" : [
  {"category" : "same shit"},
  {"category" : "different heading"}]
}

and the problematic segment of my code is as follows

$sidebar.html("<h1>Catalog</h1>");
$.getJSON("script.php", function(data) {
  $sidebar.append("<h2>Manufacturing</h2><ul>"); // open the <ul> tag
  $.each(data.M, function(i, row) {
    $sidebar.append("<li><a>" + row.category + "</a></li>");
  });
  $sidebar.append("</ul>"); // close out the <ul> tag AFTER the $.each
});

This makes the HTML of my sidebar takes the form of

<h1>...</h1>
<h2>...</h2>
<ul>...</ul>
<li><a>...</a></li>
<li><a>...</a></li>
<li><a>...</a></li>

Which I find incredibly annoying. I've been trying to apply the $.promise().done() strategy as is suggested in these three posts asking exactly this question, but none of those askers were working with an $.each() that exists inside a PHP-calling function. I could not cause the script to construct the unordered list in the correct sequence. It keeps closing the <ul> tag before appending any of the list items.

I've chained both .done() and .promise().done() to the $.each, to the $sidebar I'm appending, and even to the $.getJSON() itself just to see if maybe that would work (even though it working would not help me get to the point of adding more arrays into the script and finishing it).

I'm quite frustrated, as answers to the three referenced posts all insist that a $.each() should finish before moving to the next line unless said $.each() involves animation or an AJAX call which take non-negligible times to complete. My application has neither of these gotchas, yet seems to be running asynchronously anyway.

In this JFiddle you can see that the tags appended by the each do not fall within a <ul> tag, as the CSS would make it green instead of red.

Community
  • 1
  • 1
  • 4
    *"for(i=0;iManufacturing
      "` is invalid HTML.
    – tymeJV Jan 13 '15 at 19:05
  • 1
    jQuery is probably correcting your html where you open the `ul` tag. You should build a string instead of appending partial html. Then, at the end, you append the string. More efficient too. – jeroen Jan 13 '15 at 19:07

2 Answers2

7

Problem is you're calling .append() and appending invalid HTML. The browser won't allow invalid html to be inserted into the dom, so it closes the <ul> for you, and you'll be appending your <li> outside of the <ul>.

You should create your <ul> separately, append the new <li> to that, then insert the finished <ul> into the dom.

  $sidebar.append("<h2>Manufacturing</h2>");
  ul = $('<ul>');
  for(...) {
     ul.append('<li>...</li>');
  }
  $sidebar.append(ul);
Marc B
  • 356,200
  • 43
  • 426
  • 500
5

You can definitely run a for loop inside an anonymous callback, yours probably failed because length is a property, not a method. Also, your HTML is butchered because you append partial elements to the DOM:

<h2>Manufacturing</h2><ul>

Is invalid, the ul needs to be closed:

$sidebar.html("<h1>Catalog</h1>");
$.getJSON("script.php", function(data) {
    $sidebar.append("<h2>Manufacturing</h2>"); // open the <ul> tag -- NO
    var ul = "<ul>";

    //for (var i = 0; i < data.M.length; i++)
    $.each(data.M, function(i, row) {
        ul += "<li><a>" + row.category + "</a></li>";
    });

    ul += "</ul>";
    $sidebar.append(ul);
});
tymeJV
  • 103,943
  • 14
  • 161
  • 157
  • If this was the answer you chose, @MichaelDoughty, then this should be marked as the correct answer. – dckuehn Jan 13 '15 at 19:36