Note: You are currently viewing documentation for Moodle 4.0. Up-to-date documentation for the latest stable version of Moodle may be available here: JavaScript guidelines.

Development:JavaScript guidelines

From MoodleDocs

Template:Moodle 2.0 This page is currently just a brain-dump by Tim Hunt. Hopefully this is a useful starting point for discussion - please use this forum thread. If we can all agree on something, then they will become actual coding guidelines, and we can fix up some of the more horrific things Moodle currently does with JavaScript.

General principles

Everything in Moodle should work with JavaScript turned off. This is important for accessibility, and in line with the principles of unobtrusive JavaScript and progressive enhancement.

Almost all JavaScript code should be in separate .js files. There should be the smallest possible amount of JavaScript inline in the HTML code of pages.

The official JavaScript library for Moodle is YUI. That may not be your favourite, but it's the one that was chosen after careful research, so live with it.

Try to add as few things as possible to the global JavaScript name-space - use a few objects in the global name-space with properties and methods. An extreme example is the YUI libraries which only add a single YAHOO object to the global scope. Everything else is contained within that.

Load and execute the JavaScript as late as possible. Since our page should work without JavaScript, load the scripts, and initialise your event handlers from the page footer. If you do this, the page appears to load much more quickly. The user can start to read the page, and by the time they have moved the mouse pointer over your control and click (or whatever), the JavaScript will have had time to load and be ready for them.

Use as few different .js files as possible. Each separate file load that the browser performs has a big overhead. On the other hand, don't include irrelevant JavaScript code. Code that is loaded but never used is a waste of time. Getting this right involves taking an informed decision about how to chunk your JavaScript.

The rest of this page explains how you can achieve these goals.

Getting Moodle to load your JavaScript files

There is a function require_js in lib/weblib.php. It takes one argument, which is either the path to the JavaScript file you want (which will automatically have $CFG->wwwroot prefixed). For example:

require_js('question/qengine.js');

To simplify including the various YUI libraries, require_js recognises some short cuts like

require_js('yui_yahoo');

The full list of recognised short cuts is in lib/ajax/ajaxlib.php. You can also call require_js with an array of libraries, to further save typing. For example:

require_js(array('yui_yahoo','yui_event', 'yui_connection'));

By default, these libraries will be included by adding <script src=...> tags to the footer of the HTML page. This improves performance. If you really need some JavaScript to be loaded before the rest of the page, you can set the optional second argument of require_js to true (note that this only applies from Moodle 2.0 onwards. In Moodle 1.9, require_js put the <script> tags in the header.

require_js keeps track of which files have been included, and ensures that each one is only included once, like the PHP require_once directive.

What should go in your .js files

In almost every case, your .js files should simply define things like functions, classes and variables. When the file is loaded, no JavaScript code should actually be executed that has any effect. This is so that it plays nicely with the require_once-like behaviour of require_js.

The code should follow all the normal moodle coding guidelines about function_names and class_names, and variablenames - with the obvious allowances made for the differences between PHP and JavaScript - and the layout of standard structures like function definitions and if statements and so on should be the same as the PHP guidelines too.

Activating your JavaScript

Since your .js files should not actually do anything when they are loaded, you need some way to trigger them into action. Normally the best way to do this is to put a simple inline script in the HTML that just calls an initialisation function. Typically, you want the HTML page you generate to look like this:

<script type="text/javascript">

   init_my_script('perhaps_some_data', 'for_example', 'mything');

</script>

For an example of this, look at case QUESTION_FLAGSEDITABLE in the print_question_flag method in question/type/questiontype.php.

The arguments you pass to the initialisation function are a good way to get data from your PHP code to your JavaScript. Examples of the kind of thing you need to send are the id of a relevant HTML element, a language string, or values like $CFG->wwwroot. If you have more than a few values to pass however, this becomes a pain, so see the next section.

In lib/weblib.php, this is a helpful function print_js_call that prints a simple script like this for you, given the function name and an array of arguments.

Getting more variables from PHP to JavaScript

When you have lots of values to pass from PHP to JavaScript, or when you have lots of initialisation function calls that all need the same values, a better way to handle the communication is to generate a small amount of JavaScript inline in the HTML of your page that sets up JavaScript variables for all the values you need. The sort of inline script you want is:

 <script type="text/javascript">

qengine_config = {

   pixpath:       'http://example.com/moodle/pix/smartpix.php/standardwhite'
   wwwroot:       'http://example.com/moodle'
   flagtooltip:   'Click to flag this question'
   unflagtooltip: 'Click to un-flag this question'
   flaggedalt:    'Flagged'
   unflaggedalt:  'Not flagged'

}

 </script>

That sets up a number of variables that can be accessed inside your JavaScript functions as qengine_config.pixpath, qengine_config.wwwroot and so on..

The easiest way to generate a script like this is to use the print_js_config function in lib/weblib.php. To generate the above script, you would need to do:

$config = array(

   'pixpath'       => $CFG->pixpath,
   'wwwroot'       => $CFG->wwwroot,
   'flagtooltip'   => get_string('clicktoflag', 'question'),
   'unflagtooltip' => get_string('clicktounflag', 'question'),
   'flaggedalt'    => get_string('flagged', 'question'),
   'unflaggedalt'  => get_string('notflagged', 'question'),

); print_js_config($config, 'qengine_config');

Advice on how to chunk your JavaScript

The aim is to only require a few different .js files to be loaded by each page, while not including huge amounts of irrelevant JavaScript for the browser to parse and then ignore.

Generally speaking, each component of Moodle (activity module, block, filter, ...) should just have one JavaScript file. You might make an exception to this if, for example, you have some JavaScript that your module uses all the time, and a lot of JavaScript that is only used when your module is being edited. In this case, it would be sensible to make a second edit.js file.

(At some point, Moodle may implement a system for automatically combining different JavaScript files from different plugins, for example automatically combining all the JavaScript required by all currently enabled filters, but this should be transparent to the rest of the code.)


Other points

When JavaScript is turned on, the class name 'jsenabled' will be added to the body tag of the page.

Testing

JavaScript support varies a lot between browsers. JavaScript needs to be tested in IE, Firefox and Safari. Ideally, Moodle will support all the browsers that YUI does.

See also

Template:CategoryDeveloper