![]() |
|
sfUJSPlugin - 0.5.2Unobstrusive JavaScript helpers |
|
![]() |
3
users
Sign-in
to change your status |
The sfUJSPlugin proposes a set of temlate helpers that facilitate the creation of JavaScript interactions in an unobstrusive way. |
| Name | Status | |
|---|---|---|
|
|
lead | moc.tcejorp-ynofmys <<ta>> ottoninaz.siocnarf |
Copyright (c) 2004-2006 Francois Zaninotto
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
| Version | License | API | Released |
|---|---|---|---|
| 0.6.0beta | MIT license | 0.6.0beta | 20/05/2007 |
| Version | License | API | Released |
|---|---|---|---|
| 0.6.0beta | MIT license | 0.6.0beta | 20/05/2007 |
| 0.5.2alpha | MIT license | 0.5.2alpha | 25/04/2007 |
| 0.5.1alpha | MIT license | 0.5.1alpha | 07/03/2007 |
| 0.5.0alpha | MIT license | 0.5.0alpha | 27/02/2007 |
The sfUJSPlugin offers helpers that facilitate the creation of interactive effects with JavaScript in an unobtrusive way.
If you want to learn more about the unobtrusive approach to JavaScript, and how you can use JavaScript to enhance usability without sacrificing accessibility, read this article.
Warning: This plugin is in Alpha state. Syntax is subject to change.
Doing unobtrusive JavaScript can be a pain for several reasons:
id, you have to refer to it using complicated CSS selectors/XPath queries, hard to read and to maintain when the template changes.All these lead to an alternative way to code UJS, using PHP helpers in the template. The sfUJSPlugin can be used to add interactive effects and Ajax calls to your pages, and is an alternative to the [symfony Javascript helpers]. This implementation uses [http://jquery.com/ JQuery] as the underlying JavaScript framework, but the same could be achieved with http://www.prototypejs.org/ Prototype.
The UJS implementation of this plugin uses the sfPJSPlugin, which provides a way to call dynamically generated JavaScript files. The benefit of UJS over PJS alone is that you can write JavaScript through helpers directly in the main template and avoid switching back and forth to a behaviour template.
Install the plugin
$ symfony plugin-install http://plugins.symfony-project.com/sfUJSPlugin
Alternatively, if you don't have PEAR installed, you can download the latest package attached to this plugin's wiki page and extract it under your project's plugins/ directory
If it is not installed yet, install the sfPJSPlugin, which is necessary to run this plugin.
Enable the UJS module in the settings.yml:
all: .settings: enabled_modules: UJS
Activate the sfUJSFilter in the apps/myapp/filters.yml:
rendering: web_debug: security:
cache: common:
UJS: class: sfUJSFilter
flash: execution: ~
Optional: You can choose to use the UJS alternative to the Tag helper so that every mention of an event handler (such as onclick) in a symfony helper gets rendered in an unobtrusive way. This is done by copying the sfUJSPlugin/lib/helper/UJSTagHelper.php file into myapp/lib/helper/TagHelper.php and clearing the cache (see below)
Clear the cache to enable the autoloading to find the new classes
$ symfony cc
The UJS plugin provides a set of helpers to be used within templates. As for other helpers, you must declare the related helper group to make the helper functions available in the template.
The following template code:
<?php UJS("$('#foobar').css('display', 'none')") ?>
Renders as follows:
<head>
</head>
<body>
</body>
The filter contained in the plugin automatically declares the use of a special JavaScript file, which is generated dynamically by the UJS/script action. This action packages all the code included by calls to the UJS() helper. In the previous example, the included JavaScript is:
$().ready(function(){
$('#foobar').css('display', 'none');
})
The resulting DOM after execution is:
Alternatively, you can write the Javascript code between two helper calls, to avoid complicated quotes escaping problems.
$('#foobar').css('display', 'none');
// same as
By default, all code declared with UJS helpers ends up in an attached file. That's the way the plugin achieves complete unobtrusiveness: there is absolutely no JavaScript code in the XHTML response, and the behaviour layer is clearly separated from the content layer. This also allows for caching of the behaviour code by the browsers, just like CSS files are cached to avoid reloading style declarations. As compared to embedded code, UJS code saves server bandwith and accelerates the display on the client side.
This works fine as long as the UJS code is static. But on some special cases, the UJS code depends on the user session or on the database, and in this case separating the UJS code from the reponse creates issues due to caching. That's why the helper provides a way to directly embed UJS code in the XHTML instead of getting it via the UJS/script action.
There are three ways to enable this 'non-static' behaviour:
Set the static response parameter to false in the symfony/view/UJS namespace. This is mostly useful for when you want to define this behaviour in an actions file:
sfContext::getInstance()->getResponse()->setParameter('static', false, 'symfony/view/UJS');
Call the UJS_set_inclusion(false) helper at the end of a template to declare that the UJS code of the template is not static:
Alternately, you can define that the default inclusion method for all templates is to embed instead of attach via the app.yml file:
all: UJSPlugin: static: false
Once the UJS code is declared non-static, the UJS plugin filter will no longer include the UJS/script action as an external script, but rather look for a </body> tag and insert a <script> content just before. So for instance, the following template code:
<?php UJS("$('#foobar').css('display', 'none')") ?>
<?php UJS_set_inclusion(false) ?>
Renders as follows:
...
<script>
// <![CDATA[
$().ready(function(){
$('#foobar').css('display', 'none');
})
// ]]>
</script>
</body>
If you prefer that the UJS code appears somewhere else in the document, all the include_UJS() helper somewhere in your template/layout, and the plugin will use this placeholder instead of looking for a </body> for script inclusion.
The UJS() helper is just one of a bunch of helpers proposed by the UJS helper group to facilitate the writing of UJS code with PHP. The other helpers of the plugin, for which you will find complete syntax and examples below, are:
UJS_add_behaviour($selector, $event, $code)
UJS_change attributes($selector, $attributes)
UJS_change style($selector, $style_attributes)
UJS_write($code)
UJS_link_to_function($text, $code)
UJS_button_to_function($text, $code)
UJS_ajaxify_link($selector, $ajax_options)
UJS_ajaxify_form($selector, $ajax_options)
UJS_ajaxify($selector, $ajax_options)
UJS uses jQuery to translate the helpers into JavaScript. This means that the code you write can also use JQuery, since the library is automatically included by the helper functions.
One of the great advantages of jQuery is that when a selector returns more than one DOM element, calling a method on the result of the selector automatically iterates over the results. So for instance,
$('p').css('color', 'red');
...will loop over all <p> elements of the document and change their color CSS attribute to red.
The UJSPlugin helpers support the same automatic iteration feature, so all the helpers expecting a selector as first parameter can work with a selector returning one or more results.
The following template code:
<div id="foobar">
click me
</div>
<?php UJS_add_behaviour('#foobar', 'click', "alert('foobar')") ?>
Renders as follows:
<div id="foobar">
click me
</div>
// in linked UJS/script
$().ready(function(){
$('#foobar').click(function() { alert('foobar') });
}
And the resulting DOM after execution is:
<div id="foobar" onclick="alert('foobar')">
click me
</div>
The following template code:
<div id="foobar">
I'm here !
</div>
<?php UJS_change_attributes('#foobar', 'style=color:yellow class=foo') ?>
<?php UJS_change_style('#foobar', 'text-decoration:underline') ?>
Renders as follows:
<div id="foobar">
I'm here !
</div>
// in linked UJS/script
$().ready(function(){
$('#foobar').attr('style', 'color:yellow').attr('class', 'foo');
$('#foobar').css('text-decoration', 'underline');
})
And the resulting DOM after execution is:
<div id="foobar" style="color:yellow; text-decoration:underline" class="foo">
I'm here !
</div>
The following template code:
<?php UJS_write('<a href="#" onclick="$(\'#foobar\').toggle();return false;">click me</a>') ?>
<div id="foobar">
I'm here !
</div>
<?php UJS_change_style('#foobar', 'display:none') ?>
Renders as follows:
<span style="display: none" class="UJS_placeholder" id="UJS_0"></span>
<div id="foobar">
I'm here !
</div>
// in linked UJS/script
$().ready(function(){
$('#UJS_0').after('<a href="#" onclick="$(\'#foobar\').toggle();return false;">click me</a>');
$('#UJS_0').remove();
$('#foobar').css('display', 'none');
})
And the resulting DOM after execution is:
<a href="#" onclick="$('#foobar').toggle();return false;">click me</a>
<div id="foobar" style="display:none"">
I'm here !
</div>
Alternatively, you can write the HTML code between two helper calls, to avoid complicated quotes escaping problems.
<?php UJS_write_block() ?>
<a href="#" onclick="$('#foobar').toggle();return false;">click me</a>
<?php UJS_end_write_block() ?>
// same as
<?php UJS_write('<a href="#" onclick="$(\'#foobar\').toggle();return false;">click me</a>') ?>
The most common JavaScript effects are triggered by a user click, either on a link or on a button. Just like the regular JavaScript helpers, the UJS helper group provides two helpers just for that purpose:
<?php echo UJS_link_to_function('click me', "alert('foobar')") ?>
<?php echo UJS_button_to_function('and also click me', "alert('foobarbaz')") ?>
These two lines render as follows:
<span style="display: none" class="UJS_placeholder" id="UJS_0"></span>
<span style="display: none" class="UJS_placeholder" id="UJS_1"></span>
// in linked UJS/script
$().ready(function(){
$('#UJS_0').after('<a href="#" onclick="alert(\'foobar\'); return false;">click me</a>');$('#UJS_0').remove();
$('#UJS_1').after('<input type="button" value="and also click me" onclick="alert(\'foobarbaz\')" />');$('#UJS_1').remove();
})
And the resulting DOM after execution is:
<a onclick="alert('foobar'); return false;" href="#">click me</a>
<input type="button" onclick="alert('foobarbaz')" value="and also click me"/>
The plugin provides helpers to modify an existing link, button or form to make it Ajax, so that clicking/submitting it will issue a remote call and update a DOM element with the response.
The following template code:
<div id="ajax_feedback">
ajax feedback zone
</div>
<?php echo link_to('click me', 'foo/bar', 'id=1234') ?>
<?php UJS_ajaxify_link('#1234', array(
'update' => '#ajax_feedback',
'url' => 'foo/ajaxbar',
)) ?>
Renders as follows:
<div id="ajax_feedback">
ajax feedback zone
</div>
<a href="/foo/bar" id="1234">click me</a>
// in linked UJS/script
$().ready(function(){
$('#1234').click(function() { $.ajax({url: '/foo/ajaxbar', success: function(response) { $('#ajax_feedback').html(response) }}); return false });
})
The syntax is the same for all the UJS Ajax helpers. UJS_ajaxify_link() (to ajaxify a link), UJS_ajaxify_form() (to ajaxify a form) and UJS_ajaxify() (to ajaxify whatever element) all expect two parameters: a CSS3 selector of the element(s) to ajaxify, and an associative array of Ajax options. The possible Ajax options are:
url: Internal URI of the action called remotely
update: Selector of the element(s) to update with the remote response
position: If specified, the remote response will not replace the content of the update element, but rather complement it. Possible values are before, top, bottom, and after.
You can specify code to be executed at various steps of the Ajax request execution via the callback options:
beforeSend
success
error
complete
confirm: Adds a confirmation dialog.
condition: Perform remote request conditionally by this expression. Use this to describe browser-side conditions when request should not be initiated.
Symfony already contains quite a lot of helpers outputting HTML elements. All these helpers support setting additional attributes, including event handlers. For instance, the link_to() helper transforms this call:
<?php echo link_to('click me', 'foo/bar', 'onclick=alert("you clicked me!")') ?>
Into:
<a href="/foo/bar" onclick="alert("you clicked me!")">click me</a>
This is obtrusive code, for sure, but a damn fast way to define event handlers. What if symfony coud do the translation for you, so that every event handler defined in a symfony helper gets rendered in an unobtrusive way? That's exactly what the UJSTag helper group does.
To enable this helper, copy the lib/helper/UJSTagHelper.php file bundled in this plugin into your application's lib/helper/ directory, and rename it to TagHelper.php.
Now, every mention of an event handler in a symfony helper call will be catched and transformed into an UJS behaviour. So the previous helper call:
<?php echo link_to('click me', 'foo/bar', 'onclick=alert(\'you clicked me!\')') ?>
Will be transformed into:
<a id="UJS_0" href="/foo/bar">click me</a>
// in linked UJS/script
$().ready(function(){
$('#UJS_0').click( function() { alert('you clicked me!') } );
})
So, you have always been using the classic 'Javascript' helper group, you don't want to learn a new syntax, but you want unobtrusiveness? You don't really deserve it, but hey, it's possible. This plugin comes with an alternative to the 'Javascript' helper group that uses the same syntax, but outputs unobtrusive code instead of obtrusive one.
To enable this helper, copy the lib/helper/UJSJavascriptHelper.php file bundled in this plugin into your application's lib/helper/ directory, and rename it to JavascriptHelper.php. From then on, symfony will use this file when you call <?php use_helper('Javascript') ?> instead of the one package with the framework.
Use the helpers as you always did:
<?php use_helper('UJavascript') ?>
<div id="ajax_feedback">
ajax feedback zone
</div>
<?php link_to_remote('click me for Ajax', array(
'update' => 'ajax_feedback',
'url' => 'test/ajax',
)) ?>
Renders as follows:
<div id="ajax_feedback">
ajax feedback zone
</div>
<span style="display: none" class="UJS_placeholder" id="UJS_0"></span>
// in linked UJS/script
$().ready(function(){
$('#UJS_0').after('<a href="#" onclick="jQuery.ajax({url: \'/frontend_dev.php/test/ajax\', success: function(response) { $(\'#ajax_feedback\').html(response) }}); return false;">click me for Ajax</a>');$('#UJS_0').remove();
})
Helpers implemented in the UJavascript helper group are:
link_to_remote()
link_to_function()
button_to_remote()
button_to_function()
Warning: Although this is very practical, using this helper is not advised. Unobtrusiveness is not just a post-processing, it's a way to design interactions. You will never achieve true unobtrusiveness unless you drop the habits taken with the Javascript helpers, and this means stop using the functions listed ahead.
Warning: Due to a bug in sfWebDebug (#1548), this method will work in production but not in the development environment.
UJavascript helper group as an alternative to the normal Javascript helper groupUJS_ajaxify helpersUJSTagHelper when UJS helper was not loaded
