quickshiftin quickshiftin - 1 month ago 17
Javascript Question

RequireJS sporadic failure

I'm working on a Magento2 site. Magento2 uses requirejs and the theme I'm using makes use of it as well. Part of the theme uses Owl Carousel. The code used to load the carousel was originally

require(['jquery', 'owl.carousel/owl.carousel.min'], function($) {
$("#banner-slider-demo-1").owlCarousel({
// carousel settings
});
});


However I noticed sometimes when I load the homepage (where the carousel is used) the carousel doesn't display, and there are errors in the console


Uncaught TypeError: $(...).owlCarousel is not a function


Thinking jQuery might not be loaded, I changed the require code to serialize the requirements

require(['jquery'], function($) {
require(['owl.carousel/owl.carousel.min'], function () {
$("#banner-slider-demo-1").owlCarousel({
// ...


but this had no effect... Sometimes the carousel loads and there are no errors, other times there are errors and it doesn't load.

Even when there is an error the carousel file has been fetched by the browser. It also seems require has loaded the carousel script per developer tools

Developer Tools

Any idea what could be going on?

Answer

Your issue is that you are loading jQuery through a script element and through RequireJS. You have to use one method, not both.

If I run this in the console:

console.log("owlCarousel", typeof jQuery.fn.owlCarousel)

I get:

owlCarousel function

But with this:

require(["jquery"], function ($) { 
  console.log("owlCarousel", typeof $.fn.owlCarousel); 
})

I get:

owlCarousel undefined

And if you try require(["jquery"], function ($) { console.log("equal", $ === window.jQuery); }) you'll get equal false. So you have two instances of jQuery loaded. The code that uses RequireJS to access jQuery gets a version without .owlCarousel. That's because Owl Carousel installed itself on window.jQuery.

If you must load jQuery through script for some reason, you should move the script element that loads it before you load RequireJS. (You should do this for all scripts you load that you want to load with script and that are AMD-aware.) Then for RequireJS, you should just define this fake module:

define("jquery", function () {
    return jQuery;
});

This makes it so that when RequireJS loads jQuery it just uses the jQuery already defined in the global space. The ideal place for this fake module would be just before you configure RequireJS.

While you're at it you should define a shim for owl.carousel/owl.carousel.min:

`owl.carousel/owl.carousel.min`: ['jquery']

This way there's no need to nest the call to load owl.carousel/owl.carousel.min inside a call to load jquery. You can just do what you were trying initially:

require(['jquery', 'owl.carousel/owl.carousel.min'], function($) {
  $("#banner-slider-demo-1").owlCarousel({
    // carousel settings
  });
});
Comments