通过 Ajax 更新 JSF 组件后,JavaScript/jQuery 事件侦听器不起作用

Posted

技术标签:

【中文标题】通过 Ajax 更新 JSF 组件后,JavaScript/jQuery 事件侦听器不起作用【英文标题】:JavaScript/jQuery event listeners do not work after JSF component is updated via Ajax 【发布时间】:2013-12-12 06:40:41 【问题描述】:

我正在<p:dataTable> 过滤器(其 id 为 id)上执行以下 jQuery 函数,该函数允许用户在过滤器组件中仅输入数字。

$(document).ready(function() 
    $("#form\\:dataTable\\:id\\:filter").keydown(function(event) 
        //Allow: tab, escape, and enter
        if(event.keyCode===9||event.keyCode===27||event.keyCode===13||
            //Allow: Ctrl+A, Ctrl+C
            (event.keyCode===65&&event.ctrlKey===true)||(event.keyCode===67&&event.ctrlKey===true)||
            //Allow: home, end, left, right
            (event.keyCode>=35&&event.keyCode<=39))
            //let it happen, don't do anything
            event.preventCapture();
            return;
        //backspace, delete
        else if(event.keyCode===46||event.keyCode===8)
        
            return;
        
        else//Ensure that it is a number and stop the keypress
            if (event.shiftKey||(event.keyCode<48||event.keyCode>57)&&(event.keyCode< 96||event.keyCode>105))
                //event.preventDefault();
                event.preventCapture();
            
        
    );
);

这个函数放在context/resources/default/js/digit_only_textfield.js下。因此,它可以用于 Xhtml 页面,例如,

<h:outputScript library="default" name="js/digit_only_textfield.js"/>

XHTML 页面如下所示。

<h:outputScript library="default" name="js/digit_only_textfield.js"/>

<h:form id="form" prependId="true">

    <!--PrimeFaces extension <pe:blockUI>-->         

    <p:remoteCommand name="updateTable" update="dataTable"/>

    <p:panel id="panel">
        <h:panelGrid id="panelGrid" columns="3" cellpadding="5">

            <!--Some UIInput components-->

            <p:commandButton id="btnSubmit" 
                             update="panel" 
                             onstart="PF('blockUIWidget').block();" 
                             oncomplete="if(!args.validationFailed) updateTable();PF('blockUIWidget').unblock();" 
                             actionListener="#bean.insert" 
                             value="Save"/>
        </h:panelGrid>
    </p:panel>

    <p:dataTable id="dataTable" 
                 var="row" 
                 value="#bean"
                 filterEvent="keydown"

                 ...
                 ...  >

                 ...
                 ...
    <p:dataTable>
<h:form>

这个 jQuery 对 id 为 is 的过滤器工作正常,但是当这个 &lt;p:dataTable&gt; 通过按下给定的&lt;p:commandButton&gt; 更新时,它停止运行。

&lt;p:dataTable&gt; 被 AJAX 更新后,如何使该功能生效?


引入了一个新的问题域:

This PrimeFaces 社区论坛上的问题和相应回复仍然无法解决以下问题。

如果按下了错误的键(即非数字键,退格键除外,请删除 等)然后,数据表被不必要地更新,导致一些 对数据库进行昂贵的查询,这完全是 不必要的并且必须防止更新数据表

【问题讨论】:

在表格更新后,您是否尝试过再次运行 jQuery keydown 函数?如果表格在更新时由 PrimeFaces 从头开始​​渲染,则 jQuery on event trigger 将被删除。 只是PrimeFaces数据表中的一个过滤事件-&lt;p:dataTable&gt;,filterEvent="keydown"的一个属性。 我不明白赏金和明显的新问题。 我本可以提出一个单独的问题,但除了问题中的块引用中的文本之外,大多数情况下情况相同。 这不是您自定义 jQuery 函数的全部意图吗?还是那部分不起作用? 【参考方案1】:

流程如下:

浏览器检索 HTML 输出。 浏览器根据 HTML 标记填充 HTML DOM 树。 完成后,浏览器触发 HTML DOM ready 事件。 jQuery 的$(document).ready() 函数处理程序都被调用。 Yours 通过 ID 在 DOM 中找到一个元素,并将 keydown 侦听器附加到它。 在用户交互期间,会触发 ajax 请求,并使用 ajax 响应提供的新 HTML 元素更新 HTML DOM 树。 除其他外,具有keydown 侦听器的元素将从HTML DOM 树中删除,并替换为没有任何keydown 侦听器的新元素。在 ajax 请求期间不会触发文档 ready 事件。您的就绪处理程序永远不会重新调用。 keydown 监听器永远不会重新连接。对于最终用户来说,它确实看起来“停止运行”。

这种特殊情况的解决方案现在应该很明显了:在完成 ajax 调用时显式重新附加 keydown 侦听器。最直接的方法是将 keydown 侦听器附加到可重用函数中并按如下方式触发它:

function applyKeydownOnTableFilter()  
    // ...


$(document).ready(applyKeydownOnTableFilter);

这样你就可以做一个:

<p:commandButton ... oncomplete="applyKeydownOnTableFilter()" />

但是对于每个单独的 ajax 命令/侦听器重复这是非常乏味的,并且对维护不是很友好。更好的方法是采用不同的方法:改用 jQuery 的 $.on()。替换

$(document).ready(function() 
    $("#form\\:dataTable\\:id\\:filter").keydown(function(event) 
        // ...
    );
);

通过

$(document).on("keydown", "#form\\:dataTable\\:id\\:filter", function(event) 
    // ...
);

这样keydown 监听器实际上并没有附加到感兴趣的元素上。相反,由于 javascript 的事件冒泡特性,keydown 事件最终会到达$(document)——它始终存在并且在 JSF ajax 请求期间通常不会更改。一旦到达,$(document).on() 就会被触发,然后它将确定事件的来源并检查它是否与给定的选择器匹配,如果是,则调用该函数。这一切都不需要将keydown 侦听器附加到物理元素,因此对元素是否在 HTML DOM 树中被删除/替换不敏感。

另见:

JSF/PrimeFaces ajax updates breaks jQuery event listener function bindings

顺便问一下,您是否也看到 HTML DOM 树和 JSF 组件树之间有多少相似之处?

【讨论】:

一如既往的感谢。 $.on() 的方法按预期工作。有一件小事,但是我无法弄清楚。在使用$.on() 时,如果按下了错误的键(即任何非数字键),则数据表会被不必要地更新,这会导致在数据库上执行一些代价高昂的查询,这是完全不必要的。当按下错误的键时,是否有任何解决方法可以防止数据表被更新。这应该由event.preventDefault(); 单独完成,但事实并非如此。在问题中使用$(document).ready(function() ); 时,这并没有发生。 哦,对了 :( PrimeFaces 不直接使用 onkeydown 属性,而是通过 jQuery 附加一个事件处理程序。这是在您的事件处理程序之前触发的。所以,基本上,你来晚了......对不起,你必须切换回ready事件,以便首先附加你的事件。为了尽量减少oncomplete的混乱,你可能想在一个页面中执行这项工作- &lt;p:ajaxStatus oncomplete&gt;$.ajaxStop() 的广泛方式。 至于这个问题的具体要求,PrimeFaces 5.0(目前作为候选发布-RC1)自带了方便的数据表过滤器data table filters中有一个major enhancement。因此,可以完全消除对数据表过滤器的此类客户端验证:)

以上是关于通过 Ajax 更新 JSF 组件后,JavaScript/jQuery 事件侦听器不起作用的主要内容,如果未能解决你的问题,请参考以下文章

JSF Ajax Update 不更新组件

JSF(Primefaces)通过ID更新几个元素的ajax

JSF 复合组件 <f:ajax> 包含未知 id - 无法在组件的上下文中找到它

为什么使用f:ajax(JSF)更新后,我的Materialize可折叠菜单不起作用?

JSF 2 -- 保存所有有效的组件值

使用 f:ajax 时 JSF 不触发 bean setter