Different Ways of Using JavaScript in Magento 2

This post will demonstrate the different ways of using JavaScript in Magento 2, specifically adding them through various methods that Magento provides. There is also the ability to extend of override existing JavaScript. Here will show you how.

To include local and external JavaScript in layout XML similar to Magento 1, you can edit your theme’s default_head_blocks.xml layout file.

// app/design/frontend/[Vendor]/[theme]/Magento_Theme/layout/default_head_blocks.xml

<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <head>
        <!-- Here are two ways to add local JavaScript files -->
        <script src="[Vendor]_[Module]::js/scripts.js"/>
        <link src="js/scripts.js"/>

        <!-- Add external resources -->
        <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js" src_type="url" />
    </head>
</page>

External JavaScript should include the src_type="url" attribute like the example above.

Magento 2 makes use of RequireJS, and it is recommended to add local and external JavaScript via the RequireJS configuration rather than the above method shown.

Usually, RequireJS related files will be added as part of a module, and so within your module’s view/[area] directory, create a requirejs-config.js file. For example, for adding JavaScript to the frontend, add the file within the view/frontend directory.

// app/code/[Vendor]/[Module]/view/frontend/requirejs-config.js

var config = {
    map: {
        '*': {
            "customElementClick": "[Vendor]_[Module]/js/custom-element-click"
        }
    }
};

Declare a config variable, and map your newly added JavaScript module, by declaring an alias (in this case, customElementClick) and the path to your file. Note that [Vendor]_[Module] should be prefixed to the class.

In addition, take note that you do not need to add the .js extension as RequireJS will add this.

The snippet above suggests you have a web/js/custom-element-click.js file. Within this file, there are a couple of ways within Magento about how to construct your custom JavaScript.

In many of the core modules, Magento uses jQuery widgets. These are always created using $.widget, with the first parameter being a unique name of your widget. Magento’s predefined widgets are always prefixed with mage., such as mage.relatedProducts. It’s a good idea to continue with this naming convention, with the name after the . that describes the widget’s purpose and behaviour.

A simple example of what this widget might look like can be seen below.

// app/code/[Vendor]/[Module]/view/frontend/web/js/custom-element-click.js

define([
    "jquery"
], function($) {
    "use strict";

    // Create the widget
    $.widget('mage.customElementClick', {
        _create: function() {

            // Access to the options
            var options = this.options;

            // Access to the JS element
            var element = this.element;

            element.on('click', function(e){
                alert('Hey, the element was clicked!');
            });
        }

    });
    return $.mage.customElementClick;
});

Widgets will always want to have a _create method, which gives access to the JavaScript element and its optional options.

Your jQuery widget will, funnily enough, be using jQuery, which is why it is included in the define statement. This will give the widget access to jQuery methods such as on() in the above code.

As well as using jQuery widgets, you can also use plain RequireJS modules.

// app/code/[Vendor]/[Module]/view/frontend/web/js/custom-element-click.js

define([
    "jquery"
], function($) {
    "use strict";

    return function (options, element) {
        $(element).click(function (event) {
            alert('Hey, the element was clicked!');
        });

    };
});

These will work just as well, although it looks as though as Magento mostly uses jQuery widgets, you are expected to use those instead.

Now to call and initialise this JavaScript, the easiest method is to add a data-mage-init attribute onto the element you want to add the onClick functionality.

The markup for this within a frontend template file might look like the following.

<div class="some-class" data-mage-init='{ "customElementClick": {} }'>Click me!</div>

By clicking on the Click me! text on the website will cause the Hey, the element was clicked! alert to show!

You can pass in options to data-mage-init.

data-mage-init='{ "customElementClick": {"foo":"bar"} }'

These are picked up within the custom-element-click.js file, and are accessible in the options.

element.on('click', function(e){
    alert('Hey, the element was clicked! The config is: ' + options.foo);
});

This will alert: Hey, the element was clicked! The config is: bar.

An alternative method to using data-mage-init is using the text/x-magento-init script tags.

The following markup for the frontend template file might look like the following.

<div class="some-class">Click me!</div>

<script type="text/x-magento-init">
{
    ".some-class": {
        "customElementClick": {"foo": "bar"}
    }
}
</script>

You can also use RequireJS to include and initialise a jQuery slider plugin, such as Flexslider.

Add the config as usual within requirejs-config.js, include shim, which specifies the Flexslider plugin is dependent on jQuery.

var config = {
    map: {
        '*': {
            'flexslider': '[Vendor]_[Module]/js/jquery.flexslider-min'
        }
    },
    shim: {
        'flexslider': {
            deps: ['jquery']
        }
    }
};

Within a template file, add the JavaScript code that will initialise the slider, ensuring that your code depends on the jquery and flexslider JS modules being available.

Below is the HTML and JavaScript markup you might use to achieve this.

<div class="flexslider">
    <ul class="slides">
        <li>
            <img src="http://flexslider.woothemes.com/images/kitchen_adventurer_cheesecake_brownie.jpg" />
        </li>
        <li>
            <img src="http://flexslider.woothemes.com/images/kitchen_adventurer_lemon.jpg" />
        </li>
        <li>
            <img src="http://flexslider.woothemes.com/images/kitchen_adventurer_donut.jpg" />
        </li>
    </ul>
</div>

<script type="text/javascript">
    require(['jquery', 'flexslider'],function($){
        $(document).ready(function () {
            $('.flexslider').flexslider();
        });
    });
</script>

Magento will give you the ability to extend existing JavaScript, such as jQuery widgets. Within your widget, when defining the widget name as the first parameter in the $.widget() declaration, you can add in a second parameter that contains an existing jQuery widget you wish to override.

define([
    'jquery',
    'jquery/ui',
    'mage/<widget.name>'
], function($) {
 
    $.widget('<your_namespace>.<your_widget_name>', $.mage.<widget.name>, {
        // Your code here 
    });
 
    return $.<your_namespace>.<your_widget_name>;
});

If you want to completely replace existing JavaScript, you can replace an existing component name defined in the requirejs-config.js file with a custom component, where a component is the [Vendor]_[Module]/js/custom-element-click as described the example earlier.

Below shows code to override the Magento_Checkout‘s opc-checkout-method.js file.

var config = {
    map: {
        '*': {
            'Magento_Checkout/js/opc-checkout-method':'[Vendor]_[Module]/js/opc-checkout-method'
        }
    }
};

If you can manage to learn how JavaScript is implemented within Magento, you’ll learn how to add cool features to the application.

Magento itself provides some useful functionality. Did you know that you can enable adding products to the cart on the product detail page via AJAX by simply changing the bindSubmit option of the catalogAddToCart to true?

This code is located in Magento’s addtocart.phtml file. Override this by copying the template into your theme, and change the value described above.

// app/design/[Vendor]/[theme]/Magento_Catalog/templates/product/view/addtocart.phtml

<?php if (!$block->isRedirectToCartEnabled()) : ?>
    <script type="text/x-magento-init">
    {
        "#product_addtocart_form": {
            "catalogAddToCart": {
                "bindSubmit": true
            }
        }
    }
</script>
<?php endif; ?>

Note: This article is based on Magento Open Source version 2.1.9.