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".
When JSF/ajax renders a view, the HTML DOM tree is partially updated/replaced with new elements. The
addButtonelement in your JSyourAppendChild()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 theaddButtonelement directly from thedocumentinstead of fromdata.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.