Note: You are currently viewing documentation for Moodle 3.3. Up-to-date documentation for the latest stable version of Moodle is probably available here: JavaScript guidelines.

Development:JavaScript guidelines: Difference between revisions

From MoodleDocs
(→‎What should go in your .js files: link to coding guidelines added)
No edit summary
Line 1: Line 1:
{{Work in progress}}
{{Moodle 2.0}}
{{Moodle 2.0}}


This page is currently (September 2008) just a brain-dump by [[User:Tim Hunt|Tim Hunt]]. Hopefully this is a useful starting point for [http://moodle.org/mod/forum/discuss.php?d=106312 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.
These guidelines can only be applied fully from Moodle 2.0 onwards, becuase the rely on our new API to facilitate use of JavaScript. When writing JavaScript for earlier versions of Moodle, please try to follow these guidelines in spirit, and use the require_js function, in place of $PAGE->requires->js and $PAGE->requires->yui_lib.


'''Update, April 2009''': It looks like the [[Development:Navigation_2.0_implementation_plan#JavaScript_clean-up|JavaScript cleanup that is now part of Navigation 2.0]] will change some of the details of this, but the general principles will be adopted. Once I have implemented the new $PAGE->requires->... methods, I will update this page.--[[User:Tim Hunt|Tim Hunt]] 03:23, 21 April 2009 (UTC)
==General principles==


==General principles==
===Moodle should be usable without JavaScript===


Everything in Moodle should work with JavaScript turned off. This is important for accessibility, and in line with the principles of [[Development:Unobtrusive_Javascript|unobtrusive JavaScript]] and [[Development:Progressive_enhancement|progressive enhancement]].
Everything in Moodle should work with JavaScript turned off. This is important for accessibility, and in line with the principles of [[Development:Unobtrusive_Javascript|unobtrusive JavaScript]] and [[Development:Progressive_enhancement|progressive enhancement]].
===Minimise inline JavaScript===


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.
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 only <script> tags in the HTML should be
# <script src=... tags to include the necessary .js files.
# Simple function calls to trigger initialisation. For example <script type="text/javascript">initialise_my_feature('some', 'data', 123);</script>.
# Variable definitions to transfer data from the PHP code to JavaScript. For example <script type="text/javascript">var moodle_cfg = {"modpixpath":"http:\/\/tim.moodle.local\/moodle\/mod","sesskey":"ZVNQiq7pHu","developerdebug":true};</script>.
===JavaScript libraries===


The official JavaScript library for Moodle is [http://developer.yahoo.com/yui/ YUI]. That may not be your favourite, but it's the one that was chosen after careful research, so live with it.
The official JavaScript library for Moodle is [http://developer.yahoo.com/yui/ 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.
Moodle also has its own JavaScript library code, principally in lib/javascript-static.js.
 
Moodle uses the TinyMCE HTML editor.
 
===When to include the JavaScript===
 
As per [http://developer.yahoo.com/performance/rules.html Yahoo's best practice guidelines], load and execute the JavaScript as late as possible, ideally the script tags should be the last thing before the </body> close tag.
 
Since everything should work without JavaScript, load and initialising your scripts only after everything else on the page has loaded should not be a problem and will increased the perceived page-load performance for users.
 
===Minimise the number of .js files===


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.
Try not to use too many different .js files. Each separate file load that the browser performs has a big overhead.


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.
On the other hand, organise the JavaScript logically to ease maintenance, and don't include large amounts of irrelevant JavaScript code. Code that is loaded but never used is a waste of time.


The rest of this page explains how you can achieve these goals.
So, if you are writing a new module that needs its own JavaScript, try starting with with a single file mod/mymod/scripts.js. If you find that you are writing a lot of JavaScript that is only needed when the teacher edits your module, but is not needed by students, then consider splitting that code into a separate file like mod/mymod/edit.js, and only including that where needed.


==Getting Moodle to load your JavaScript files==
==How to achive these general principles in Moodle==


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:
The rest of this page explains how you can achieve the above goals.
 
===Getting Moodle to load your JavaScript files===
 
Everything required by the current page is tracked by the $PAGE->requires object, which is an instance of the [http://phpdocs.moodle.org/HEAD/moodlecore/page_requirements_manager.html page_requirements_manager] class. You use it like this:


<code php>  
<code php>  
require_js('question/qengine.js');
$PAGE->requires->js('mod/mymod/scripts.js');
</code>
</code>


To simplify including the various YUI libraries, require_js recognises some short cuts like
By default, this will put the &lt;script> tag that loads the script at the end of the &lt;body> tag, as recommended. If you really must load the script sooner, use one of the following
<code php>
// Load the js file from head.
// (This will throw an exception if head has already been output.)
$PAGE->requires->js('mod/mymod/scripts.js')->in_head();


<code php>
// Loads the js file as soon as possible (from head, if that has
require_js('yui_yahoo');
// not been output yet, otherwise output the script tag right here.)
echo $PAGE->requires->js('mod/mymod/scripts.js')->in_head();
</code>
</code>


The full list of recognised short cuts is in [http://cvs.moodle.org/moodle/lib/ajax/ajaxlib.php?view=markup lib/ajax/ajaxlib.php]. You can also call require_js with an array of libraries, to further save typing. For example:
$PAGE->requires keeps track of which files have been included, so even if two different pieces of code request the same library, it is only loaded once.
 
===Getting Moodle to load YUI libraries===
 
Because YUI is the official JavaScript library for Moodle, there  is a short cut syntax for loading the YUI libraries.


<code php>
<code php>
require_js(array('yui_yahoo','yui_event', 'yui_connection'));
$PAGE->requires->yui_lib('autocomplete');
</code>
</code>


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.
This knows about the dependencies between the JavaScript libraries, so the above code will actually load the five libraries yahoo, dom, event, datasource and autocomplete. It will also load the required CSS file autocomplete/assets/skins/sam/autocomplete.css.


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.
Because some YUI libraries rely on associated CSS, and becuase CSS can only be included in the &lt;head> section of the HTML, you should try to call $PAGE->requires->yui_lib before &lt;head> is output, if at all possible.


== What should go in your .js files ==
===JavaScript coding style===


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.
Moodle JavaScript code should should follow the same [[Development:Coding_style|coding style as Moodle PHP code]], allowing for the differences between PHP and JavaScript.


The code should follow all the [[Development:Coding|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.
For example, all the rules on ''function_names'', ''class_names'' and ''variablenames'' apply. You should document your code with [http://jsdoc.sourceforge.net/ JSDoc] comments. Layout your JavaScript expressions and statements like the equivalent PHP ones.
 
Normally, 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 unless it is the sort of code that can safely be executed once per HTML page. This is so that it plays nicely with the require_once-like behaviour of $PAGE->requires->js.
 
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 of this practice is the YUI libraries which only add a single YAHOO object to the global scope and put everything inside that. Moodle code does not need to go to that extreme. In particular, do not add Moodle-specific code to the YAHOO namespace!)


==Activating your JavaScript==
==Activating your JavaScript==
Line 57: Line 93:
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:
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:


<code php>
<code html>
<!-- HTML code that will work with JavaScript disabled,
<!-- HTML code that will work with JavaScript disabled,
     perhaps with an id="mything" attribute on the main HTML tag. -->
     perhaps with an id="mything" attribute on the main HTML tag. -->
<script type="text/javascript">
<script type="text/javascript">
     init_my_script('perhaps_some_data', 'for_example', 'mything');
     init_my_script('perhaps some data', 'for example', 'mything');
</script>
</script>
</code>
</code>


For an example of this, look at [http://cvs.moodle.org/moodle/question/type/questiontype.php?annotate=1.103#l972 case QUESTION_FLAGSEDITABLE in the print_question_flag method in question/type/questiontype.php].
The best way to generate this initialisation script is to use another feature of $PAGE->requires:
<code php>
$PAGE->requires->js_function_call('init_my_script',
        array('perhaps some data', 'for example', 'mything'));
</code>


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.
Once again, by default the &lt;script> tag will be generated at the end of the HTML, but you can change that with modifiers like ->asap() or ->on_dom_ready() (which uses the YUI onDomReady event).
 
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==
==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:
The arguments you pass to the initialisation function are one way to get specific data from your PHP code to your JavaScript. However, when you need to initialise several things on the page and they all need the same data, there is a better way:
 
<code php>
<code php>
  <script type="text/javascript">
$data = array(
qengine_config = {
     'value1' => $value1,
     pixpath:      'http://example.com/moodle/pix/smartpix.php/standardwhite'
     'value2' => $value2,
     wwwroot:      'http://example.com/moodle'
);
    flagtooltip:  'Click to flag this question'
$PAGE->requires->data_for_js('mymod_config', $data);
    unflagtooltip: 'Click to un-flag this question'
    flaggedalt:    'Flagged'
    unflaggedalt:  'Not flagged'
}
  </script>
</code>
</code>


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..
If you do that, then in JavaScript, mymod_config.value1 will contain the value of the PHP variable $value1.


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:
==Getting language strings from PHP to JavaScript==


One type of value you frequently need to get from PHP to JavaScript is the translation of some language strings into the current language. Because this is a common requirement, there is a special syntax for this:
<code php>
<code php>
$config = array(
$PAGE->requires->data_for_js('tooltip', 'mymod');
    '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');
</code>
</code>
 
If you make this call, then the JavaScript variable mstr.mymod.tooltip will hold the value of get_string('tooltip', 'mymod').
==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==
==Other points==


When JavaScript is turned on, the '''class name 'jsenabled'''' will be added to the body tag of the page.
When JavaScript is turned on, the class name 'jsenabled' will be added to the body tag of the page. You can use this if, for example, you have some content that you wish to be hidden when JavaScript is enabled. Just add
<code css>
.jsenabled .nonjsfallback {
    display: none;
}
</code>


==Testing==
==Testing==
Line 123: Line 145:
==See also==
==See also==


* [http://moodle.org/mod/forum/discuss.php?d=106312 Forum thread for discussing this proposal]
* [[Development:Coding|The other Moodle coding guidelines]]
* [[Development:Coding|The other coding guidelines]]
* [http://developer.yahoo.com/yui/ YUI documentation]
* [http://developer.yahoo.com/yui/ YUI documentation]
* [[Javascript FAQ]]
* [[Development:Unobtrusive Javascript]]
* [[Development:Unobtrusive Javascript]]
* [[Development:JavaScript functions]]
* [[Development:JavaScript functions]]
* [http://developer.yahoo.com/performance/rules.html Yahoo's Best Practices for Speeding Up Your Web Site]
* [http://developer.yahoo.com/performance/rules.html Yahoo's Best Practices for Speeding Up Your Web Site]
* [[Javascript FAQ]]
* [http://moodle.org/mod/forum/discuss.php?d=106312 Forum thread for discussing this proposal]


{{CategoryDeveloper}}
{{CategoryDeveloper}}

Revision as of 06:41, 15 June 2009

Template:Moodle 2.0

These guidelines can only be applied fully from Moodle 2.0 onwards, becuase the rely on our new API to facilitate use of JavaScript. When writing JavaScript for earlier versions of Moodle, please try to follow these guidelines in spirit, and use the require_js function, in place of $PAGE->requires->js and $PAGE->requires->yui_lib.

General principles

Moodle should be usable without JavaScript

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.

Minimise inline JavaScript

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 only <script> tags in the HTML should be

  1. <script src=... tags to include the necessary .js files.
  2. Simple function calls to trigger initialisation. For example <script type="text/javascript">initialise_my_feature('some', 'data', 123);</script>.
  3. Variable definitions to transfer data from the PHP code to JavaScript. For example <script type="text/javascript">var moodle_cfg = {"modpixpath":"http:\/\/tim.moodle.local\/moodle\/mod","sesskey":"ZVNQiq7pHu","developerdebug":true};</script>.

JavaScript libraries

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.

Moodle also has its own JavaScript library code, principally in lib/javascript-static.js.

Moodle uses the TinyMCE HTML editor.

When to include the JavaScript

As per Yahoo's best practice guidelines, load and execute the JavaScript as late as possible, ideally the script tags should be the last thing before the </body> close tag.

Since everything should work without JavaScript, load and initialising your scripts only after everything else on the page has loaded should not be a problem and will increased the perceived page-load performance for users.

Minimise the number of .js files

Try not to use too many different .js files. Each separate file load that the browser performs has a big overhead.

On the other hand, organise the JavaScript logically to ease maintenance, and don't include large amounts of irrelevant JavaScript code. Code that is loaded but never used is a waste of time.

So, if you are writing a new module that needs its own JavaScript, try starting with with a single file mod/mymod/scripts.js. If you find that you are writing a lot of JavaScript that is only needed when the teacher edits your module, but is not needed by students, then consider splitting that code into a separate file like mod/mymod/edit.js, and only including that where needed.

How to achive these general principles in Moodle

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

Getting Moodle to load your JavaScript files

Everything required by the current page is tracked by the $PAGE->requires object, which is an instance of the page_requirements_manager class. You use it like this:

$PAGE->requires->js('mod/mymod/scripts.js');

By default, this will put the <script> tag that loads the script at the end of the <body> tag, as recommended. If you really must load the script sooner, use one of the following // Load the js file from head. // (This will throw an exception if head has already been output.) $PAGE->requires->js('mod/mymod/scripts.js')->in_head();

// Loads the js file as soon as possible (from head, if that has // not been output yet, otherwise output the script tag right here.) echo $PAGE->requires->js('mod/mymod/scripts.js')->in_head();

$PAGE->requires keeps track of which files have been included, so even if two different pieces of code request the same library, it is only loaded once.

Getting Moodle to load YUI libraries

Because YUI is the official JavaScript library for Moodle, there is a short cut syntax for loading the YUI libraries.

$PAGE->requires->yui_lib('autocomplete');

This knows about the dependencies between the JavaScript libraries, so the above code will actually load the five libraries yahoo, dom, event, datasource and autocomplete. It will also load the required CSS file autocomplete/assets/skins/sam/autocomplete.css.

Because some YUI libraries rely on associated CSS, and becuase CSS can only be included in the <head> section of the HTML, you should try to call $PAGE->requires->yui_lib before <head> is output, if at all possible.

JavaScript coding style

Moodle JavaScript code should should follow the same coding style as Moodle PHP code, allowing for the differences between PHP and JavaScript.

For example, all the rules on function_names, class_names and variablenames apply. You should document your code with JSDoc comments. Layout your JavaScript expressions and statements like the equivalent PHP ones.

Normally, 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 unless it is the sort of code that can safely be executed once per HTML page. This is so that it plays nicely with the require_once-like behaviour of $PAGE->requires->js.

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 of this practice is the YUI libraries which only add a single YAHOO object to the global scope and put everything inside that. Moodle code does not need to go to that extreme. In particular, do not add Moodle-specific code to the YAHOO namespace!)

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>

The best way to generate this initialisation script is to use another feature of $PAGE->requires: $PAGE->requires->js_function_call('init_my_script',

       array('perhaps some data', 'for example', 'mything'));

Once again, by default the <script> tag will be generated at the end of the HTML, but you can change that with modifiers like ->asap() or ->on_dom_ready() (which uses the YUI onDomReady event).

Getting more variables from PHP to JavaScript

The arguments you pass to the initialisation function are one way to get specific data from your PHP code to your JavaScript. However, when you need to initialise several things on the page and they all need the same data, there is a better way: $data = array(

   'value1' => $value1,
   'value2' => $value2,

); $PAGE->requires->data_for_js('mymod_config', $data);

If you do that, then in JavaScript, mymod_config.value1 will contain the value of the PHP variable $value1.

Getting language strings from PHP to JavaScript

One type of value you frequently need to get from PHP to JavaScript is the translation of some language strings into the current language. Because this is a common requirement, there is a special syntax for this: $PAGE->requires->data_for_js('tooltip', 'mymod'); If you make this call, then the JavaScript variable mstr.mymod.tooltip will hold the value of get_string('tooltip', 'mymod').

Other points

When JavaScript is turned on, the class name 'jsenabled' will be added to the body tag of the page. You can use this if, for example, you have some content that you wish to be hidden when JavaScript is enabled. Just add .jsenabled .nonjsfallback {

   display: none;

}

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