Plugin Tutorial: JavaScript

Client side scripting can greatly improve the user experience of a web site and particularly a web application. So you are encouraged to add client side scripting to your plugin, wherever that makes sense. All client side scripting for CMSimple_XH should be done in JavaScript. Note that you can count on JavaScript being enabled for the back-end; for front-end scripting, you should not rely on JavaScript being enabled in the visitors browser, so prefer to use JavaScript as progressive enhancement and provide a graceful degredation for user agents, which ignore JavaScript.

When writing JavaScript for a CMSimple_XH plugin, the “golden rule” applies also:

You are not alone!

So prefix all you global identifiers with a unique string (preferably the plugin name). As JavaScript offers the possibility to define objects ad hoc, you can make use of the namespace pattern, e.g. (simplified):

var myplugin = {
    variable = "some value",
    doThis = function() {...},
    doThat = function() {...}
}

Embedding in the Document

Usually you'll want to embed your JavaScript code in the produced document with a “script” element; either directly in the document or by including a separate JS file 1). The latter has some general advantages (particularly it is not necessary to take care of escaping the JavaScript), but sometimes the former is more appropriate. In any way you should not use the “language” attribute, but instead the “type” attribute:

<script type="text/javascript" ...>...</script>

If you're going to embed the JavaScript directly, you should precautiously 2) cater for XML processing of the XHTML by declaring the code as CDATA section:

<script type="text/javascript">/* <![CDATA[ */
...
/* ]]> */</script>

To actually put the “script” element in the generated output, you often can simply do it the same way as with (X)HTML, i.e. as part of the return value of a plugin function or directly by appending to the global $o (CMSimple_XH's output buffer for the content area) 3). For performance reasons it's often reasonable to write “script” elements at the end of the “body” element. Since CMSimple_XH 1.5.4 there's the global variable $bjs, which you can extend with your “script” element, to do so. Sometimes it's necessary to write your “script” element to the “head” element of the document; therefore you can use the global $hjs. Note that appending to $hjs doesn't have the desired effect, if done from the template, after the funtion head() was called.

Triggering JavaScript Execution

To trigger the actual execution of the JavaScript code, there are basically three possibilities:

  1. directly when the JavaScript code is parsed
  2. via an event handler attribute
  3. via an event listener

(1) is useful for initialization; just let some code execute in the global scope or, often preferable, use a anonymous function expression that is called immediately after creation:

(function() {
    doThis();
    doThat();
})()

(2) is a simple way to handle an event. You can output the required code directly with the (X)HTML:

<form ... onsubmit="return validateInput()">

In this case you have to be careful with the quotes when using more complex expressions. Somewhat special is the onload attribute of the “body” element, which can be used to trigger execution of JavaScript code after the complete DOM is built. To add code to this attribute, you can append it to the global $onload:

$onload .= 'doSomething();';

Note the semicolon, which is important if another plugin appends to $onload after yourself. And note, that appending to $onload has not the desired effect, when done from the template after the opening “body” tag was processed.

(3) is the most flexible and powerful mechanism. Unfortunately that's a little bit complicated, as IE < 9 doesn't implement the standardized EventListener interface, so you'll have to provide a fallback:

if (typeof element.addEventListener != "undefined") { // for standard conforming browsers
    element.addEventListener("click", doSomething(), false);
} else if (typeof element.attachEvent != "undefined") { // for IE < 9
    element.attachEvent("onclick", doSomething());
} else { // handle other browsers, if reasonable
    doSomethingElse();
}

JSON

JSON (JavaScript Object Notation) is a universal data exchange format, which is built from a subset of JavaScript. Therefore it is particularly well suited to pass values from PHP to JavaScript and vice versa.

Make your life easy and use PHP's json_encode() resp. json_decode().

Of course it's possible to construct a JSON string “manually”, but there are several issues with regard to the necessary escaping. All this is handled automatically by json_encode(), which converts an arbitrary PHP value to a JSON string. json_encode()/json_decode() are bundled with PHP since PHP 5.2; if you want your plugins to be compatible with older versions, you can use the fallback implementations of the CMBUtils.

AJAX

A very useful feature of modern JavaScript is the XMLHttpRequest (XHR for short), which is commonly known as AJAX (asynchronous JavaScript and XML). Here's not the place to go into detail what you can do with XHR, and how to do it generally, though; there's plenty of information available on the web. However, a note about how to use it with CMSimple_XH seems to be appropriate:

Make your life easy and use the CMSimple_XH framework.

That means that you may avoid setting up a seperate file, which handles the request(s), but instead you may call CMSimple_XH's index.php passing an appropriate GET resp. POST parameter, that triggers your plugin to handle the request. This has the big advantage, that the full CMSimple_XH API is available, so you don't have to construct some paths yourself or even have to pass other values in a session variable for example. Note, that in this case you'll want to terminate CMSimple_XH after you have sent the response. An example:

if (isset($_GET['myplugin_xhr'])) { // handle XHR of myplugin
    header('Content-Type: text/plain'); // set the appropriate Content-Type header
    echo $plugin_tx['myplugin']['response'];  // sent the response
    exit; // prevent further processing
}

To refer to CMSimple_XH's index.php you can simply use the global $sn.

jQuery

You may want to use a JavaScript library/framework to ease writing the code. Basically that's fine and you may choose whichever library you prefer. But note, that there's a potential problem: several of these libraries use the $ as important identifier (e.g. jQuery, Mootools, Prototype), so this may clash, if more than one library is used on a single CMSimple page. As jQuery is the most prominent of these libraries nowadays, it was decided to endorse jQuery as official JavaScript library for CMSimple, which seems the only way to reduce the probability of clashes. To avoid problems when more than one plugin is including jQuery in combination with a jQuery plugin, jQuery4CMSimple was developed. This CMSimple_XH plugin offers a simple API, which will take care, that jQuery, jQueryUI and jQuery plugins will be included at most once. How to use jQuery4CMSimple is described in its manual.

If you're going to use a JavaScript library for a publicly distributed plugin, you should use jQuery (and not another library), and if you use jQuery, you should use it through jQuery4CMSimple. If you don't use jQuery, you should neither define nor use an identifier named $.

« Data Storage | Security »

1)
which might be a PHP script that creates the JavaScript dynamically
2)
at the time of writing CMSimple_XH always delivers the (X)HTML as “text/html”, but this might change in the future
3)
Note that the latter might have undesireable side-effects, see http://cmsimpleforum.com/viewtopic.php?f=29&t=7663#p41465
 
Except where otherwise noted, content on this wiki is licensed under the following license: GNU Free Documentation License 1.3
Valid XHTML 1.0 Valid CSS Driven by DokuWiki