Daksh
Daksh

Reputation: 986

Template code creates exponential number of elements

I noticed this strange behaviour with my jQuery code, when it pulled a JSON from an AJAX request to create the elements with a template. What I am trying to do is, with the JSON, which is technically an array of few objects, I am constructing a series of cards, which show something to the user.

I have drilled down the problem to the below code (without using AJAX) and got the issue replicated here as well.

$(function() {
  // Mimicking the AJAX Request.
  // The getJSON gets an array of objects.
  // I am limiting it to just string array, and lenght of 3 to keep things simple.
  var response = ["Alice", "Bob", "Charlie"];
  // My Code.
  $.each(response, function(index, value) {
    $tmp = $(".template").clone();
    $tmp.find("strong").text("Name");
    $tmp.find("span").text(value);
    $tmp.appendTo("body");
  });
});
* {
  margin: 0;
  padding: 0;
  list-style: none;
  text-align: center;
}
.template {
  border: 1px solid #ccc;
  border-radius: 5px;
  padding: 5px;
  width: 150px;
  line-height: 150px;
  display: inline-block;
  cursor: pointer;
  margin: 5px;
}
<script src="https://code.jquery.com/jquery-2.1.4.js"></script>
<div class="template">
  <strong>All</strong>
  <span>Names</span>
</div>

Problem

If you see in the above snippet, you could find that the elements are exponentially increasing. i.e., for the nth element, it displays the cards for n times, starting from one:

n   card        desc
--- ---------- -----------------------------------
1   1 card      Alice is displayed only once.
2   2 cards     Bob is displayed two times.
3   4 cards     Charlie is displayed four times.

And so on... I could find an amusing series here, which resembles the powers of 2: 1, 2, 4, 8, 16... (20, 21, 22, 23, 24,... Exponential on 2).

Another strange behaviour that I noticed was, when I use an extra <div>, this problem doesn't happen. For e.g., consider the same code, but wrap the template by nesting it into another <div> structure:

$(function() {
  // Mimicking the AJAX Request.
  // The getJSON gets an array of objects.
  // I am limiting it to just string array, and lenght of 3 to keep things simple.
  var response = ["Alice", "Bob", "Charlie"];
  // My Code.
  $.each(response, function(index, value) {
    $tmp = $(".extra .template").clone();
    $tmp.find("strong").text("Name");
    $tmp.find("span").text(value);
    $tmp.appendTo("body");
  });
});
* {
  margin: 0;
  padding: 0;
  list-style: none;
  text-align: center;
}
.template {
  border: 1px solid #ccc;
  border-radius: 5px;
  padding: 5px;
  width: 150px;
  line-height: 150px;
  display: inline-block;
  cursor: pointer;
  margin: 5px;
}
<script src="https://code.jquery.com/jquery-2.1.4.js"></script>
<div class="extra">
  <div class="template">
    <strong>All</strong>
    <span>Names</span>
  </div>
</div>

I am not sure what exactly is causing the problem here. Is there any crazy mistake that I am doing? :)

Upvotes: 2

Views: 78

Answers (1)

Praveen Kumar Purushothaman
Praveen Kumar Purushothaman

Reputation: 167192

The reason is, you are not removing the class template, so every time you append, you have double the number of elements with the class template, so the first way to do is, when you do the clone, you need to remove the class template.

$tmp = $(".template").clone().removeClass("template");

Note: In the below preview I have slightly modified the class, because of styling.

$(function() {
  // Mimicking the AJAX Request.
  // The getJSON gets an array of objects.
  // I am limiting it to just string array, and lenght of 3 to keep things simple.
  var response = ["Alice", "Bob", "Charlie"];
  // My Code.
  $.each(response, function(index, value) {
    // ----------- Change here ---------- //
    $tmp = $(".template").clone().removeClass("template");
    $tmp.find("strong").text("Name");
    $tmp.find("span").text(value);
    $tmp.appendTo("body");
  });
});
* {
  margin: 0;
  padding: 0;
  list-style: none;
  text-align: center;
}
.item {
  border: 1px solid #ccc;
  border-radius: 5px;
  padding: 5px;
  width: 150px;
  line-height: 150px;
  display: inline-block;
  cursor: pointer;
  margin: 5px;
}
<script src="https://code.jquery.com/jquery-2.1.4.js"></script>
<div class="item template">
  <strong>All</strong>
  <span>Names</span>
</div>

The reason why, second time it worked is, you had enclosed the template inside the extra div, and you are appending the new .templates to the <body> directly, so the selector .extra .template selects only one. Still this is a mistake because each and every <div> you generate bears the template class, which ideally shouldn't.

Hope the explanation is clear.

Upvotes: 2

Related Questions