1

I have a basic JSF page called Index.xhtml and a backing bean called TestBean.java. Basically I am trying to append a child to a td element in a table after a JSF's ajax call renders the table.

The codes are the following.

Index.xhtml:

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:ui="http://java.sun.com/jsf/facelets">
    <h:head>
        <h:outputScript library="javascript" name="surveyquestions.js"/>
    </h:head>
    <h:body>
        <h:outputLink value="reset.xhtml">reset</h:outputLink>

        <h:form id="myForm">
            <h:panelGroup id="myTable">
                <table>
                    <tr>
                        <td id="targetParent">
                            targetParent here.
                        </td>
                        <td>
                            <h:commandButton value="Click to append a child">
                                <f:ajax event="click" execute="@form"  render="myTable" listener="#{testBean.m()}" onevent="myAppendChild"  />
                            </h:commandButton>
                        </td>
                    </tr>
                </table>

            </h:panelGroup>
        </h:form>

    </h:body>
</html>

And the backing bean only has one method called m and it does nothing. I have two javascript functions and they are called in <f:ajax event="click" execute="@form" render="myTable" listener="#{testBean.m()}" onevent="myAppendChild" />:

function myAppendChild(data){
    if(data.status == "success"){
        var targetParent = document.getElementById("targetParent")
        alert(targetParent.nodeName);
        alert(targetParent.firstChild.nodeValue);
        var spanTag = document.createElement("span");
        spanTag.innerHTML="child";
        targetParent.appendChild(spanTag);

    }
}

function yourAppendChild(data){
    var addButton = data.source;
    if(data.status == "success"){
        var targetParent = addButton.parentNode.parentNode.cells[0];
        alert(targetParent.nodeName);
        alert(targetParent.firstChild.nodeValue);
        var spanTag = document.createElement("span");
        spanTag.innerHTML="child";
        targetParent.appendChild(spanTag);
    }
}

When I tried to append a child to the td element whose Id is targetParent, I fount that the first javascript function myAppendChild worked OK. However, the second function yourAppendChild only worked if I removed the render="myTable".

If I keep render="myTable", yourAppendChild runs to the end, calls the appendChild without error but somehow the child is not appended.

It seems to me that both functions got the exactly same element and tried to append a child to that element but the second function does not work with render="myTable".

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
Wang Sheng
  • 780
  • 1
  • 6
  • 18
  • You always better add/remove or hide/show stuff using JSF... the way you try to accomplish things will give you much troubles (INMO) – Daniel Jul 22 '12 at 09:08
  • Hi Daniel, thanks a lot for your kind reply. I think I will hide/show the child part using jsf in the end to solve this problem. Just curious why the second function doesn't work while the first one is ok xD. – Wang Sheng Jul 22 '12 at 09:40
  • well.. dunno i guess cause of `nulls` or `undefined` in `addButton.parentNode.parentNode.cells[0];` , just place a breakpoint in your browser js debugger and do some debugging... – Daniel Jul 22 '12 at 09:56
  • I used alert() to debug and it appears that the `addButton.parentNode.parentNode.cells[0];`is working ok. But thanks for the comment anyway^_^ – Wang Sheng Jul 22 '12 at 10:56

1 Answers1

1

When JSF/ajax renders a view, the HTML DOM tree is partially updated/replaced with new elements. The addButton element in your JS yourAppendChild() function, which is still part of the old HTML DOM tree before the render, isn't a member of the new HTML DOM tree anymore at the moment your JS function runs. You're basically traversing a dangling reference which doesn't point to anything in the current HTML DOM tree anymore. You basically need to grab the addButton element directly from the document instead of from data.source.

But I recommend to forget that JS approach and go for a sane and pure JSF approach. You may find this kickoff example helpful: Recommended JSF 2.0 CRUD frameworks.

Community
  • 1
  • 1
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • Hi @BalusC thanks a lot for your answer:-) When I used `alert(targetParent.nodeName);` and `alert(targetParent.firstChild.nodeValue);` I got the expected `nodeName` and `nodeValue`, is it that these `nodeName` and `nodeValue` belong to the `addButton` element which is part of the old DOM tree? But because the `yourAppendChild` function is called (for the third time) after the page is rendered and the DOM tree updated, by the time the `yourAppendChild` is called (for the third time), the old DOM tree should not exist anymore. – Wang Sheng Jul 24 '12 at 03:41
  • So it seems that I am referencing something of a non-existing tree and yet I got the `nodeName` and `nodeValue`:-) And thanks again for the example. I will try to use less JS and also try to stay with pure JSF approach xD. – Wang Sheng Jul 24 '12 at 04:02