0

Prior to this revision, I was trying to duplicate one specific node among a few others but I was having undesired duplications

var data = {
    "id": "1",
    "username": "user5613506",
    "data": {
        "items": [
            {
                "id": 1,
                "name": "Item #1",
                "picture": "assets/images/thumbnails/item1.jpg",
                "group": "group1",
                "amount": 2,
            },
            {
                "id": 2,
                "name": "Item #2",
                "picture": "assets/images/thumbnails/item2.jpg",
                "group": "group2",
                "amount": 3,
            }
        ]
    }
};

var index = 0;

$( 'button' ).click( function() {
  
  index++;

  if( index >= data.data.items.length ) index = 0;

  var $elements = $( '#elements' );
  var $icon = $elements.children( 'img.' + data.data.items[ index ].group ).slice( 0, 1 );

  $elements.children( 'img' ).addClass( 'hidden' );

  for( var i = 0; i < data.data.items[ index ].amount; i++ ) {
    $icon.clone().removeClass( 'hidden' ).appendTo( $elements );
  }
});
.hidden { display: none; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="elements">
  <img src="http://i.imgur.com/TuHUIAC.gif" class="group1 hidden" />
  <img src="http://i.imgur.com/dUeBcoU.png" class="group2 hidden" />
</div>

<button>Click Me</button>

Also on JSFiddle ;)

With some comments below the code is not based on the classes available anymore (see revisions if you're curious ^_^).

In the older versions when the <button> was clicked the first time, and thus the pointer defined by the index variable was pointing to the second entry of the JSON, everything was working fine.

When the button was clicked one more time, instead of hide/remove the elements previously added, the new ones were being added along of them. Because of this, ever consecutive click was accumulating nodes, breaking the whole layout with the progressive duplication.

Now, before anything, I hide all nodes by adding the hidden class to them all. Then, with a more efficient set of selectors, I find the only image with a specific class that matches some value in the JSON and then I clone it as many times as need, removing the hidden class before append it to the parent node.

However, and now comes the new issue, for now I'm having to use jQuery.slice() to get the first and only the first matching node to serve as base for cloning because, although working, all nodes are still in the DOM when they're hidden.

This is not a problem in this example, with only two entries in the JSON, but when that entry becomes larger, maybe with hundreds of items and, for some odd reason, the user decide to click indefinitely the button, the DOM might get too crowded, which might crash the browser with a memory overflow.

How could I solve that part then?

user5613506
  • 286
  • 1
  • 16
  • As you duplicate the entire entry include classes, you will get multiple matches and therefore multiple copies. PS. It should be `.elements`, not `#element` in your example. – iCollect.it Ltd Dec 06 '16 at 15:19
  • Without the json-part, which we do not know. You clone image3 with the first click. After that, there should be two images with class "image3". So clicking the button again should give you 4 images of class "image3". – Seb Dec 06 '16 at 15:20
  • Instead of looping through all the images, why not hide them all, and then -find- image3 and do your clone work with it? – Taplar Dec 06 '16 at 15:25
  • @Seb, the JSON part is not really relevant because in the code abstraction above, only two things are dynamic, coming from the JSON: The value used by `indexOf()` and the number of iterations. – user5613506 Dec 06 '16 at 15:40
  • It's slightly confusing that you are talking about working with JSON and yet your example is doing nothing with JSON. How are we to provide helpful advice if you do not provide an accurate example of your issue? – Taplar Dec 06 '16 at 15:41
  • There, I extracted part of the JSON and adjusted the example. By the way, thank you Gone, I was referencing a class with an ID in the markup. Too bad that wasn't the real problem >. – user5613506 Dec 06 '16 at 15:59
  • Then what is your problem? What do you not understand or what is not working? You're cloning an element and the next time the clone is used too? (Is it possible to use different images, just to tell them apart?) – Seb Dec 06 '16 at 16:02
  • [Here](https://jsfiddle.net/a9wqLk89/1/). In the first click `index` is equal to **1**, so it points to *Item #2* and adds 3 red dots because the entry **amount** is equal to 3. In the second click, `index` is higher than the length, so it goes back to zero and do the same with the first group, now adding two blue dots. But the previously added red dots are still there and the issue goes over and over with every new click. I can't just put a simple `empty()` because that would remove any other "clonable" image that I removed to simplify a bit. ImgUr was out-of-service before. – user5613506 Dec 06 '16 at 16:24
  • I got it working now, except for one small detail if you could help me with this this topic revision – user5613506 Dec 07 '16 at 12:09

2 Answers2

0

Only target hidden elements for the copy and then remove that class from the clones only:

$('button').click(function() {
  $(".elements img.hidden").each(function() {
    var $this = $(this);
    if ($this.hasClass('image3')) {
      for (var i = 1; i < 3; i++) {
        $this.clone().removeClass('hidden').appendTo($this.parent());
      }
    }
  });
});

JSFiddle: https://jsfiddle.net/TrueBlueAussie/689qn979/1/

iCollect.it Ltd
  • 92,391
  • 25
  • 181
  • 202
0

Not the most elegant solution or with the best performance since the appendTo() still inside the loop, but removing all elements with a specific class defined during the cloning process did the trick:

var data = {
    "id": "1",
    "username": "user5613506",
    "data": {
        "items": [
            {
                "id": 1,
                "name": "Item #1",
                "picture": "assets/images/thumbnails/item1.jpg",
                "group": "group1",
                "amount": 2,
            },
            {
                "id": 2,
                "name": "Item #2",
                "picture": "assets/images/thumbnails/item2.jpg",
                "group": "group2",
                "amount": 3,
            }
        ]
    }
};

var index = 0;

$( 'button' ).click( function() {
  
  index++;

  if( index >= data.data.items.length ) index = 0;

  var $elements = $( '#elements' );
  var $icon = $elements.children( 'img.' + data.data.items[ index ].group ).slice( 0, 1 );

  $elements.find( '.cloned' ).remove();

  $elements.children( 'img' ).addClass( 'hidden' );

  for( var i = 0; i < data.data.items[ index ].amount; i++ ) {
    $icon.clone().removeClass( 'hidden' ).addClass( 'cloned' ).appendTo( $elements );
  }
});
.hidden { display: none; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="elements">
  <img src="http://i.imgur.com/TuHUIAC.gif" class="group1 hidden" />
  <img src="http://i.imgur.com/dUeBcoU.png" class="group2 hidden" />
</div>

<button>Click Me</button>
user5613506
  • 286
  • 1
  • 16