[ http://issues.apache.org/jira/browse/MYFACES-1277?page=comments#action_12373949 ]
Chad Lyon commented on MYFACES-1277: ------------------------------------ >> 3. In order to dynamically focus a page with multiple buttons or links the >> user is burdoned with writing server-side backing code to handle the focus >> logic. >I don't understand this statement. I appologize, I was trying to be brief due to time constraints. I mean if you have a bunch of buttons/links on a page and and want to re-focus that button/link when it is pressed (this is usually what people want to do) you would have to bind the "for" attribute of the "focus" control to an expression rather than giving it a static control ID. The evaluation of that expression would contain the logic for which control recieves focus based on the button/link pressed. >What I'd like to see is a way for it to work with a focus component instead of >forcing the end-user to write javascript on their page. Ahhh, end-users are not forced to write any javascript unless they want a control that doesn't normally do an HTTP post to submit the HTML form (e.g. postback on value change) by specifying the onchange javascript event. This same rule goes for AutoScroll too! In this case they will have to write javascript if they want that control (i.e. a SelectOneMenu, inputText, etc) to receive focus again after the page reloads. Actually, they should be forced to write that javascript anyway. First of all they are introducing javascript simply by using the "onchange" event. Furthermore, there are too many "what ifs" for controls other than commnandLink and commandButton. What I mean by this is that there are too many ways to fire the onchange event. If you click on an inputText field, change the value, and then click on some other field then the event fires. There is no way to tell which field the user was intending to focus next. Then there is the case of tabbing to the field, changing it and then tabbing off. Still, because tab index can be specified, there is no way to know where to focus next. The javascript I suggest above simply re-focuses the field (and re-scrolls) that caused the onchange event to get fired. It is just a suggestion to get people started. It also shows how to re-scroll if you are doing this "postback on value change." > Auto-Focus functionality similar to Auto-Scroll. [PATCH] > -------------------------------------------------------- > > Key: MYFACES-1277 > URL: http://issues.apache.org/jira/browse/MYFACES-1277 > Project: MyFaces Core > Type: New Feature > Components: General > Reporter: Chad Lyon > Attachments: Myfaces_392980_AutoFocus.patch > > I have created the attached patch from revision 392980 to add the ability for > the page to automatically return focus to the control (link, or button) that > submitted the page. This functionality mimics the auto-scroll functionality > and is configurable VIA a similar context parameter in the deployment > descriptor (org.apache.myfaces.AUTO_FOCUS). > ---Begin Patch--- > Index: > shared/core/src/main/java/org/apache/myfaces/shared/config/MyfacesConfig.java > =================================================================== > --- > shared/core/src/main/java/org/apache/myfaces/shared/config/MyfacesConfig.java > (revision 392980) > +++ > shared/core/src/main/java/org/apache/myfaces/shared/config/MyfacesConfig.java > (working copy) > @@ -56,16 +56,20 @@ > private static final String INIT_PARAM_AUTO_SCROLL = > "org.apache.myfaces.AUTO_SCROLL"; > private static final boolean INIT_PARAM_AUTO_SCROLL_DEFAULT = false; > > + private static final String INIT_PARAM_AUTO_FOCUS = > "org.apache.myfaces.AUTO_FOCUS"; > + private static final boolean INIT_PARAM_AUTO_FOCUS_DEFAULT = false; > + > private static final String INIT_PARAM_ADD_RESOURCE_CLASS = > "org.apache.myfaces.ADD_RESOURCE_CLASS"; > private static final String INIT_PARAM_ADD_RESOURCE_CLASS_DEFAULT = > "org.apache.myfaces.renderkit.html.util.DefaultAddResource"; > > private static final String INIT_CHECK_EXTENSIONS_FILTER = > "org.apache.myfaces.CHECK_EXTENSIONS_FILTER"; > private static final boolean INIT_CHECK_EXTENSIONS_FILTER_DEFAULT = true; > - > + > private boolean _prettyHtml; > private boolean _detectJavascript; > private boolean _allowJavascript; > private boolean _autoScroll; > + private boolean _autoFocus; > private String _addResourceClass; > private boolean _checkExtensionsFilter; > > @@ -86,14 +90,16 @@ > > INIT_PARAM_DETECT_JAVASCRIPT_DEFAULT)); > myfacesConfig.setAutoScroll(getBooleanInitParameter(extCtx, > INIT_PARAM_AUTO_SCROLL, > > INIT_PARAM_AUTO_SCROLL_DEFAULT)); > + myfacesConfig.setAutoFocus(getBooleanInitParameter(extCtx, > INIT_PARAM_AUTO_FOCUS, > + INIT_PARAM_AUTO_FOCUS_DEFAULT)); > myfacesConfig.setAddResourceClass(getStringInitParameter(extCtx, > INIT_PARAM_ADD_RESOURCE_CLASS, > > INIT_PARAM_ADD_RESOURCE_CLASS_DEFAULT)); > myfacesConfig.setAddResourceClass(getStringInitParameter(extCtx, > INIT_PARAM_ADD_RESOURCE_CLASS, > INIT_PARAM_ADD_RESOURCE_CLASS_DEFAULT)); > - > + > > myfacesConfig.setCheckExtensionsFilter(getBooleanInitParameter(extCtx, > INIT_CHECK_EXTENSIONS_FILTER, > INIT_CHECK_EXTENSIONS_FILTER_DEFAULT)); > - > + > return myfacesConfig; > } > > @@ -183,6 +189,16 @@ > _autoScroll = autoScroll; > } > > + public boolean isAutoFocus() > + { > + return _autoFocus; > + } > + > + private void setAutoFocus(boolean autoFocus) > + { > + _autoFocus = autoFocus; > + } > + > private void setAddResourceClass(String addResourceClass) > { > _addResourceClass = addResourceClass; > @@ -211,7 +227,7 @@ > } > > /** > - * Should the environment be checked so that the ExtensionsFilter will > work properly. > + * Should the environment be checked so that the ExtensionsFilter will > work properly. > */ > public boolean isCheckExtensionsFilter() > { > Index: > shared/core/src/main/java/org/apache/myfaces/shared/renderkit/html/HTML.java > =================================================================== > --- > shared/core/src/main/java/org/apache/myfaces/shared/renderkit/html/HTML.java > (revision 392980) > +++ > shared/core/src/main/java/org/apache/myfaces/shared/renderkit/html/HTML.java > (working copy) > @@ -91,7 +91,7 @@ > String STYLE_ATTR = "style"; > String TITLE_ATTR = "title"; > String STYLE_CLASS_ATTR = "styleClass"; //"class" cannot be used as > property name > - > + > String[] UNIVERSAL_ATTRIBUTES_WITHOUT_STYLE = > { > DIR_ATTR, > Index: > shared/core/src/main/java/org/apache/myfaces/shared/renderkit/html/HtmlButtonRendererBase.java > =================================================================== > --- > shared/core/src/main/java/org/apache/myfaces/shared/renderkit/html/HtmlButtonRendererBase.java > (revision 392980) > +++ > shared/core/src/main/java/org/apache/myfaces/shared/renderkit/html/HtmlButtonRendererBase.java > (working copy) > @@ -174,7 +174,7 @@ > } > String formName = formInfo.getFormName(); > UIForm nestingForm = formInfo.getForm(); > - > + > StringBuffer onClick = new StringBuffer(); > String commandOnClick = > (String)uiComponent.getAttributes().get(HTML.ONCLICK_ATTR); > > @@ -192,6 +192,11 @@ > > org.apache.myfaces.shared.renderkit.html.util.JavascriptUtils.appendAutoScrollAssignment(onClick, > formName); > } > > + if > (MyfacesConfig.getCurrentInstance(facesContext.getExternalContext()).isAutoFocus()) > + { > + > org.apache.myfaces.shared.renderkit.html.util.JavascriptUtils.appendAutoFocusAssignment(onClick, > formName); > + } > + > //add hidden field for the case there is no commandLink in the form > String hiddenFieldName = > HtmlRendererUtils.getHiddenCommandLinkFieldName(formName); > addHiddenCommandParameter(facesContext, nestingForm, > hiddenFieldName); > Index: > shared/core/src/main/java/org/apache/myfaces/shared/renderkit/html/HtmlFormRendererBase.java > =================================================================== > --- > shared/core/src/main/java/org/apache/myfaces/shared/renderkit/html/HtmlFormRendererBase.java > (revision 392980) > +++ > shared/core/src/main/java/org/apache/myfaces/shared/renderkit/html/HtmlFormRendererBase.java > (working copy) > @@ -89,7 +89,12 @@ > { > > org.apache.myfaces.shared.renderkit.html.util.JavascriptUtils.renderAutoScrollHiddenInput(facesContext,writer); > } > - > + > + if > (MyfacesConfig.getCurrentInstance(facesContext.getExternalContext()).isAutoFocus()) > + { > + > org.apache.myfaces.shared.renderkit.html.util.JavascriptUtils.renderAutoFocusHiddenInput(facesContext,writer); > + } > + > > if(!facesContext.getApplication().getStateManager().isSavingStateInClient(facesContext)) > { > writer.startElement(HTML.INPUT_ELEM, component); > Index: > shared/core/src/main/java/org/apache/myfaces/shared/renderkit/html/HtmlLinkRendererBase.java > =================================================================== > --- > shared/core/src/main/java/org/apache/myfaces/shared/renderkit/html/HtmlLinkRendererBase.java > (revision 392980) > +++ > shared/core/src/main/java/org/apache/myfaces/shared/renderkit/html/HtmlLinkRendererBase.java > (working copy) > @@ -238,6 +238,11 @@ > > org.apache.myfaces.shared.renderkit.html.util.JavascriptUtils.appendAutoScrollAssignment(onClick, > formName); > } > > + if > (MyfacesConfig.getCurrentInstance(facesContext.getExternalContext()).isAutoFocus()) > + { > + > org.apache.myfaces.shared.renderkit.html.util.JavascriptUtils.appendAutoFocusAssignment(onClick, > formName); > + } > + > //add id parameter for decode > String hiddenFieldName = > HtmlRendererUtils.getHiddenCommandLinkFieldName(formName); > onClick.append(jsForm); > @@ -298,7 +303,7 @@ > { > return _ComponentUtils.findNestingForm(uiComponent, facesContext); > } > - > + > protected void addHiddenCommandParameter(FacesContext facesContext, > UIForm nestingForm, String hiddenFieldName) > { > if (nestingForm != null) > Index: > shared/core/src/main/java/org/apache/myfaces/shared/renderkit/html/util/JavascriptUtils.java > =================================================================== > --- > shared/core/src/main/java/org/apache/myfaces/shared/renderkit/html/util/JavascriptUtils.java > (revision 392980) > +++ > shared/core/src/main/java/org/apache/myfaces/shared/renderkit/html/util/JavascriptUtils.java > (working copy) > @@ -47,6 +47,8 @@ > private static final String AUTO_SCROLL_PARAM = "autoScroll"; > private static final String AUTO_SCROLL_FUNCTION = "getScrolling()"; > > + private static final String AUTO_FOCUS_PARAM = "autoFocus"; > + > private static final String OLD_VIEW_ID = JavascriptUtils.class + > ".OLD_VIEW_ID"; > > > @@ -257,7 +259,7 @@ > { > session.setAttribute(JAVASCRIPT_DETECTED, Boolean.valueOf(value)); > } > - > + > public static boolean isJavascriptDetected(ExternalContext > externalContext) > { > //TODO/FIXME (manolito): This info should be better stored in the > viewroot component and not in the session > @@ -278,6 +280,17 @@ > } > > /** > + * Adds the hidden form input value assignment that is necessary for the > autofocus > + * feature to an html link or button onclick attribute. > + */ > + public static void appendAutoFocusAssignment(StringBuffer onClickValue, > String formName) > + { > + > onClickValue.append("document.forms['").append(formName).append("']"); > + > onClickValue.append(".elements['").append(AUTO_FOCUS_PARAM).append("']"); > + onClickValue.append(".value=this.id;"); > + } > + > + /** > * Renders the hidden form input that is necessary for the autoscroll > feature. > */ > public static void renderAutoScrollHiddenInput(FacesContext > facesContext, ResponseWriter writer) throws IOException > @@ -291,6 +304,19 @@ > } > > /** > + * Renders the hidden form input that is necessary for the autofocus > feature. > + */ > + public static void renderAutoFocusHiddenInput(FacesContext facesContext, > ResponseWriter writer) throws IOException > + { > + > org.apache.myfaces.shared.renderkit.html.HtmlRendererUtils.writePrettyLineSeparator(facesContext); > + > writer.startElement(org.apache.myfaces.shared.renderkit.html.HTML.INPUT_ELEM, > null); > + > writer.writeAttribute(org.apache.myfaces.shared.renderkit.html.HTML.TYPE_ATTR, > "hidden", null); > + > writer.writeAttribute(org.apache.myfaces.shared.renderkit.html.HTML.NAME_ATTR, > AUTO_FOCUS_PARAM, null); > + > writer.endElement(org.apache.myfaces.shared.renderkit.html.HTML.INPUT_ELEM); > + > org.apache.myfaces.shared.renderkit.html.HtmlRendererUtils.writePrettyLineSeparator(facesContext); > + } > + > + /** > * Renders the autoscroll javascript function. > */ > public static void renderAutoScrollFunction(FacesContext facesContext, > @@ -318,6 +344,7 @@ > " }\n" + > " return x + \",\" + y;\n" + > "}\n"); > + > ExternalContext externalContext = facesContext.getExternalContext(); > String oldViewId = getOldViewId(externalContext); > if (oldViewId != null && > oldViewId.equals(facesContext.getViewRoot().getViewId())) > @@ -342,12 +369,21 @@ > } > > script.append("window.scrollTo(").append(x).append(",").append(y).append(");\n"); > } > + > + // now focus the control that had focus before. > + String focus = > (String)externalContext.getRequestParameterMap().get(AUTO_FOCUS_PARAM); > + if (focus != null && focus.length() > 0) > + { > + script.append("\nif(document.getElementById('" + focus + "') > != null) {\n" + > + "document.getElementById('" > + focus + "').focus();\n" + > + "}\n"); > + } > } > > writer.writeText(script.toString(),null); > > > writer.endElement(org.apache.myfaces.shared.renderkit.html.HTML.SCRIPT_ELEM); > - HtmlRendererUtils.writePrettyLineSeparator(facesContext); > + HtmlRendererUtils.writePrettyLineSeparator(facesContext); > } > ---End Patch--- > Additionally, if anyone would like their page to return focus and scroll to > other controls (inputText, selectOneMenu, etc) on value change then add the > following javascript to your page: > function setScrolling(component) { > if(component.form.elements['autoScroll'] != null) > { > component.form.elements['autoScroll'].value=getScrolling(); > } > if(component.form.elements['autoFocus'] != null) > { > component.form.elements['autoFocus'].value=component.id; > } > } > And to your controls onchange attributes: > onchange="setScrolling(this);submit()" > For example: > <h:selectOneMenu id="state" immediate="true" > onchange="setScrolling(this);submit()" > value="#{someBean.state}" > valueChangeListener="#{someBean.stateChangeListener}"> > <f:selectItems value="#{someBean.stateOptions}" /> </h:selectOneMenu> -- This message is automatically generated by JIRA. - If you think it was sent incorrectly contact one of the administrators: http://issues.apache.org/jira/secure/Administrators.jspa - For more information on JIRA, see: http://www.atlassian.com/software/jira
