I'm trying to separate html and jQuery events handlers in my webapp. Here's my context : my webapp works with tabs with ajax loaded content. Each tab content is identified by an unique id. In each tab I got a table. Two or more tabs contents can be the same (like in Chrome for an example, the same website can be loaded in two tabs). In a tab, each table row is editable by clicking a jQuery initialized button.
Here is a simplified example how it currently works :
SMARTY TEMPLATE
<div id="tabContent_{$uniqueId}">
<table>
{foreach $operations as $op}
<tr><td><button id="buttonEditOperation_${op}" /></td></tr>
{/foreach}
</table>
</div>
<script>
$('#tabContent_{$uniqueId} [id^="buttonEditOperation_"]')
.click( function() { alert('CLICK !'); })
.button(blabla);
</script>
When I load a new tab with the same content, only buttons in this new content are initialized, because of #tabContent_{$uniqueId}
selector
I would like to create a javascript file to separate JS and my SMARTY TEMPLATE but I don't know how to select only the content of the new loaded tab. I would like something like this :
SMARTY TEMPLATE
<script type="text/javascript" src="./js/buttonInit.js"></script>
<div id="tabContent_{$uniqueId}">
<table>
{foreach $operations as $op}
<tr><td><button id="buttonEditOperation_${op}" /></td></tr>
{/foreach}
</table>
</div>
JS FILE
<script>
$('#tabContent_?????? [id^="buttonEditOperation_"]') // HERE IS MY PROBLEM
.click( function() { alert('CLICK !'); })
.button(blabla);
</script>
I have some ideas (HTML5 data properties, initialization function, global variable, etc.) but I would like to know the best solution for this problem.
Thanks for your feedback !
See below the best solution I've found ! Do not hesitate to tell me if I'm doing something wrong...
SMARTY TEMPLATE
<script type="text/javascript" src="./js/object.Module.js"></script>
<script type="text/javascript" src="./js/object.Operation.js"></script>
<div id="tabContent_{$uniqueId}">
<table>
{foreach $operations as $op}
<tr><td>
<button id="buttonEditOperation_${op}" />
<span class="tooltip" title="Tooltip content">Show tooltip</span>
</td></tr>
{/foreach}
</table>
</div>
<script>
// Create a new object "Operation" which inherit from "Module"
var currentModule_{$uniqueId} = new Operation( {$uniqueId} );
// Use a method from main object "Module"
currentModule_{$uniqueId}.init_tooltips();
// Use a method from specific object "Operation"
currentModule_{$uniqueId}.init_buttonEditOperation('CLICK !');
</script>
MAIN JS FILE : "object.Module.js"
var Module = function(_uniqueId) {
// Store the value of this in a different variable because jQuery
// override it when handling events
// (http://stackoverflow.com/questions/10596727/this-keyword-overriden-in-javascript-class-when-handling-jquery-events)
var self = this;
/* ========================================= */
/* PRIVATE ATTRIBUTE */
/* ========================================= */
// Initialized w/ arguments values when creating "object"
// (e.g. : var myVar = new Module("35d98q452e");
self.uniqueId = _uniqueId; // e.g. : "35d98q452e"
/* ========================================= */
/* PUBLIC METHODS */
/* ========================================= */
/*-------------------------------------
/* ---> Initialize tooltips */
self.constructor.prototype.init_tooltips = function() {
$( "#tabContent_" + self.uniqueId + " .tooltip" ).each(function(index) {
$(this).tooltip();
});
}
};
MODULE "OPERATION" SPECIFIC JS FILE "object.Operation.js"
var Operation = function(_uniqueId) {
var self = this;
/* ========================================= */
/* INHERIT FROM PARENT OBJECT "Module" */
/* ========================================= */
Module.call(self, _uniqueId);
/* ========================================= */
/* PUBLIC METHODS */
/* ========================================= */
/*-------------------------------------
/* ---> Initialize button "Edit operation" */
self.init_buttonEditOperation = function(_msg) {
$('#tabContent_' + self.uniqueId + ' [id^="buttonEditOperation_"]')
.click( function() { alert(_msg); })
.button(blabla);
}
};
While separating your HTML and Javascript is a good idea, generating a Javascript file may be overkill for achieving this and also be a lot harder to maintain than it should be.
The best way i've found is to add simple plugin functions to Smarty to allow you to generate all of the HTML and Javascript associated with 'widgets' that appear many times throughout your site.
These plugin functions should:
PHP code to add functions to Smarty
$GLOBALS['pageJavascript'] = array();
$smarty->registerPlugin("function", "addButton", "addButtonSmarty");
$smarty->registerPlugin("function", "displayPageJavascript", "displayPageJavascript");
//Outputs the HTML for a button, generates the associated Javascript and either
//outputs that Javascript or saves it for later.
function addButtonSmarty($params, &$smarty){
if(isset($params['operation']) == FALSE){
return "operation not set.";
}
if(isset($params['tabID']) == FALSE){
return "tabID not set.";
}
$tabID = $params['tabID']
$operation = $params['operation'];
$uniqueID = "buttonEditOperation_".$operation;
addButtonJavascript($tabID, $operation);
echo "<button id='buttonEditOperation_".$operation."' />";
}
function addButtonJavascript($tabID, $operation){
$javascript = "
$('#tabContent_".$tabID." [id^=\"buttonEditOperation_".$operation."\"]')
.click( function() { alert('CLICK !'); })
.button(blabla);";
if(FALSE){
//You can just add the Javascript directly to the web page, however in this case
//it may throw an error if the user clicks on something before jQuery is
//fully loaded.
echo "<script type="text/javascript">";
echo $javascript;
echo "</script>";
}
else{
//Much better is to save the Javascript to be included later.
$GLOBALS['pageJavascript'][] = $javascript;
}
}
function displayPageJavascript(){
echo "<script type="text/javascript">";
foreach($GLOBALS['pageJavascript'] as $pageJavascript){
echo $pageJavascript;
echo "
";
}
echo "</script>";
}
Wherever you add a button, instead of adding it directly, do: {addButton tabID=$uniqueId operation=$op}
So your Smarty template would look like:
Smarty Template
<script type="text/javascript" src="./js/buttonInit.js"></script>
<div id="tabContent_{$uniqueId}">
<table>
{foreach $operations as $op}
<tr><td>{addButton tabID=$uniqueId operation=$op}</td></tr>
{/foreach}
</table>
</div>
...
...
...
<!-- bottom of smarty template -->
{displayPageJavascript}
Before anyone jumps in and says that writing HTML and Javascript in PHP is ugly and shouldn't be done - I fully agree!
However in this case the benefits of writing widgets as plugins in Smarty so outweigh the uglyness: