JSF/PrimeFaces ajax 更新破坏了 jQuery 事件侦听器函数绑定
Posted
技术标签:
【中文标题】JSF/PrimeFaces ajax 更新破坏了 jQuery 事件侦听器函数绑定【英文标题】:JSF/PrimeFaces ajax updates breaks jQuery event listener function bindings 【发布时间】:2013-01-02 06:19:50 【问题描述】:我正在使用 jQuery 为 html 中的每个 input
注册一个 change
事件侦听器,如下所示:
<h:head>
<script type="text/javascript">
//<![CDATA[
$(document).ready(function()
$(':input').each(function()
$(this).change(function()
console.log('From docReady: ' + this.id);
);
);
);
function changeHandler()
console.log("from changeHandler");
//]]>
</script>
</h:head>
<h:body>
<h:form id="topForm">
<p:commandButton id="myButton" value="update"
action="#testBean.submit"
partialSubmit="true" process=":myForm:panel"
update=":myForm:panel" />
</h:form>
<h:form id="myForm">
<p:panel id="panel">
<p:inputTextarea id="myTextarea"
value="#testBean.val"
onchange="changeHandler();"/>
</p:panel>
</h:form>
</h:body>
如果用户更改myTextarea
的内容,则会触发两个change
事件。然而,在按下更新按钮后,部分更新了myTextarea
,之后只有 changeHandler 被触发。 $(document).ready()
中绑定的事件不再触发。
这是否与 PrimeFaces 相关和/或预期行为?如果是,那么如何确保在不重新运行文档就绪脚本的情况下触发第二个事件。
【问题讨论】:
【参考方案1】:至于问题的原因,ajax 请求将使用来自 ajax 响应的新 HTML 元素更新 HTML DOM 树。那些新的 HTML 元素——显然——没有附加 jQuery 事件处理函数。但是,$(document).ready()
不会在 ajax 请求上重新执行。需要手动重新执行。
这可以通过多种方式实现。最简单的方法是使用$(document).on(event, selector, function)
而不是$(selector).on(event, function)
。这与文档相关联,并且当在与给定selector
匹配的元素上触发给定eventName
时,始终调用给定functionRef
。所以你永远不需要通过 JSF 方式显式地重新执行函数。
$(document).on("change", ":input", function()
console.log("From change event on any input: " + this.id);
);
另一种方法是在完成 ajax 请求时自己显式地重新执行该函数。当您真正有兴趣在ready
/load
事件期间立即执行该函数时,这将是唯一的方法(例如,直接应用某些插件特定的行为/外观,例如日期选择器)。首先,您需要将$(document).ready()
作业重构为可重用函数,如下所示:
$(document).ready(function()
applyChangeHandler();
);
function applyChangeHandler()
$(":input").on("change", function()
console.log("From applyChangeHandler: " + this.id);
);
(请注意,我删除并简化了您完全不必要的 $.each()
方法)
然后,选择以下方式之一在完成 ajax 请求时重新执行它:
使用 PrimeFaces 命令按钮的oncomplete
属性:
oncomplete="applyChangeHandler()"
使用<h:outputScript target="body">
代替$(document).ready()
,
<h:outputScript id="applyChangeHandler" target="body">
applyChangeHandler();
</h:outputScript>
并在update
属性中引用它:
update=":applyChangeHandler"
使用 <p:outputPanel autoUpdate="true">
在每个 ajax 请求上自动更新它:
<p:outputPanel autoUpdate="true">
<h:outputScript id="applyChangeHandler">
applyChangeHandler();
</h:outputScript>
</p:outputPanel>
使用OmniFaces <o:onloadScript>
而不是$(document).ready()
、<h:outputScript>
等等。
<o:onloadScript>applyChangeHandler();</o:onloadScript>
【讨论】:
或者您可以使用event delegation,这是专门为解决此类问题而设计的技术。 @Anthony:这仅适用于通过“plain jQuery”执行的 ajax 请求和通过“plain jQuery”附加的 HTML DOM 元素,因为它有自己的内部事件侦听器。跨度> 我的所有经验告诉我,这些说法都不正确。 @BalusC。感谢您发布。似乎第三个选择适合我的用例。我打算将该代码块放在 facelet 模板中。对于这第三种方式,我删除了 $(document).ready(function() 否则处理程序在初始页面加载时注册了两次,然后手动将 autoUpdatable p:outputPanel 放在正文的末尾。如果我最终找到我自己实现了 Omnifaces 已经提供的功能,肯定会试一试。:) @BalusC。有没有办法只在ajax响应之后才在新添加的元素上执行脚本?否则处理程序会一次又一次地绑定到元素。以上是关于JSF/PrimeFaces ajax 更新破坏了 jQuery 事件侦听器函数绑定的主要内容,如果未能解决你的问题,请参考以下文章
JSF/Primefaces AJAX 请求真的是异步的吗?
JSF/PrimeFaces ajax 请求上的会话超时和 ViewExpiredException 处理
JSF + PrimeFaces:`update` 属性不更新组件