JSF2 + AJAX:保持Scrollbar的位置,简单的mojarra

时间:2012-03-08 12:13:22

标签: jsf-2

我在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工作?

1 个答案:

答案 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。