jQuery/PHP/Smarty - 非侵入式 JS - 在动态元素 ID(PHP 变量)上设置处理程序


jQuery/PHP/Smarty - Non-intrusive JS - Set handlers on dynamic element ID (PHP variables)

我正在尝试在我的web应用程序中分离html和jQuery事件处理程序。这是我的上下文:我的网络应用程序适用于带有 ajax 加载内容的选项卡。每个选项卡内容都由唯一 ID 标识。在每个选项卡中,我都有一个表格。两个或多个标签页内容可以相同(例如,在 Chrome 中,同一个网站可以在两个标签页中加载)。在选项卡中,每个表行都可以通过单击 jQuery 初始化按钮进行编辑。

这是一个简化的示例,它目前是如何工作的:

智能模板

<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>

当我加载具有相同内容的新选项卡时,由于选择器#tabContent_{$uniqueId}仅初始化此新内容中的按钮

我想创建一个javascript文件来分离JS和我的SMARTY模板,但我不知道如何仅选择新加载选项卡的内容。我想要这样的东西:

智能模板

<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 文件

<script> 
     $('#tabContent_?????? [id^="buttonEditOperation_"]')  // HERE IS MY PROBLEM
       .click( function() { alert('CLICK !'); })
       .button(blabla);
</script>

我有一些想法(HTML5数据属性,初始化函数,全局变量等),但我想知道这个问题的最佳解决方案。

感谢您的反馈!

虽然将

HTML和Javascript分开是一个好主意,但生成Javascript文件对于实现这一目标来说可能是矫枉过正的,而且维护起来也比应有的困难得多。

我发现的最好的方法是向 Smarty 添加简单的插件函数,以允许您生成与"小部件"相关的所有 HTML 和 Javascript,这些小部件在您的站点中多次出现。

这些插件函数应:

  • 为小部件生成所有 HTML,这样您就不必在不同的 Smarty 模板之间重复 HTML。
  • 生成小部件所需的所有Javascript,这样你就不必在Smarty模板中编写Javascript(这几乎总是一个坏主意 - 你应该只调用函数)。
  • 将 Javascript 的生成与页面中的实际输出分开,以允许您控制 Javascript 的实际放置位置。

用于向 Smarty 添加函数的 PHP 代码

$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 "'n";
    }
    echo "</script>";
}
无论在哪里添加按钮,而不是

直接添加按钮,请执行以下操作: {addButton tabID=$uniqueId operation=$op}

因此,您的 Smarty 模板如下所示:

聪明的模板

<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}

在有人跳进来说用PHP编写HTML和Javascript是丑陋的,不应该这样做之前 - 我完全同意!

然而,在这种情况下,在 Smarty 中将小部件编写为插件的好处超过了丑陋:

  • 它使用于操作小部件的 Javascript 位于小部件的 HTML 的几行内,这使得编写和调试该小部件比将它们放在单独的文件中要容易得多。
  • 在智能模板中隐藏小部件的实现,意味着您可以在一个地方更改一次它的实现,并确保该小部件的每次使用都会更新。
  • 它使代码更加可靠,因为模板编辑器不必记住为他们添加的任何小部件单独添加 Javascript,因此在编辑模板时的错误会少得多。

请参阅下面我找到的最佳解决方案!如果我做错了什么,请随时告诉我......

智能模板

<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>

主JS文件:"对象。模块.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();
        }); 
    }
};

模块"操作"特定JS文件"对象。操作.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);
    }
};