我们开始使用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
。这是预期的行为吗?有没有解决方法?
答案 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()
在真正的PrimeFaces源代码中,这应该在PrimeFaces.widget.SelectBooleanButton
文件的forms.js
函数的{{1}}函数中解决。
答案 1 :(得分:0)
应用BalusC提出的完全相同的解决方法的另一种方法如下。
优点:
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"
+ "}";
}
}