6

I am using Packery.js [ http://packery.metafizzy.co/ ] on a site and am having a few layout issues on page load. I have looked at the documentation for Masonry, Isotope, and Packery (all very similar plugins, all developed by David DeSandro) and it discusses addressing issues where the layout should be triggered after all images are loaded as well as any webfonts.

http://packery.metafizzy.co/appendix.html

I have Packery working just fine with imagesLoaded... but am not sure how to tie in the Google Web Font loader with that. Below is my code for loading fonts, followed by the imagesLoaded Packery Layout. If any one could suggest a way to have Packery fire after both Web Fonts and imagesLoaded, I would be eternally grateful.

// before <body>
<script type="text/javascript">
WebFontConfig = {
    google: {
        families: [ 'Bitter:400,700,400italic:latin' ]
    },
    typekit: {
        id: // hidden for obvious reasons
    }
};
(function() {
    var wf = document.createElement('script');
    wf.src = ('https:' == document.location.protocol ? 'https' : 'http') + '://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js';
    wf.type = 'text/javascript';
    wf.async = 'true';
    var s = document.getElementsByTagName('script')[0];
    s.parentNode.insertBefore(wf, s);
})();
</script>

// after </body>
// jquery and plugins are loaded prior to the following code
jQuery(document).ready(function($) {

    var $container = $('section.home-page main');

    imagesLoaded( $container, function() {

        $container.packery({
            itemSelector: 'article',
            columnWidth: '.grid-sizer',
            gutter: '.gutter-sizer'
        });

    });

});
beefchimi
  • 1,222
  • 2
  • 15
  • 34

3 Answers3

3

This works perfectly.

<script>
  function loadIsotope(){
      var $container = $('section.home-page main');

      imagesLoaded( $container, function() {

          $container.packery({
              itemSelector: 'article',
              columnWidth: '.grid-sizer',
              gutter: '.gutter-sizer'
          });
      });
  }

  WebFont.load({
    google: {
      families: ['Bitter']
    },
    active: function() {
        // This event is triggered when the fonts have rendered.
        loadIsotope();
    },
    inactive:function(){
        // This event is triggered when the browser does not support
        // linked fonts or if none of the fonts could be loaded.
        loadIsotope();
    }
  });
</script>
Chuck Le Butt
  • 43,669
  • 58
  • 179
  • 268
3

It is all about creating the best algorithm to solve your problems , and this requires robust analysis of the problem , then finding all possible solutions , and finally apply the best solution.

The problem :

  • Creating a Masonry effect for your page, which depends on the load of different unrelated elements (webfonts, and images), or in other terms which depends on the asynchronous loading of unrelated page elements.

Possible Algorithms :

  • Load an item (webfont, or images first), and on success load of first item , start to load the other

  • Use Flags, and invoke both load functions asynchronously , based upon returned flags, the developer can decide what to do.

First Algorithm is wrong for sure

  • As in a series call if one call fails for any reason, rest of calls will not take place, because it depends on another item load callback.

  • Time taken to load item will be longer cause events are taking place in a series way, not in a parallel way.

Second algorithm seems ok

Solution :

Referring to both docs for webfontloader , imagesLoaded

  1. Create two flag variables

    var imagesAreLoaded = false , fontsAreLoaded = false

  2. Set each flag true on a successful callback for load event

for webfontloader if you want to give highest priority to the font that will be used only in your packery plugin then you should use fontactive, if the plugin depends on more than one family you better use active :

WebFontConfig = {  
  active: function() { // set fontsAreLoaded to true here }, 
  fontactive: function(familyName, fvd) { // set fontsAreLoaded to true here } 
};

WebFontConfig is the object you pass to webfontloader load method.

  1. Do same for imagesLoaded event

    $('#container').imagesLoaded()
    .done( function( instance ) { //set imagesAreLoaded to true here })

please note that imagesLoaded jQuery plugin returns a jQuery Deferred object. This allows you to use .always(), .done(), .fail() and .progress(), similarly to the emitted events.

  1. Usually everything should run fine, and usually fonts will load before the images will, but a good developer should handle all possible scenarios.

    $(document).load(function() {
    // if both flags are true , go ahead and load your masonry. // else if either of flags is false, show a warning, retry loading , or whatever you think is better })

Note : My answer doesn't really provide much coding , because from what i think the documents for both plugins are very clear , but i think the issue was how to handle the problem using correct algorithm.

Chuck Le Butt
  • 43,669
  • 58
  • 179
  • 268
ProllyGeek
  • 14,423
  • 5
  • 44
  • 69
  • 2
    Excellent answer @ProllyGeek , very well explained. I'm having a hard time electing the "correct" answer, as Dennis builds upon your breakdown of the problem and provides the code. I want to award you both... – beefchimi Mar 17 '15 at 15:52
  • @beefchimi thanks for your nice comment :) ,i just wanna say im always after questions with 0 answers or not clear answers ,just to help whoever did not get a proper help ,when i first came to this , there was only chuck answer ,and it was not clear ,and it was also built upon a series type solution which is totally not correct ,i see that people have edited their answers ,and added new answers inspired from my answer , anyway ,it doesnt really matter who deserves the bounty ,what matters that im glad i could help ,and people believe that i could provide a very well explained answer ,cheers :) – ProllyGeek Mar 17 '15 at 17:01
  • This is only fractionally more "asynchronous" than the other version listed here. Images are loaded automatically by the browser, they don't wait for the `ImagesLoaded` to be called. Same goes for the fonts. The timing of both versions will be therefore be almost identical in all situations. – Chuck Le Butt Mar 18 '15 at 11:41
  • 1
    @ProllyGeek There's many mistakes in this answer. The first "algorithm" isn't even an option -- it would require preventing the browser from doing what it does automatically -- so why even list it? You continue this same misunderstanding throughout your answer. (But your final solution is useful.) – Chuck Le Butt Mar 18 '15 at 11:43
2

Just like ProllyGeek said, async solution is more reasonable. Code could look something like this (I tested – it works):

<script>
var loadedImages = false;
var loadedFonts = false;

function loadedEverything() {
        return (loadedImages && loadedFonts);
}

function loadMasonry() {
        console.log("Loading masonry");
}

WebFontConfig = {
        google: {
                families: ['Droid Sans']
        },
        active: function() {
                loadedFonts = true;
                if (loadedEverything()) loadMasonry();
        }
};

imagesLoaded( document.querySelector('.container'), function(instance) {
        loadedImages = true;
        if (loadedEverything()) loadMasonry();
});
</script>

Whatever loads slower will fire the event you need.

Denis Mysenko
  • 5,667
  • 19
  • 30
  • Thanks so much for completing this @Dennis Mysenko ! EDIT: Both you and ProllyGeek deserve the winning answer, and I originally gave it to you for providing the code, but have reconsidered and have it to ProllyGeek for his explanation of the problem and how to solve it. – beefchimi Mar 17 '15 at 15:56
  • @beefchimi You are welcome! :) But as far as I see – his answer is chosen as accepted, not mine? :-) – Denis Mysenko Mar 17 '15 at 16:00
  • 1
    Dang tried to edit my comment but ran out of time. I originally gave it to you for providing the code, but have reconsidered and awarded it to ProllyGeek. Stackoverflow encourages a "detailed canonical answer is required to address all the concerns", and it would be unfair for me to ignore ProllyGeek's contribution. Honestly, you both deserve the golden ticket. Hope I haven't broke any SO rules... – beefchimi Mar 17 '15 at 16:03
  • @DenisMysenko good answer Denis , but providing a code was not hard for me , it is better to teach people how to cook , rather than offering them a meal :) . – ProllyGeek Mar 17 '15 at 17:07
  • @beefchimi why is chuck offering a bounty if it is your question ? – ProllyGeek Mar 17 '15 at 17:08