如何使用tab键来关注ap:selectBooleanButton组件?

Eug*_*vas 2 java primefaces jsf-2

我们开始使用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>
Run Code Online (Sandbox Code Playgroud)

按下选项卡inputId2直接进入inputId3.这是预期的行为吗?有没有解决方法?

Bal*_*usC 7

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

<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>
Run Code Online (Sandbox Code Playgroud)

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

如果我们看一下对应的复选框<p:selectBooleanCheckbox>,这也是由视觉上更吸引人的部件取代了复选框实际上可成为焦点,那么我们看到的复选框没有完全被隐藏CSS,只是不可见的被包裹在一个<div>这是绝对由CSS定位position:absolute.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>
Run Code Online (Sandbox Code Playgroud)

我不会认为这<p:selectBooleanButton>是不可聚焦的"预期"或"直觉"行为,如果我是你,我肯定会向PrimeFaces 报告这个用户体验问题.


同时,你最好的解决方法是创建一个自定义渲染器,它覆盖encodeMarkup()PrimeFaces 的方法,SelectBooleanButtonRenderer如下所示,以便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");
    }

}
Run Code Online (Sandbox Code Playgroud)

(看看这个//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>
Run Code Online (Sandbox Code Playgroud)

(component-familyrenderer-type值从SelectBooleanButton组件中提取)

这对我有用,嗯,有点.在<p:selectBooleanButton>获得焦点,您可以使用空格键切换布尔状态.但是,焦点在任何方面都不会在视觉上可见.这需要在JavaScript方面解决.该<div class="ui-button">代表按钮应该得到一个.ui-state-focus类时隐藏复选框获得焦点.以下jQuery实现了:

$(".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");
});
Run Code Online (Sandbox Code Playgroud)

现在它完全适合我.

在真正的PrimeFaces源代码中,这应该根据文件init()PrimeFaces.widget.SelectBooleanButton功能来解决forms.js.