Thursday, June 14, 2018

Isotope & javascript - elements aren't clickable anymore after adding a new one

Leave a Comment

I'm trying to make a table of images loaded from a json (not a real json, more like a javascript array) and every time the json changes (when I append a new image to the json file with some script, I want that my table of images to upload as well.

This is the json format:

[{ "image": "images/set_1_UTC+03.jpg", "weight": 101 }, { "image": "images/set_1_UTC+03.jpg", "weight": 102  }, { "image": "images/set_1_UTC+03.jpg", "weight": 103 }, {   "image": "images/set_1_UTC+03.jpg", "weight": 104 }] 

For this I use isotope. I managed to achieve everything I mentioned above, the only problem is that I wanted to make the images clickable and whenever I click one of the image, to have it in a bigger size and when I click it again to get back to the small size. Here is the code:

<script> var previous = 0; var current = 0; loadJSON(function(response) {  // Parse JSON string into object   current = JSON.parse(response); }); function loadJSON(callback) {   var xobj = new XMLHttpRequest();       xobj.overrideMimeType("application/json");   xobj.open('GET', 'data.json', true); // Replace 'my_data' with the path to your file   xobj.onreadystatechange = function () {         if (xobj.readyState == 4 && xobj.status == "200") {           // Required use of an anonymous callback as .open will NOT return a value but simply returns undefined in asynchronous mode           callback(xobj.responseText);         }   };   xobj.send(null); } let lengthOfprevious = previous.length;    setInterval(function() {   loadJSON(function(response) {    // Parse JSON string into object     current = JSON.parse(response);   });   previous = current;   if (lengthOfprevious!=current.length){     UpdateBody(lengthOfprevious);   }   lengthOfprevious = previous.length; }, 5000);  function UpdateBody(startIndex) {   var newElements = "";   for (let i = startIndex; i < previous.length; i++) {     $(document).ready(function () {           newElements = "";           newElements +=                 '<div class="photo element-item">' +                 '<a href="' + previous[i].image +'"><img class="small-image" src="'+previous[i].image+'"/></a>' +                 '<a class="weight">'+previous[i].weight+'</a></div>';         var $newElems = $(newElements);          $('#container').append($newElems).imagesLoaded(function () {              $('#container').isotope('insert', $newElems);         });     });   } // ============//   $(function(){     var $container = $('#container'),     $photos = $container.find('.photo'),     $loadingIndicator = $('<div class="loading"><span><img src="http://i.imgur.com/IE7iw.gif" /></span></div>');     // trigger Isotope after images have loaded     $container.imagesLoaded( function(){       $container.isotope({         itemSelector : '.photo',         masonry: {           columnWidth: 200         }       });     });     // shows the large version of the image     // shows small version of previously large image     function enlargeImage( $photo ) {       $photos.filter('.large').removeClass('large');       $photo.addClass('large');       $container.isotope('reLayout');     }      $photos.find('a').click( function() {       var $this = $(this),       $photo = $this.parents('.photo');        if ( $photo.hasClass('large') ) {         // already large, just remove         $photo.removeClass('large');         $container.isotope('reLayout');       } else {         if ( $photo.hasClass('has-big-image') ) {           enlargeImage( $photo );         } else {           // add a loading indicator           $this.append( $loadingIndicator );            // create big image           var $bigImage = $('<img>', { src: this.href });            // give it a wrapper and appended it to element           $('<div>', { 'class': 'big-image' })           .append( $bigImage )           .appendTo( $this )           .imagesLoaded( function() {             $loadingIndicator.remove()             enlargeImage( $photo );           });            // add a class, so we'll know not to do this next time           $photo.addClass('has-big-image');          }       }        return false;     });    }); } </script> 

The problem is that after setInterval runs once, everything works as expected, when it runs again, the images aren't clickable anymore. If I move the part after the // ============// tag in the for, only the last image is clickable.

I cannot figure out the solution for this (I'm a beginner with javascript). Can someone point me in the right direction?

Update Here you can an archive of the project so that one can run it locally. https://wetransfer.com/downloads/45bf4d0625517a39918060b1075fec1b20180613112617/f7e638

You need to manually copy and pasteduplicate some lines from the data file in order for the demo to work because it searches for the difference of the previous state and the current state of the data file. The problem is that it is clickable at first, and then it's not anymore.

2 Answers

Answers 1

I found the couple of things need to be modified in your code:

  1. why $(document).ready re-iterate more than 1 time
  2. no need to use of $(function()
  3. As I see the photo class is being generated dynamically on run time and container class is already in DOM, so i have modified click event

    $container.on('click', '.photo a', function() { 

Edit please note code is not tested, i have just code corrected it.

Final Updated Code is following :

var previous = 0; var current = 0; loadJSON(function(response) {     // Parse JSON string into object     current = JSON.parse(response); });  function loadJSON(callback) {     var xobj = new XMLHttpRequest();     xobj.overrideMimeType("application/json");     xobj.open('GET', 'data.json', true); // Replace 'my_data' with the path to your file     xobj.onreadystatechange = function() {         if (xobj.readyState == 4 && xobj.status == "200") {             // Required use of an anonymous callback as .open will NOT return a value but simply returns undefined in asynchronous mode             callback(xobj.responseText);         }     };     xobj.send(null); } let lengthOfprevious = previous.length;  setInterval(function() {     loadJSON(function(response) {         // Parse JSON string into object         current = JSON.parse(response);     });     previous = current;     if (lengthOfprevious != current.length) {         UpdateBody(lengthOfprevious);     }     lengthOfprevious = previous.length; }, 5000);  function UpdateBody(startIndex) {     var newElements = "",         $container = $('#container'),         $photos = $container.find('.photo'),         $loadingIndicator = $('<div class="loading"><span><img src="http://i.imgur.com/IE7iw.gif" /></span></div>');      $(document).ready(function() {         for (let i = startIndex; i < previous.length; i++) {             newElements = "";             newElements +=                 '<div class="photo element-item">' +                 '<a href="' + previous[i].image + '"><img class="small-image" src="' + previous[i].image + '"/></a>' +                 '<a class="weight">' + previous[i].weight + '</a></div>';              var $newElems = $(newElements);              $container.append($newElems).imagesLoaded(function() {                 $container.isotope('insert', $newElems);             });         }         $container.imagesLoaded(function() {             $container.isotope({                 itemSelector: '.photo',                 masonry: {                     columnWidth: 200                 }             });         });         // shows the large version of the image         // shows small version of previously large image         function enlargeImage($photo) {             $photos.filter('.large').removeClass('large');             $photo.addClass('large');             $container.isotope('reLayout');         }          $container.on('click', '.photo a', function() {             var $this = $(this),                 $photo = $this.parents('.photo');              if ($photo.hasClass('large')) {                 // already large, just remove                 $photo.removeClass('large');                 $container.isotope('reLayout');             } else {                 if ($photo.hasClass('has-big-image')) {                     enlargeImage($photo);                 } else {                     // add a loading indicator                     $this.append($loadingIndicator);                      // create big image                     var $bigImage = $('<img>', {                         src: this.href                     });                      // give it a wrapper and appended it to element                     $('<div>', {                             'class': 'big-image'                         })                         .append($bigImage)                         .appendTo($this)                         .imagesLoaded(function() {                             $loadingIndicator.remove()                             enlargeImage($photo);                         });                      // add a class, so we'll know not to do this next time                     $photo.addClass('has-big-image');                  }             }              return false;         });      }); } 

Answers 2

the problem in $photos.find('a').click( - this bind click to existing items, so new its does not fire click. You should you event delegation&

$('.items_wrap_class').on('click', 'a', function() { /* all existings an new items clicks will fire this */ })

UPD

In your case do delegate #container .photo a click handling.

replace $photos.find('a').click( function() { ... })

with

$('#container').on('click', '.photo a', function() { ... })

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment