Magento 2: Creating JavaScript and CSS

Magento 2 makes use of quite a large number of JavaScript Libraries and also uses a PHP port of the official LESS processor to parse the .less files into .css file

Posted on September 26, 2017 in Magento2

JavaScript

Magento 2 makes use of quite a large number of JavaScript libraries, shown as follows:

  • Knockout: http://knockoutjs.com
  • Ext JS: https://www.sencha.com/products/extjs/
  • jQuery: https://jquery.com
  • jQuery UI: https://jqueryui.com
  • modernizer: http://www.modernizer.com
  • Prototype: http://www.prototypejs.org
  • RequestJS: http://requirejs.org
  • Script.aculo.us: http://script.aculo.us
  • moment.js: http://momentjs.com/
  • Underscore.js http://underscorejs.org
  • gruntjs: http://gruntjs.com
  • AngularJS: https://angularjs.org
  • jasmine: http://jasmine.github.io

The RequireJS and jQuery libraries are probably the most interesting ones, as they often step into the spotlight during frontend development. RequireJS plays a big role in magento 2, as it loads other JavaScript files. Using a modular script loader like RequireJS improves the speed of code.

JavaScript resources can be found at following location in the Magento 2 Systemt:

  • Libarry level for all libraries in the Magento code base (lib/web).
  • Module level for all libraries in a module (app / code / {vendorName} / {moduleName} / view / {area} / web)
  • Theme for all libraries in a theme (app / design / {area} / {vendorName} / {theme} / {vendorName}_{moduleName}/web)

It is recommended to specify JavaScript resources in the templates rather than in the layout updates. This way, we ensure processing of the resources through RequireJs.

To work with the RequireJS library, specify the mapping of JavaScript resources, which is to assign the aliases to resources by using requirejs-config.js to create the mapping.

One example of requirejs-config.js file is shown as follows:


# magento2/vendor/magento/module-theme/view/base/requirejs-config.js

/**
 * Copyright © 2013-2017 Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

var config = {
    "waitSeconds": 0,
    "map": {
        "*": {
            "ko": "knockoutjs/knockout",
            "knockout": "knockoutjs/knockout",
            "mageUtils": "mage/utils/main",
            "rjsResolver": "mage/requirejs/resolver"
        }
    },
    "shim": {
        "jquery/jquery-migrate": ["jquery"],
        "jquery/jquery.hashchange": ["jquery", "jquery/jquery-migrate"],
        "jquery/jstree/jquery.hotkeys": ["jquery"],
        "jquery/hover-intent": ["jquery"],
        "mage/adminhtml/backup": ["prototype"],
        "mage/captcha": ["prototype"],
        "mage/common": ["jquery"],
        "mage/new-gallery": ["jquery"],
        "mage/webapi": ["jquery"],
        "jquery/ui": ["jquery"],
        "MutationObserver": ["es6-collections"],
        "tinymce": {
            "exports": "tinymce"
        },
        "moment": {
            "exports": "moment"
        },
        "matchMedia": {
            "exports": "mediaCheck"
        },
        "jquery/jquery-storageapi": {
            "deps": ["jquery/jquery.cookie"]
        }
    },
    "paths": {
        "jquery/validate": "jquery/jquery.validate",
        "jquery/hover-intent": "jquery/jquery.hoverIntent",
        "jquery/file-uploader": "jquery/fileUploader/jquery.fileupload-fp",
        "jquery/jquery.hashchange": "jquery/jquery.ba-hashchange.min",
        "prototype": "legacy-build.min",
        "jquery/jquery-storageapi": "jquery/jquery.storageapi.min",
        "text": "mage/requirejs/text",
        "domReady": "requirejs/domReady",
        "tinymce": "tiny_mce/tiny_mce_src"
    },
    "deps": [
        "jquery/jquery-migrate"
    ],
    "config": {
        "mixins": {
            "jquery/jstree/jquery.jstree": {
                "mage/backend/jstree-mixin": true
            }
        }
    }
};

require(['jquery'], function ($) {
    $.noConflict();
});

To make oure configurations more precise and specific for different modules/thems, we can identify mapping in the requirejs-config.js file at several levels depending on our needs.

  • Library configurations
  • configurations at the module level
  • configurations at the theme module level for the ancestor themes
  • configurations at the theme module level for a current theme
  • configurations at the theme level for the ancestor themes
  • configurations at the theme level for the current theme

There are two ways we can initialize a JavaScript component in themplate files:

  1. Using the data-mage-init attribute
  2. Using the {script} tag

The data-mage-init attribute is parsed on a DOM ready event. Since it is initialized on a certain element, the script is called only for that particular element, and is not automatically initialized for other elements of the same type on the page. One example is as following:


<div data-mage-init='{ "<componentName>": {....} }'></div>

The <em{script} tag initialization is done without relation to any specific element. The {script} tag has initialization would be something like the following:


<script type="text/x-magento-init">
    //specific element but no direct access to the element
    "<element_selector>": {
        "<jsComponent1>": ....,
        "<jsComponent2>": ....,
    //without relation to any specific element
    "*": {
            "<jsComponent3>": ....
        }
    }
</script>

Creating a custom JS component

Let’s go through a parctical example of creating a JS component within our Jeff_Office module (see Magento 2: Examples About Blocks and Templates).

First, we add our entry to app/code/Jeff/Office/view/frontend/rquirejs-config.js, as following:


var config = {
    map: {
        "*": {
             jeffhello: 'Jeff_Office/js/jeff-hello'
        }
    }
};

The we add the actual JavaScript at app / code / Jeff / Office/ view /frontend /web /js /jeff-hello.js as follows:


define([
'jquery',
'jquery/ui'
], function($) {
    "use strict";
    $.widget('mage-jeffhello', {
       options:{
       },
       _create: function() {
           alert(this.options);
       }
    });
    return $.mage.jeffhello;
});

Finally, we call our JavaScript compoent within some PTHML template, app/code/Jeff /Office/ view/frontent/ templates/ office/hello.phtml:


<div data-mage-init = '{"jeffhello": {"var1": "var1Value", "var2": "var2Value"}}'>Jeff Hello</div>

Once we refresh the frontend, we should see the result of alter(this.options) in the browser showing var1Value and var2Value.

CSS

Magento 2 uses a PHP port of the official LESS processor to parse the .less file into .css files. Less is a CSS preprocessor that extends the CSS language by adding various features to it, like variables, mixins and functions. We can customize the storefront look and feel through one of the following approaches:

  • Override the default LESS files — only if our theme inherits from the default or any other theme, in which case we can override the actual LESS files
  • Create our own LESS files using the built-in LESS preprocessor
  • Create our own CSS files, optionally having compiled them using a theird-party CSS preprocessor

Within the individual frontend them directory, we can find style sheets at the following locations:

  • {vendorName}_{moduleName}/web/css/source/
  • {vendorName}_{moduleName}/web/css/source/module/
  • web/css/
  • web/css/source

CSS files can be included in a page through templates and layout files. A recommended way is to include them through layout files. If we want our style sheets to be available through all pages on the frontend, we can use the default_head_blocks.xml file.

One example of default_head_blocks.xml as following:


<?xml version="1.0"?>
<!--
/**
 * Copyright © 2013-2017 Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
-->
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <head>
        <css src="css/styles-m.css"/>
        <css src="css/styles-l.css" media="screen and (min-width: 768px)"/>
        <css src="css/print.css" media="print"/>
    </head>
</page>


comments powered by Disqus