我们开始使用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中的tab直接转到inputId3。这是预期的行为吗?有什么解决方法吗?

最佳答案

这是因为PrimeFaces <p:selectBooleanButton>实际上呈现了表示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>


该复选框完全被display:none类中的CSS .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>


我不会认为<p:selectBooleanButton>是无法集中的“预期”或“直觉”行为,如果我是你,我肯定会 PrimeFaces的UX问题。



同时,解决此问题的最佳选择是创建一个自定义渲染器,该渲染器将覆盖PrimeFaces encodeMarkup()report方法,如下所示,以便从复选框中删除SelectBooleanButtonRenderer并将其包装在class="ui-helper-hidden"中,完全与<div class="ui-helper-hidden-accessible>一样:

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");
    }

}


(请参见<p:selectBooleanCheckbox>部分,我添加了//input注释,以说明我已添加/删除了哪些行,并将其删除并复制到了原始源代码中)

要使其运行,请在<--中进行如下注册:

<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>


faces-config.xmlcomponent-family值是从renderer-type组件中提取的)

这对我来说很好。 SelectBooleanButton获取焦点,您可以使用空格键切换布尔状态。但是,焦点在任何情况下都不可见。这需要在JavaScript方面解决。当隐藏的复选框获得焦点时,表示按钮的<p:selectBooleanButton>应该获得一个<div class="ui-button">类。下面的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");
});


.ui-state-focus

在实际的PrimeFaces源代码中,应在init()文件的PrimeFaces.widget.SelectBooleanButton函数的forms.js函数中解决此问题。

08-15 22:17