I must have something set up incorrectly in my custom theme. jQuery is loading and works fine elsewhere on the same page in a views slideshow using jquery.cycle. My custom JavaScript however reports that $ is not a function.

The info.yml file includes the global-scripts property.

The libraries.yml file contains the following code.

    js/script.js: {}
    - core/jquery

The content of js/script.js is the following.

$(document).ready(function () {
    $(window).on('load scroll', function () {
        var scrolled = $(this).scrollTop();
        $('#layer-1').css('transform', 'translate3d(0, ' + -(scrolled * 0.25) + 'px, 0)'); // parallax (25% scroll rate)

The first line in script.js returns the error in the console. It is the only error I receive.

I checked the page source and confirmed that jQuery is being loaded before script.js at the bottom of the page below the footer.

Can anyone see what I have done wrong here?


The Drupal js documentation states

Note: Since Drupal uses jQuery.noConflict() and only loads JavaScript files when required, to use jQuery and the $ shortcode for jQuery you must include jQuery and Drupal as dependencies in the library definition in your MODULE.libraries.yml and add a wrapper around your function.

You should also use behaviors instead of document.ready() which is stated earlier in the above documentation.

However with Drupal there is an alternate better method [to document.ready()]; using the functionality of Drupal.behaviors and once(). If used properly this will ensure that your code runs both on normal page loads and when data is loaded by AJAX (or BigPipe!) - but not jQuery methods like load() which should be avoided as Drupal behaviors will fail to load for loading functions other than ajax().

So your snippet should look like:

(function ($, Drupal) {
  Drupal.behaviors.myModuleBehavior = {
    attach: function (context, settings) {
      $(window).once().on('load scroll', function () {
        // ...
})(jQuery, Drupal);

If your selector is something other than window, e.g. a class, you should update it to be something like $('.my-class', context).once().etc. Passing context like this ensures that your js only fires if the element exists in the currently being loaded context, i.e. if a small chunk of the page is loaded via ajax context will only contain that small chunk of the page. .once() ensures the js will only be added to the element once. It will prevent scenarios where you single click an element but it acts as though you double or triple clicked it. Here is the documentation for .once().

  • 2
    The linked page has the answer, but it's not related to document ready (although that needs fixing too). – rooby May 1 '18 at 3:38

It looks like I had a conflict with another module or something. I was able to resolve this issue by wrapping my code like this.

(function ($) {
    $(document).ready(function () {
        $(window).on('load scroll', function () {
            var scrolled = $(this).scrollTop();
            $('#layer-1').css('transform', 'translate3d(0, ' + -(scrolled * 0.25) + 'px, 0)'); // parallax (25% scroll rate)
  • 1
    That wouldn't work, the last line needs to be })(jQuery); – Clive May 1 '18 at 9:22

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.