-->

requireJS with JQuery, Masonry, & ImagesLoaded: Ob

2019-03-20 13:17发布

问题:

RequireJS newbie here. Trying to convert some JQuery code I had working fine in the old way to work w/ RequireJS.

Header of my page loads three JS files via script tags -- require.js itself, my require.cfg.js, and boot/main.js with the site-specific functionality.

Relevant require.cfg.js excerpt:

,paths: {
    'boot': 'src/boot'
    ,'jquery': 'lib/jquery.min'
    ,'jquery.masonry': 'lib/plugins/masonry.pkgd.min'
    ,'jquery.imagesloaded': 'lib/plugins/imagesloaded.pkgd.min'
}

,shim: {
    'jquery': {
        exports: 'jQuery'
    }
    ,'jquery.masonry': ['jquery']
    ,'jquery.imagesloaded': ['jquery']
}

boot/main.js:

require([
'jquery',
'jquery.masonry',
'jquery.imagesloaded',
], function($) {

    // The following code worked just fine when I included it in the header of the page as-is
$(function() {

    var $container = $('#container');
    // This doesn't work
    $container.imagesLoaded(function() {
                    // Neither does this
            $('#container').masonry({itemSelector : '.item',});
        });

});

});

I can confirm that all of these JS files are being found and loaded by the browser. And I confirm that if I do:

require([
'jquery',
'jquery.masonry',
'jquery.imagesloaded',
], function($, Masonry, ImagesLoad) {

the Masonry and ImagesLoaded variables are set correctly.... but I don't want to proceed w/o jQuery

But when I try to call .imagesLoaded() and .masonry() on the JQuery container object, I get:

Uncaught TypeError: Object [object Object] has no method 'imagesLoaded'

And if I comment out the imagesLoaded line, I get:

Uncaught TypeError: Object [object Object] has no method 'masonry'

Not sure what I'm doing wrong here...? From what I've read in other StackOverflow questions, the code looks correct to me...?

Thanks!

Update:

If I use this code the non-JQuery way like so, it works:

        var container = document.querySelector('#container');
        imagesLoaded(container, function() {
            var msnry = new Masonry( container, {
                itemSelector: '.item',
            });
        });

回答1:

Try defining exports for each plugin in the shim too...

, paths: {
    boot: 'src/boot'
    , jquery: 'bower_components/jquery'
    , masonry: 'bower_components/masonry',
    , imagesloaded: 'bower_components/imagesloaded'
}
, shim: {
    jquery: {
        exports: 'jQuery'
    }
    , masonry: {
        deps : ['jquery'],
        exports : 'jQuery.masonry'
    }
    , imagesloaded: {
        deps : ['jquery'],
        exports : 'jQuery.imagesLoaded'
    }
}


回答2:

Try this instead:

require([
'jquery',
'jquery.masonry',
'jquery.imagesloaded',
], function($, Masonry, ImagesLoad) {

    // The following code worked just fine when I included it in the header of the page as-is
$(function() {

    var $container = $('#container');
    // This doesn't work
    $container.imagesLoaded(function() {
                    // Neither does this
            $('#container').masonry({itemSelector : '.item',});
        });

});

});


回答3:

Ok, I think I have the answer to your problem (and mine!).

If you're installing the versions hosted on github, you're probably installing the "vanilla" version, not the jquery plugin. That's what you'll be doing if you install via bower, for example, which is what I was doing.

Here's what I found with a search on bower:

% bower search masonry
# jquery-masonry git://github.com/desandro/masonry
# masonry git://github.com/desandro/masonry.git
# $.masonry git://github.com/tysoncadenhead/masonry
# angular-masonry git://github.com/passy/angular-masonry.git
# jquery.masonry git://github.com/tysoncadenhead/masonry.git

AFAICT, jquery-masonry, $.masonry and jquery.masonry are all pointing to the same source (in two different locations), and it's not the jquery plugin it's just the "vanilla" version of masonry.

Instead, just download from here and extract the file jquery.masonry.js, but it in your assets path, and add a shim for it with a dependency on jquery.

After you do all that, it should work. Of course since it's not installed through bower, you can't manage dependencies as you would with other bower packages. I honestly don't understand what's going on but it looks like a problem on the package-maintainer's side.

Hope that helps.

UPDATE: Although the above works, note that the version downloaded from meta.metafizzy.co is quite old and depends on an outdated version of jquery. I think I'm just going to go with the vanilla version, it seems that the plugin is not being maintained.



回答4:

Try using jquery-bridget: https://github.com/desandro/jquery-bridget to convert masonry to a jquery plugin. I created a new js file that gets loaded by requirejs to ensure it is converted before the application starts running:

//masonry-config.js:
'use strict'

define(['jquery-bridget', 'masonry'], function(Bridget, Masonry){
    Bridget('masonry', Masonry );
});   

Then in my requirejs file

//main.js
require.config({
   paths: {
       'masonry-config': 'masonry-config'
       .....
   },
   shim: {  
       'angular-masonry': ['angular', 'masonry'],
       'angular' : {
         deps: ['imagesloaded', 'masonry-config'],
         exports: 'angular'
       },
       'masonry': ['imagesloaded'],
   } 
}

This is for my app using angular and angular-masonry (with masonry) so you might need to config a little differently but hopefully that give you some idea.



回答5:

In our particular case we had RequireJS included and configured in our main template, but wanted to include Masonry/ImagesLoaded on one particular page.

We kept our code in the template:

<script type="text/javascript" src="/path/to/require.min.js"></script>
<script type="text/javascript">
    require.config({
        waitSeconds: 0,
        paths: {
            'jquery': "/path/to/jquery.min",
            // ...
        },
        shim: {
            // ...
        }
    });
    require(["jquery", /* ... */], function ($) {
        // ...
    });
</script>

Then we included a JavaScript file after this, on the page that used Masonry, that triggered Masonry/ImagesLoaded:

requirejs(['jquery', 'https://unpkg.com/masonry-layout@4.2.2/dist/masonry.pkgd.min.js', 'https://unpkg.com/imagesloaded@4.1.4/imagesloaded.pkgd.min.js'],
    function ($, Masonry, ImagesLoaded) {
        $(document).ready(function () {
            ImagesLoaded('.js-masonry', function () {
                new Masonry('.js-masonry', {
                    // This was required for us, but from what I can tell shouldn't need to be/may need to be updated per-usage.
                    itemSelector: '.item'
                });
            });
        });
    }
);

I'm sure this could be further optimized, but it resolved the errors and didn't require any new packages to be included.