$ is not a function but jQuery is loaded when adding JavaScript to a custom theme 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:
    js/script.js: {}
  dependencies:
    - 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().