9

I have an Isotope gallery (version 2) with almost 400 elements. A typical gallery item looks like this:

<div class="element hybrid a" data-category="hybrid">
    <p class="type">H</p>
    <h2 class="name">Name</h2>
    <p class="strain-info">No Info Available</p>
    <p class="review"><a class="button radius small black review-form-lb" href="#review-form-lightbox">Review</a></p>
</div>

For instance when I run the code below, which basically adds a class to the element clicked, it takes several seconds for the element to enlarge.

$container.on( 'click', '.element', function() {
  $( this ).toggleClass('large');
  $container.isotope('layout');
});

Another example is if I have a button group that contains several options to filter the gallery, again it takes several seconds. Filter JS:

$('#filters').on( 'click', '.button', function() {
  var $this = $(this);
  // get group key
  var $buttonGroup = $this.parents('.button-group');
  var filterGroup = $buttonGroup.attr('data-filter-group');
  // set filter for group
  filters[ filterGroup ] = $this.attr('data-filter');
  // combine filters
  var filterValue = '';
  for ( var prop in filters ) {
    filterValue += filters[ prop ];
  }
  // set filter for Isotope
  $container.isotope({ filter: filterValue });
});

Here is the variable for $container

// init Isotope
var $container = $('.isotope').isotope({
  itemSelector: '.element',
  layoutMode: 'fitRows',
  filter: function() {
    return qsRegex ? $(this).text().match( qsRegex ) : true;
  }
});

I should note that the above code works great when there is a small number of items. How could I improve the performance of the gallery?

I should note that in version 1 of Isotopes I have the same gallery and it works fine. I am upgrading because of enhanced abilities of v2 of isotopes.

Here is the live site - IMPORTANT! - This site is for a marijuana dispensary in Colorado, USA. Website may not be appropriate for work.

Update

I tried changing all divs to li elements. Didn't see much improvement.

I tried turning on hardware acceleration per mfirdaus's answer but it didn't appear to work.

Note: The above link to the live site is a stripped down version where I removed everything not required for the Isotope gallery. It is slightly faster than when I originally posted this, however, it needs to be more responsive. Try using it on a tablet and it takes several seconds for the Isotope item to respond.

Update 2

Version One of Isotopes performs much better with a large gallery and I ended up using version one because of this.

Community
  • 1
  • 1
L84
  • 45,514
  • 58
  • 177
  • 257
  • Not 100% sure on this, thus a comment for now. But you are using `
    ` elements. Which makes sense to an extent. But in my experience, galleries & things like this should be using an unordered list structure with `
      ` as a wrapper with each element inside it being an `
    • `. Perhaps you can try that & see if it helps?
    – Giacomo1968 May 25 '14 at 03:47
  • @JakeGould - I wouldn't think using a `div` over an `li` element would matter much but I have been surprised before. => I will give it a try. – L84 May 25 '14 at 04:07
  • Yup. That is part of the reason my suggestion is a comment rather than an answer. Feel free to not be impressed if it does not work. But it struck me as odd. – Giacomo1968 May 25 '14 at 04:33
  • @JakeGould - Changing to `li` elements didn't work but it saved a few bytes of data at the least. => – L84 May 25 '14 at 23:42
  • Have you tried to force 'animationEngine' option only to css? Here a discussion about max items https://github.com/metafizzy/isotope/issues/191 so De Sandro suggest 100 items. another way is to extend isotope with pagination or hide/show item based on user scrolling – keypaul May 29 '14 at 16:22
  • @keypaul - I will look at that. Thank You. Also the idea for showing items based on scrolling is something I considered but I have filtering and I am unsure how to combine the scrolling + filters. Any thoughts on that? – L84 May 29 '14 at 17:36
  • Here a good article http://css-tricks.com/slide-in-as-you-scroll-down-boxes/ , you need to use as selector, one that match your filters. Also to improve performance on scroll events you can use RAF for debouncing here a good article http://www.html5rocks.com/it/tutorials/speed/scrolling/ I've done, in a little site, isotope + pagination (without extend isotope) but only cause the client wanted ( is not a real interaction/usability improvement ... also boring ) – keypaul May 29 '14 at 21:05
  • @keypaul - I have been working with this more and wanted to point out that animateOptions has been removed in version 2 of isotopes. On the [Appendix](http://isotope.metafizzy.co/appendix.html#upgrading-from-v1) page it says this:`animationOptions removed. jQuery animation removed. This means no animation for IE8 and IE9.` – L84 Jun 11 '14 at 00:21

4 Answers4

14

I have a suggestion. First of all, you can add the css property transform:translate3d(0,0,0) to your .element css selector. This turns on hardware acceleration and should speed up the page reflow. This is a tip often used to make highly animated pages smoother, especially on mobile. so something ilke:

.element {
  ...
  /* add this. prefixes for compabtibility */
  transform:translate3d(0,0,0);
  -webkit-transform:translate3d(0,0,0);
  -moz-transform:translate3d(0,0,0);
}

In my tests it seems to speed it up quite nicely.

Another suggestion would be to replace .on("click") with .on("mousedown"). click fires after the mouse is released whereas mousedown would fire immediately after the user press their mouse. there is like a ~0.2s difference but it's somewhat noticable.

Here's an example made from isotope's default example which has 400 elements.


Update

After testing out touchstart, it kinda helps but it fires even when scrolling. So this helps only if you disable scroll/have fixed viewport. Maybe you could add iScroll, but I reckon that's even more bloat.

One option you could try is to force an element to resize first, before waiting for isotope to recalculate the positions. We can do this by using setTimeout on the isotope refresh. Not sure if the behaviour is acceptable but this will allow the user to get feedback faster. So your toggling code will be something like:

$container.on( 'click', '.element',function() {
  $( this ).toggleClass('large');
  setTimeout(function(){ $container.isotope('layout'); },20); //queue this later
});

Demo. Tested this on my android device and there seemed to be an improvement.

mfirdaus
  • 4,574
  • 1
  • 25
  • 26
  • Unfortunately that did not work for me. It appears to slightly speed up on the desktop but on mobile it does little, if anything. Thank you for the tips. – L84 May 26 '14 at 01:47
  • I guess there was one more thing you could try that I had in mind I didn't put as my example seemed fast enough. I'll update my answer. – mfirdaus May 26 '14 at 03:02
  • As a note: I awarded the bounty to this answer because it did the most for solving the issue though it didn't solve it completely. With that said, I believe reducing the numbers and implementing the solutions above will solve the issue. => – L84 Jun 01 '14 at 03:58
  • Just tested this solution and it has remarkably improved the speed of the Isotope for me. Along with reducing the size of the content (smaller images) this should be fairly quick to execute now for me. Thanks. – Lovelock Dec 29 '14 at 11:39
1

Well, the author of the isotope plugin recommends 50 items max if you're doing filtering or sorting. You have 400!.. So I'm afraid you'll need to decrease the num. of items for better performance.

Some side notes:

  • Try touchstart or mousedown events instead of click for a bit more quick responses on mobile.
  • Check for Object.keys(filters).forEach performance on mobile (instead of for in)..
  • jQuery's closest() method might be faster than parents() in some browsers.
Community
  • 1
  • 1
Onur Yıldırım
  • 32,327
  • 12
  • 84
  • 98
  • 1
    for fast click on devices it shouldn't change the click but use https://github.com/ftlabs/fastclick – rob.m Jan 16 '15 at 10:58
1

Instead of using toggleClass, try directly manipulating the style.

$( this ).css({ /* styles for large */});
Michael Benin
  • 4,317
  • 2
  • 23
  • 15
0

While I understand that this may not be an issue directly related to your scenario, I had the same experience (mostly on mobile and tablet devices) where I had only 36 images in an Isotope gallery, and I was waiting anywhere from 3-5 seconds for the reflow to occur.

In my case it was due to the fact that I had multiple (3) event listeners on the 'resize' event. Bad move! The resize event is fired several hundred times as it keeps firing until the user stops resizing the screen.

It also turns out that scrolling on iOS devices fires the resize event as well, so I had a resource intensive function (cloning a complex mega menu and rebuilding it for mobile devices) queuing hundreds of times, thus causing all js related tasks to lockup or become intensely slow.

The solution in my case was to implement a debounce function to debounce the functions attached to the 'resize' event, thus firing them once and only when the resize is complete.

For what it's worth, I adapted the Underscore.js debounce function so that I could use it locally and outside of Underscore.js, which was borrowed from this blog post by David Walsh. Here is an example of how I implemented it:

  var debounce = function(func, wait, immediate) {
    var timeout;
    return function() {
        var context = this, args = arguments;
        var later = function() {
            timeout = null;
            if (!immediate) func.apply(context, args);
        };
        var callNow = immediate && !timeout;
        clearTimeout(timeout);
        timeout = setTimeout(later, wait);
        if (callNow) func.apply(context, args);
    };
};

var checkWindowWidthForNav = debounce(function(e) {
    sdWidth = Math.max(document.documentElement.clientWidth, window.innerWidth || 0);
    if(sdWidth <= 990){
        window.simulateMobileNav = true;
        sdMobileSnapMenus();
    }else{
        window.mobileScaffoldingDone = false;
        window.simulateMobileNav = false;
        sdMobileSnapMenus();
        $("[data-action=\"hover-toggle\"]").bind("mouseenter", function(e){
            var $target = $(e.currentTarget).attr("data-target");
            var $hide = $(e.currentTarget).attr("data-hide");
            $($hide).hide();
            $($target).show();
        });
        $(".subMenuWrapper:visible").hide();
        $(".subMenuWrapper").bind("mouseleave", function(e){
            $(".subMenuWrapper").fadeOut(500, function(){
                $(".sdSubMenu .sdHideFilters, #sdArchitecturalFilters, #sdInteriorFilters, #sdUrbanFilters").hide();
            });
            return false;
            e.preventDefault();
            e.returnValue = false;
        });
        $(".sdMainMenu > ul li a[data-action], .sdSubMenu a[data-action]").bind("click", function(e){
            e.preventDefault();
            e.returnValue = false;
            return false;
        });
        $(".sdMainMenu > ul .sdSubMenu").hide();
    }

}, 600); // Debounced function occurs 600ms after event ends

window.addEventListener("resize", checkWindowWidthForNav, false);

Debouncing my events resulted in a DRAMATIC increase in speed for all JS related events. I no longer had the Isotope lag, my reflow was instant.

This may help someone who was in the same boat I was in.

SimonDowdles
  • 2,026
  • 4
  • 24
  • 36