JSF/PrimeFaces ajax updates breaks jQuery event li

2019-01-03 07:14发布

I am registering a change event listener for every input in the HTML using jQuery as below:

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

Both change events are being triggered if the user changes the content of myTextarea. However after pressing the update button, which partially updates the myTextarea, only the changeHandler is triggering afterwards. The event bound in $(document).ready() is not triggering anymore.

Is this PrimeFaces related and/or an expected behavior? If yes then how can I ensure to trigger the second event without rerunning document ready script again.

1条回答
We Are One
2楼-- · 2019-01-03 07:40

As to the cause of your problem, the ajax request will update the HTML DOM tree with new HTML elements from the ajax response. Those new HTML elements do —obviously— not have the jQuery event handler function attached. However, the $(document).ready() isn't re-executed on ajax requests. You need to manually re-execute it.

This can be achieved in various ways. The simplest way is to use $(document).on(event, selector, function) instead of $(selector).on(event, function). This is tied to the document and the given functionRef is always invoked when the given eventName is triggered on an element matching the given selector. So you never need to explicitly re-execute the function by JSF means.

$(document).on("change", ":input", function() {
    console.log("From change event on any input: " + this.id);
});

The alternative way is to explicitly re-execute the function yourself on complete of ajax request. This would be the only way when you're actually interested in immediately execute the function during the ready/load event (e.g. to directly apply some plugin specific behavior/look'n'feel, such as date pickers). First, you need to refactor the $(document).ready() job into a reusable function as follows:

$(document).ready(function(){
    applyChangeHandler();
});

function applyChangeHandler() {
    $(":input").on("change", function() {
        console.log("From applyChangeHandler: " + this.id);
    });
}

(note that I removed and simplified your completely unnecessary $.each() approach)

Then, choose one of the following ways to re-execute it on complete of ajax request:

  1. Use the oncomplete attribute of the PrimeFaces command button:

    oncomplete="applyChangeHandler()"
    
  2. Use <h:outputScript target="body"> instead of $(document).ready(),

    <h:outputScript id="applyChangeHandler" target="body">
        applyChangeHandler();
    </h:outputScript>
    

    and reference it in update attribute:

    update=":applyChangeHandler"
    
  3. Use <p:outputPanel autoUpdate="true"> to auto update it on every ajax request:

    <p:outputPanel autoUpdate="true">
        <h:outputScript id="applyChangeHandler">
            applyChangeHandler();
        </h:outputScript>
    </p:outputPanel>
    
  4. Use OmniFaces <o:onloadScript> instead of $(document).ready(), <h:outputScript> and all on em.

    <o:onloadScript>applyChangeHandler();</o:onloadScript>
    
查看更多
登录 后发表回答