I have a more complex webapp in JSF2 (+EJB3.1 +JPA2 on glassfish 3.1), which only uses standard (mojarra) JSF-components and makes massive use of nested composites and ajax-calls.
I want all my scrollbars save and restore their position whenever an ajax-call occurs.
I tried different approaches, but none seems really good to me, so I need some hints which way to go:
1) JavaScript:
Add a JavaScript which reads all scrollbar-positions either when a scroll occurs (element.onScroll must be set by javascript, cause this attribute is not available in XHTML4) or when an ajax-request occurs (jsf.ajax.addOnEvent(savePositions)).
Save the scrollbars’ positions either in hidden input-fields or in cookie.
Restore them when the ajax-response occurs (jsf.ajax.addOnEvent(restorePositions)).
Contras if not using cookies:
-The scroll position must be stored in the hidden input field before the ajax-request occurs, so the element.onScroll-attribute must be used here. Not really nice, cause it saves the positions many many times, although one time before each ajax-request would be sufficient.
-All hidden input fields must be transferred in the ajax-call. Since the JSF-site uses several forms, there seems no way to automatically add them to all ajax-calls. Instead every element needs the hidden input fields added to the execute-attribute.
-One hidden input field is needed for every scrollable element.
Contra if using cookies:
-well, cookies need to be enabled for the website.
Contra in general:
-JavaScript code must either know or iterate through all elements, which have scrollbars.
-JavaScript code must be executed again, if a componenent is re-rendered and needs the onScroll-attribute set.
2) JavaScript + Composite:
So I thought of writing a composite scrollStateSave, which points to the JSF-id of the element, which has the scrollbars. The composite contains the hidden input field (or cookie) and javascript and handles everything, so I just need to add one “instance” of the composite per element, which has scrollbars. The javascript makes use of closures to work for multiple elements on one site.
Contra:
-The javascript inside the composite is not executed on a re-render after an ajax-call. There are workarounds for this, but they look ugly to me.
3) Myfaces has an option AUTO_SCROLL:
How does it work exactly? Does it work for non-myfaces-jsf-components?
4) Tomahawk offers a t:autoScroll-behaviour:
Using the tomahawk-replacements for the standard-mojarra-jsf2-components would be ok for me. But documentation of t:autoscroll speaks of an attribute “event”, while implementation needs attribute “value”. What should I put in this attribute to make t:autoScrolll work?
OK, I finished solution 2 and it is working quite nicely. In order to give other developers some help and in order to get hints, which parts of my solution could be better, I will post the code.
Composite /WebContent/resources/components/scrollbarStateSaver.xhtml:
Javascript /WebContent/resources/js/scrollbar.js:
Small example xhtml:
Of course, the composite could be enhanced to calculate the jsf-id via #{cc.parent} and #{cc.clientId} itself, then the for-attribute of the composite could handle jsf-ids, if the composite is inserted on the same level as the h:panelGroup.
The JavaScript can also probably be solved better, but it actually was some of my first javascript ever.