Magento 2 JavaScript Guide
It is necessary to mention RequireJS when it comes to JavaScript in Magento 2. The platform utilizes RequireJS for optimizing time necessary to load a page which contains JavaScript files. Besides, the RequireJS loader is used for managing JS resource dependencies and for customizing JavaScript in Magento 2. All the aforementioned processes are discussed in this Magento 2 JavaScript Guide through the following sections:
- Magento 2 JavaScript resources configuration
- Magento 2 JS initialization
- Magento 2 JavaScript customization
- Locating JS components in Magento 2
- How to customize Magento 2 jQuery widget
Table of contents
Magento 2 JavaScript resources configuration
If you are looking for the best tool able to make your Magento 2 website interactive and dynamic, pay attention to JavaScript. Unfortunately, pages with JS in headers essentially slow down your website. Therefore, this method is not used within the Magento platform. Another JavaScript performance improvement in Magento 2 is based on the ability to utilized the RequireJS library, which allows loading JS in the background as well as enables asynchronous loading.
As you might have guessed, it is necessary to configure all Magento 2 JavaScript resources which are used in your themes and modules. Please note that it is important to avoid editing JavaScript resources which belong to other themes and modules.
All Magento 2 JS resources are specified as follows:
- lib/web is a library level used in case of libraries in the code base of Magento 2.
- <module_dir>/view/<areaname>/web is a module level utilized in case of libraries related to a module.
- <theme_dir>/<VendorName>_<ModuleName>/web is a theme level used for all Magento 2 libraries in a theme.
- <theme_dir>/web is a level of all libraries in a single theme.
Please note that it is not recommended to use <theme_dir>/web to specify Magento 2 JavaScript resources. At the same time, you should specify them via templates, since you ensure processing Magento 2 JavaScript resources in a body of a page.
There are two types of IDs used for JavaScript resources in Magento 2:
- RequireJS ID
1 2 3 4 |
// Regular ID require(["jquery"], function($){ // ... }); |
- Magento modular ID
1 2 3 4 |
// Modular ID (Magento module: Magento_ConfigurableProduct, resource: js/configurable) require(["magento!Magento_ConfigurableProduct::js/configurable"], function(Configurable){ // ... }); |
As you can see, there is a magento! prefix in the Modular ID. The ID itself is utilized for loading JS modules. As for the ID Normalizer plugin (there is such an improvement in Magento 2), it is utilized for converting the Magento 2 modular IDs into the file paths. In their turn, these paths are used in RequireJS for loading JS modules.
Magento 2 JavaScript resources and dependencies between them
Specifying dependencies between JavaScript resources in Magento 2 is automated via the use of plugin. As a result, you only need to specify them there, and all necessary dependencies will be picked up automatically.
Thus, Magento 2 significantly simplifies your daily routine by introducing the ability to use the
The following rules should be observed to declare a plugin properly:
1. Leverage the define function:
1 2 3 4 |
define(["jquery"], function($){ // plugin code // where $ == "jquery" })(jQuery); |
2. For various environments, specify your plugins as follows:
1 2 3 4 5 6 7 8 9 10 11 12 |
(function (factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module. define(['jquery'], factory); } else { // Browser globals factory(jQuery); } }(function ($) { // plugin code // where $ == jQuery })); |
3. In case your plugin is going to have a dependency on third-party plugins, don’t forget to specify a
- For the requirejs-config.js configuration file, specify it in the following way:
1 2 3 4 5 |
var config = { "shim": { "3-rd-party-plugin": ["jquery"] } }; |
- For the {third-party-plugin}.js configuration file, specify it in the following way:
1 2 3 4 |
!(function($){ // plugin code // where $ == jQuery })(jQuery); |
Magento 2 RequireJS library and its configuration
Now, it is necessary to make the RequireJS library available in your Magento 2 instance. Open your layout.xml file and specify the library as well as other RequireJS configurations there in the following way:
1 2 3 4 5 6 7 8 9 10 |
<referenceBlock name="head"> <block class="Magento\Theme\Block\Html\Head\Script" name="requirejs" before="-"> <arguments> <!-- RequireJs library enabled --> <argument name="file" xsi:type="string">requirejs/require.js</argument> </arguments> </block> <!-- Special block with necessary config is added on the page --> <block class="Magento\RequireJs\Block\Html\Head\Config" name="requirejs-config" after="requirejs"/> </referenceBlock> |
JS resources mapping in Magento 2
The next step of your Magento 2 JavaScript resources configuration requires using the requires-config.js file to create the mapping and assigning aliases to resources. As a result, you can configure Magento 2 JavaScript resources more precise at several levels. The following order is used while collecting and executing configurations:
- Magento 2 library configurations.
- Magento 2 module level configurations.
- Magento 2 theme module level configurations aimed at your ancestor themes.
- Magento 2 theme module level configurations aimed at your current theme.
- Theme level configurations aimed at your ancestor themes.
- Theme level configurations aimed at your current theme.
Module notations or relative paths are used in Magento 2 as well. They expand opportunities introduced with standard RequireJS library aliases. So it is necessary to specify relative paths to JS resources, which belong to the module and the theme module levels, in RequireJS configurations. The following examle illustrates the situation:
1 2 3 4 5 6 |
var config = { paths: { // configuration for resource 'app/code/Magento/Catalog/view/frontend/product/product.js' "product": "./product/product" } }; |
Where ./product/product is a relative path to Magento 2 JS resources of the Catalog module. Please note that it is not necessary to specify any baseUrl parameters, since they are generated automatically.
Adjusting RequireJS in Magento 2
Magento 2 offers several ways for adjusting RequireJS. For further information, check this post: Advanced Development with RequireJS.
Magento 2 JS initialization
If you are wondering how to initialize JavaScript components in Magento 2, you’ve came to the right place. The following section of our Magento 2 JavaScript Guide sheds light on two topics:
- Magento 2 JS components initialization via a PHTML template
- jQuery widget initialization via a JS script.
It is highly recommended to avoid using inline JavaScript for the Magento 2 initialization.
Magento 2 JS components initialization via a PHTML template
Magento 2 offers two different approaches to a JS component initialization via a .phtml template. The first one utilizes the data-mage-init attribute. The second one is based on the <script> tag.
data-mage-init
You can utilize the data-mage-init attribute to work with a certain HTML element while initializing a JS component on it. The following code illustrates how a Magento 2 JavaScript component is initialized via the <nav/> element:
1 |
<nav data-mage-init='{ "<component_name>": {...} }'></nav> |
As a result, the script is called just for a particular element while initialized on it and is not initialized automatically for other elements of this type on the same page of Magento 2.
On DOM ready, the the system parses the data-mage-init attribute for extracting components’ names and configuration and applying them to the element. Although there are several types of the JavaScript component initialized in Magento 2, processing is performed in the following way:
- In a situation when an object is returned, the initializer is looking for the appropriate <component_name> key. The corresponding value can be a function. Consequently, the config and element values are passed by the initializer to the function as follows:
1 2 3 |
return { '<component_name>': function(config, element) { ... } }; |
- In a situation when a function is returned, the config and element values are passed by the initializer to the function as follows:
1 |
return function(config, element) { ... }; |
- Furthermore, there can be a situation when both a function and an object with the appropriate “<component_name>” key are not returned. Then, the jQuery prototype is examined by the initializer in order to find “<component_name>”. In case of success (the key is found), it is applied as $(element).<component_name>(config):
1 2 |
$.fn.<component_name> = function() { ... }; return; |
- If the aforementioned cases are not true, use the <script> tag.
<script>
Magento 2 JavaScript initialization on an HTML element is possible without any direct access or relation to the element , via the <script type=”text/x-magento-init”> tag:
1 2 3 4 5 6 7 8 9 10 11 |
<script type="text/x-magento-init"> // components initialized on the element defined by selector "<element_selector>": { "<js_component1>": ..., "<js_component2>": ... }, // components initialized without binding to an element "*": { "<js_component3">: ... } </script> |
If you are not familiar with the components used below, they are:
- <element_selector> contains an element’s selector on which the following JavaScript component is initialized.
- <js_component1> and <2> illustrate JavaScript components initialized on the element with the selector specified above via <element_selector>.
- <js_component3> shows JavaScript component which is initialized without binding to any element.
The following example illustrates widget initialization in Magento 2 via the <script> method. The #main-container selector is used to initialize the accordion and navigation widgets on the element, while pageCache is initialized without binding to any element.
1 2 3 4 5 6 7 8 9 |
<script type="text/x-magento-init"> "#main-container": { "navigation": <?php echo $block->getNavigationConfig(); ?>, "accordion": <?php echo $block->getNavigationAccordionConfig(); ?> }, "*": { "pageCache": <?php echo $block->getPageCacheConfig(); ?> } </script> |
Magento 2 JS Widget initialization
As you can see, the JavaScript initialization of a widget in Magento 2 is based on the following principle:
1 |
$("#main-container").accordion(); |
In case of a complex widget which contains options, the following notation should be utilized:
1 2 3 4 5 6 |
$("#main-container").accordion({ header : "#title-1" content : "#content-1", trigger : "#trigger-1, ajaxUrlElement: "a" }); |
Magento 2 JavaScript customization
The following section of our Magento 2 JavaScript Guide shows how to utilize custom JavaScript components. Below, we shed light on how to use them with components provided by Magento.
It is highly recommended to avoid changing the source code of both default Magento components and widgets. Consequently, all your Magento 2 JavaScript customizations must be performed in custom modules or themes only.
Replacing a default JavaScript component in Magento 2
The following how-to illustrates the usage of a Magento 2 JavaScript component’s custom implementation:
1. First of all, you should place the custom component source file in <theme_dir>/web/js (theme JS files) or <module_dir>/view/frontend/web/js ( module view JS files)
2. Now, create a ne requirejs-config.js file that specifies the following:
1 2 3 4 5 6 7 |
var config = { "map": { "*": { "<custom_component>": "<default_component>" } } }; |
- <default_component> indicates a default component y showing its name; you are going to replace this component;
- <custom_component> shows the name you custom JavaScript component.
Imagine the situation when you should replace the default menu widgets with a custom nav-menu.js script. In this situation, your requirejs-config.js file should look as follows:
1 2 3 4 5 6 7 8 |
var config = { "map": { "*": { "menu": "js/nav-menu", "mage/backend/menu": "js/nav-menu", } } }; |
3. Please note that your requirejs-config.js should be placed in <theme_dir> or <module_dir>/view/frontend according to the location of your custom script you’ve chosen in the first step.
Extending a default JavaScript component in Magento 2
To extend a default component/widget in Magento 2, add a custom JavaScript. In case of default jQuery widget, create a new <your_widget_name>.js file which contain the following content:
1 2 3 4 5 6 7 8 9 10 |
define([ 'jquery', 'jquery/ui', 'mage/<widget.name>' // usually widget can be found in /lib/web/mage dir ], function($){ $.widget('<your_namespace>.<your_widget_name>', $.mage.<widget.name>, { ... }); return $.<your_namespace>.<your_widget_name>; }); |
Where:
- <your_namespace>.<your_widget_name> is the name of your Mageto 2 custom widget (must contain both namespace and name).
- mage.<widget.name> is the name of the default Magento widget you are going to extend.
In case of a default JS Ui component, you should create a custom script that contain the following code:
1 2 3 4 5 6 7 8 9 10 |
define([ '<component_path>' ], function(<component_alias>){ return <component_alias>.extend({ defaults: { ... }, // properties with default values ... // methods of your component }); }); |
Where:
- <component_path> illustrates a path to the default component that you are going to extend;
- <component_alias> is a variable that contains the same default component.
For instance, you are going to use the Filters.js script to extend the default filters.js. Your custom script should look like this one:
1 2 3 4 5 6 7 8 9 10 |
define([ 'Magento_Ui/js/grid/filters/filters' ], function(Filters){ return Filters.extend({ defaults: { ... }, // properties with default values ... // methods of your component }); }); |
Disabling default JavaScript in Magento 2
If you are going to disable the default Magento 2 JavaScript components auto-loading and widget initialization, use the following algorithm:
1. Create a new requirejs-config.js file which contains the following data:
1 2 3 4 |
var config = { deps: [ ] }; |
2. Put it in<theme_dir> or in <module_dir>/view/frontend.
To enable the aforementioned processes on a certain stage, enhance your JavaScript file with the following code:
1 |
$(mage.apply); |
Locating JS components in Magento 2
With the aid of the following section of this Magento 2 JavaScript Guide, you will be able to learn how to define which JS components and widgets are used on a particular page of your Magento 2 website.
Perform the following steps for locating scripts utilized for a certain element:
1. Go to your browser and open a page of your Magento 2 store. Locate the element’s class or id with the help of browser’s debugging tools. You can use Firebug, Inspect Element, or any other solution.\
2. Now, it is necessary to select the view of the page source.
3. Look for the corresponding element in source. You should find out if it, its children, or parents have data-mage-init or <script type=”text/x-magento-init”> calls.
4. Then, get the source file of the script:
- Go to the <head></head> section, find link to requirejs-config.js file and click it. There is the Magento 2 RequireJS configuration in the file. It is collected from all modules related to the current theme.
- Now, it’s necessary to perform some work in the requirejs-config.js’ var config = {…} section. First of all, you should find the required script name and then view the path to the appropriate source file. The relativeness to certain directories depends on the availability of the module reference: 1) In case of the unspecified module context, the path is relative to your current theme (<theme_dir>/web). If the system does not find the file there, it searches for it in a parent theme web directory and then in a library directory. 2) In case of the specified module context, the path is relative to your current theme module (<theme_dir>/<Namespace>_<Module>/web). If there are no file there, the system looks for it in the same location related parent theme files and then in a module directory (<module_dir>).
How to customize Magento 2 jQuery widget
The following section is the final section of this Magento 2 JavaScript guide. It discusses such important processes as jQuery widget customization and its usage instead the default solution introduced in Magento 2. Let’s start with the customization.
jQuery widget customization
The following steps illustrate how to remove the message displayed on the product page when you hover over images:
- Define the output method of the message.
- Extend the default script with the aid of the custom one.
- Update RequireJS configuration.
1. Define the output method of the message
- Open your product page in a browser.
- View the source of the page.
- Find your string which contains the displayed message. For instance “Click on image to view…” The illustration of the search result follows:
- Find out how it is output. For instance by gallery.js.
Thus, your script needs to alter gallery.js. You can get its path by referring to the requirejs-config.js file. Let’s consider that mage/gallery is the required path.
2. Extend the default script with the aid of the custom one
Now, you can add orange-gallery.js with the following content to app/design/OrangeCo/orange/web/js:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
define([ 'jquery', 'jquery/ui', 'mage/gallery' ], function($){ $.widget('orange.gallery', $.mage.gallery, { _create: function() { // special method of jQuery UI Widgets this._setOption('controls', {'notice': {}}); } }); return $.orange.gallery; }); |
3. Update RequireJS configuration
as well as add the content listed below to app/design/OrangeCo/orange/requirejs-config.js:
1 2 3 4 5 6 7 |
var config = { "map": { "*": { "gallery": "js/orange-gallery" } } }; |
Reload the store page to see the changes.
Custom widget usage
Below, we illustrate how to set up the jCarousel widget. The new improvement will add the ability to display various product images on different product pages:
- Define the default way product images are displayed.
- Enhance your file system with the custom script.
- Update your RequireJS configuration.
1. Define the default way product images are displayed.
Define, how your product images are displayed and what is their configuration path. Use the aforementioned method.
2. Enhance your file system with the custom script
To create a “wrapper” script, add the following content to app/design/OrangeCo/orange/web/js:
- jquery.jcarousel.js – a source file of jCarousel;
- orange-carousel.js – a wrapper with:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
define([ 'jquery', 'js/jquery.jcarousel' ], function($){ return function (config, element) { var jCarouselConfig = { list: '.items.thumbs', items: '.item.thumb' }; $(element).jcarousel(jCarouselConfig); } }); |
3. Update your RequireJS configuration
Go to the app/design/OrangeCo/orange directory and place the requirejs-config.js file with the following content there:
1 2 3 4 5 6 7 8 9 10 |
var config = { "map": { "*": { "gallery": "js/orange-gallery" } }, "shim": { "js/jquery.jcarousel": ["jquery"] // as jquery.jcarousel isn't an AMD module } }; |
For further information on JavaScript in Magento 2, examine the official developer documentation: