我在JSF2(Glassfish 3.1上的+ EJB3.1 + JPA2)中有一个更复杂的webapp,它只使用标准(mojarra)JSF组件,并大量使用嵌套复合和ajax调用。
我希望所有滚动条在发生ajax调用时保存并恢复其位置。
我尝试了不同的方法,但似乎没有一个对我很好,所以我需要一些提示:
1)JavaScript:
添加一个JavaScript,它在滚动发生时读取所有滚动条位置(element.onScroll必须由javascript设置,因此该属性在XHTML4中不可用)或者当发生ajax请求时(jsf.ajax.addOnEvent( savePositions))。 将滚动条的位置保存在隐藏的输入字段或cookie中。 发生ajax响应时恢复它们(jsf.ajax.addOnEvent(restorePositions))。
如果不使用cookies,则反对:
- 在发生ajax请求之前,滚动位置必须存储在隐藏的输入字段中,因此必须在此处使用element.onScroll-attribute。不是很好,因为它可以多次保存位置,尽管在每次ajax请求之前就足够了。
- 必须在ajax调用中传输所有隐藏的输入字段。由于JSF站点使用多种形式,因此似乎无法自动将它们添加到所有ajax调用。相反,每个元素都需要添加到execute-attribute的隐藏输入字段。
- 每个可滚动元素都需要一个隐藏的输入字段。
使用cookies时反对:
-well,需要为网站启用cookie。
一般情况下反对:
-JavaScript代码必须知道或遍历所有具有滚动条的元素。
-JavaScript代码必须再次执行,如果重新呈现组件并且需要onScroll属性集。
2)JavaScript + Composite:
所以我想写一个复合scrollStateSave,它指向元素的JSF-id,它有滚动条。复合包含隐藏的输入字段(或cookie)和javascript并处理所有内容,因此我只需要为每个元素添加一个具有滚动条的复合“实例”。 javascript使用闭包来为一个站点上的多个元素工作。
魂斗罗:
- 在ajax调用之后,复合内的javascript不会在重新渲染时执行。有这方面的解决方法,但它们看起来很难看。
3)Myfaces有一个选项AUTO_SCROLL:
它是如何工作的?它适用于非myfaces-jsf组件吗?
4)Tomahawk提供了一个t:autoScroll-behavior:
使用标准mojarra-jsf2组件的tomahawk替换对我来说没问题。但是t:autoscroll的文档说的是属性“event”,而实现需要属性“value”。我应该在此属性中添加什么来使t:autoScrolll工作?
答案 0 :(得分:3)
好的,我完成了解决方案2并且它运行得非常好。为了给其他开发人员一些帮助,为了获得提示,我的解决方案的哪些部分可能更好,我将发布代码。
复合/WebContent/resources/components/scrollbarStateSaver.xhtml:
<!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:composite="http://java.sun.com/jsf/composite">
<h:body>
<composite:interface>
<composite:attribute name="for"/>
</composite:interface>
<composite:implementation>
<h:outputScript name="scrollbars.js" library="js"/>
<script type="text/javascript">
saveScrollbarPos("#{cc.attrs.for}");
</script>
</composite:implementation>
</h:body>
</html>
Javascript /WebContent/resources/js/scrollbar.js:
function saveScrollbarPos(id) {
var scrollbarid = id;
function savePos() {
var scrollbar = document.getElementById(scrollbarid);
document.cookie = scrollbarid+".scrolltop="+scrollbar.scrollTop+"; path=/";
}
function readCookie(name) {
var nameEQ = name + "=";
var ca = document.cookie.split(';');
for(var i=0;i < ca.length;i++) {
var c = ca[i];
while (c.charAt(0)==' ') c = c.substring(1,c.length);
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
}
return null;
}
function restorePos() {
var scrollbar = document.getElementById(scrollbarid);
scrollbar.scrollTop = readCookie(scrollbarid+".scrolltop");
}
function onStatusChange(data) {
var status = data.status;
if (status == "begin") {
savePos();
}
else {
restorePos();
}
};
var scrollbar = document.getElementById(scrollbarid);
if (scrollbar != null) {
jsf.ajax.addOnEvent(onStatusChange);
jsf.ajax.addOnError(onStatusChange);
}
};
小例子xhtml:
<!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:c="http://java.sun.com/jsf/composite/components">
<h:head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Title</title>
</h:head>
<h:body>
<h:form id="form">
<div id="panel" style="overflow:auto;">
long content with ajax-calls in it
</div>
<c:scrollbarStateSaver for="panel"/>
<h:panelGroup id="panel" style="overflow:auto;">
long content with ajax-calls in it
</h:panelGroup>
<c:scrollbarStateSaver for="form:panel"/>
</h:form>
</h:body>
</html>
当然,可以增强复合以通过#{cc.parent}和#{cc.clientId}本身计算jsf-id,然后复合的for-attribute可以处理jsf-id,如果复合插入与h:panelGroup相同的级别。
JavaScript也可能更好地解决,但它实际上是我的第一个javascript。