如何使用tab键聚焦p:selectBooleanButton组件?

时间:2012-09-21 11:09:17

标签: java jsf-2 primefaces

我们开始使用PrimeFaces 3.4,JSF 2.0和Tomcat 7.0进行开发。我们面临的问题是,当我们创建表单页面时,我们可以使用所有PrimeFaces输入组件上的选项卡按钮进行导航,期望<p:selectBooleanButton>。例如,

<h:form id="formId">
    <p:inputText id="inputId1" />
    <p:inputText id="inputId2" />
    <p:selectBooleanButton id="buttonId" onLabel="Yes" offLabel="No" />
    <p:inputText id="inputId3" />
    <p:inputText id="inputId4" />
</h:form>

inputId2中的标签直接转到inputId3。这是预期的行为吗?有没有解决方法?

2 个答案:

答案 0 :(得分:7)

这是因为PrimeFaces SelectBooleanButtonRenderer实际呈现了表示<p:selectBooleanButton>状态的复选框的方式:

<div id="formId:buttonId" type="button" class="ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only">
    <input id="formId:buttonId_input" name="formId:buttonId_input" type="checkbox" class="ui-helper-hidden">
    <span class="ui-button-text">no</span>
</div>

该复选框完全被display:none类中的CSS .ui-helper-hidden属性隐藏,因此永远不会获得焦点。

如果我们查看对应的<p:selectBooleanCheckbox>复选框,它也会将视觉上更具吸引力的小部件替换为 实际可关注的小部件,然后我们会看到该复选框未完全隐藏通过CSS,但只是被<div>封装在position:absolute类中的CSS .ui-helper-hidden-accessible绝对定位而被隐藏起来,因此只是被复选框小部件覆盖:

<div id="formId:checkboxId" class="ui-chkbox ui-widget">
    <div class="ui-helper-hidden-accessible">
        <input id="formId:checkboxId_input" name="formId:checkboxId_input" type="checkbox">
    </div>
    <div class="ui-chkbox-box ui-widget ui-corner-all ui-state-default">
        <span class="ui-chkbox-icon"></span>
    </div>
</div>

我不认为<p:selectBooleanButton>是不可聚焦的“预期”或“直觉”行为,如果我是你,我肯定report这个用户体验对PrimeFaces很重要。


同时,解决此问题的最佳方法是创建一个自定义渲染器,该渲染器会覆盖PrimeFaces SelectBooleanButtonRenderer的{​​{3}}方法,如下所示,以便从class="ui-helper-hidden"中移除<div class="ui-helper-hidden-accessible>复选框并将其打包在<p:selectBooleanCheckbox>中,与public class MySelectBooleanButtonRenderer extends SelectBooleanButtonRenderer { @Override protected void encodeMarkup(FacesContext context, SelectBooleanButton button) throws IOException { ResponseWriter writer = context.getResponseWriter(); String clientId = button.getClientId(context); boolean checked = Boolean.valueOf(ComponentUtils.getValueToRender(context, button)); boolean disabled = button.isDisabled(); String inputId = clientId + "_input"; String label = checked ? button.getOnLabel() : button.getOffLabel(); String icon = checked ? button.getOnIcon() : button.getOffIcon(); //button writer.startElement("div", null); writer.writeAttribute("id", clientId, "id"); writer.writeAttribute("type", "button", null); writer.writeAttribute("class", button.resolveStyleClass(checked, disabled), null); if(disabled) writer.writeAttribute("disabled", "disabled", null); if(button.getTitle()!= null) writer.writeAttribute("title", button.getTitle(), null); if(button.getStyle() != null) writer.writeAttribute("style", button.getStyle(), "style"); //input writer.startElement("div", null); // <-- Added. writer.writeAttribute("class", "ui-helper-hidden-accessible", null); // <-- Added. writer.startElement("input", null); writer.writeAttribute("id", inputId, "id"); writer.writeAttribute("name", inputId, null); writer.writeAttribute("type", "checkbox", null); // writer.writeAttribute("class", "ui-helper-hidden", null); <-- Removed. if(checked) writer.writeAttribute("checked", "checked", null); if(disabled) writer.writeAttribute("disabled", "disabled", null); if(button.getOnchange() != null) writer.writeAttribute("onchange", button.getOnchange(), null); writer.endElement("input"); writer.endElement("div"); // <-- Added. //icon if(icon != null) { writer.startElement("span", null); writer.writeAttribute("class", HTML.BUTTON_LEFT_ICON_CLASS + " " + icon, null); writer.endElement("span"); } //label writer.startElement("span", null); writer.writeAttribute("class", HTML.BUTTON_TEXT_CLASS, null); writer.writeText(label, "value"); writer.endElement("span"); writer.endElement("div"); } } 完全一样:

//input

(查看<--部分,我添加了faces-config.xml个评论来解释我添加/删除了哪些行已被复制到原始源代码中

要使其运行,请在<render-kit> <renderer> <component-family>org.primefaces.component</component-family> <renderer-type>org.primefaces.component.SelectBooleanButtonRenderer</renderer-type> <renderer-class>com.example.MySelectBooleanButtonRenderer</renderer-class> </renderer> </render-kit>

中按如下方式注册
component-family

(从renderer-type组件中提取SelectBooleanButton<p:selectBooleanButton>

这对我有用,好吧,有点儿。 <div class="ui-button">获得焦点,您可以使用空格键切换布尔状态。但是,焦点在任何方面都不会在视觉上可见。这需要在JavaScript方面解决。当隐藏的复选框获得焦点时,代表该按钮的.ui-state-focus应该获得$(".ui-button input[type=checkbox]").focus(function() { $(this).closest(".ui-button").addClass("ui-state-focus"); }).blur(function() { $(this).closest(".ui-button").removeClass("ui-state-focus"); }); 类。以下jQuery实现了:

init()

encodeMarkup()

在真正的PrimeFaces源代码中,这应该在PrimeFaces.widget.SelectBooleanButton文件的forms.js函数的{{1}}函数中解决。

答案 1 :(得分:0)

应用BalusC提出的完全相同的解决方法的另一种方法如下。

优点:

  • 您只需要创建并声明渲染器类:无需为facelet添加更多JavaScript代码,以便添加焦点状态的可视线索
  • 渲染器扩展不会替换整个超级encodeMarkup实现,而是装饰它;如果PF升级略微改变了超级实现,除非它们完全改变了DOM结构和/或计算inputId的方式,否则这应该更加健壮

我们的想法是添加一个操作原始标记的JavaScript脚本,以便使用BalusC提出的相同技术(基于应用适当的CSS类)为组件提供可聚焦性。

package com.example;

import java.io.IOException;

import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;

import org.primefaces.component.selectbooleanbutton.SelectBooleanButton;
import org.primefaces.component.selectbooleanbutton.SelectBooleanButtonRenderer;

public class MySelectBooleanButtonRenderer extends SelectBooleanButtonRenderer {

    @Override
    protected void
            encodeMarkup(FacesContext context, SelectBooleanButton button)
                    throws IOException {
        super.encodeMarkup(context, button);
        ResponseWriter writer = context.getResponseWriter();
        writer.startElement("script", null);
        writer.writeAttribute("type", "text/javascript", null);
        writer.append(getMakeButtonFocusableScript(button.getClientId(context)));
        writer.endElement("script");
    }

    protected String getMakeButtonFocusableScript(final String clientId) {
        String inputId = clientId + "_input";
        return "{\r\n"
                + "  var input = document.getElementById('"
                + inputId
                + "');\r\n"
                + "  input.classList.remove('ui-helper-hidden');\r\n"
                + "  var mainDiv = document.getElementById('"
                + clientId
                + "');\r\n"
                + "  var newDiv = document.createElement('div');\r\n"
                + "  newDiv.setAttribute('class', 'ui-helper-hidden-accessible');\r\n"
                + "  newDiv.appendChild(input);\r\n"
                + "  mainDiv.appendChild(newDiv);\r\n"
                + "  input.onfocus = function() {document.getElementById('"
                + clientId + "').classList.add('ui-state-focus'); };\r\n"
                + "  input.onblur = function() { document.getElementById('"
                + clientId + "').classList.remove('ui-state-focus'); };\r\n"
                + "}";
    }
}
相关问题