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>

按 T​​ab 键inputId2直接进入inputId3。这是预期的行为吗?有什么解决方法吗?

4

2 回答 2

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>

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

如果我们查看对应的复选框<p:selectBooleanCheckbox>,它也将复选框替换为一个视觉上更吸引人的实际上是可聚焦的小部件然后我们会看到该复选框并没有被 CSS 完全隐藏,而是通过被包裹在<div>绝对不可见的由 CSSposition: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>聚焦的“预期”或“直觉”行为,如果我是你,我肯定会将这个 UX 问题报告给 PrimeFaces。


同时,解决此问题的最佳方法是创建一个自定义渲染器,该渲染器将覆盖encodeMarkup()PrimeFaces 的方法,SelectBooleanButtonRenderer如下所示,以便class="ui-helper-hidden"从复选框中删除 并将其包装在 a 中<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-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");
});

现在它完全适合我

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

于 2012-09-24T13:30:43.087 回答
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"
                + "}";
    }
}
于 2014-12-18T14:47:36.407 回答