Discussion
Pegasystems Inc.
US
Last activity: 31 Dec 2020 17:32 EST
How to implement floating labels in your application
The Material Design System is one of the most popular design system and comes with several implementations compatible with different Javascript UI framework libraries. One of the most recognized UI pattern used by this design system is what is called the 'floating label' pattern.
This pattern was first introduced on mobile to save space by making the label inline with the input field but it also becoming very popular with desktop applications. While there is a lot of debate about the benefits of this pattern, the purpose of this document is to explain how to provide this functionality using the Pega UI.
For more details:
- http://bradfrost.com/blog/post/float-label-pattern/
- https://medium.com/simple-human/floating-labels-are-a-bad-idea-82edb64220f6
- https://material.io/components/text-fields/
There are 2 approaches for implementing this pattern:
- pure CSS solution
- Javascript approach using event listeners
The pure CSS solution is of course the most compelling but relies on the placeholder being visible to position the inline label. This approach requires some specific DOM markup by having the label field be the next sibling DOM element after the input field. (CSS3 selectors only allow to select a sibling element that is located after the selected element). The dynamic layouts generated by the Pega UI always set the label before the input field and as such we need to fallback to the javascript approach.
For more details on how to implement a floating label:
-
https://webdesign.tutsplus.com/articles/implementing-the-float-label-form-pattern--webdesign-16407
-
http://mozmonkey.com/2013/12/good-ux-for-placeholders-as-field-labels/
Assuming that your application is based on UI-Kit and the pyCaseWorker portal that is a using a single page dynamic dynamic container, the following changes would be required to implement the floating label pattern:
1/ Add event listeners on focus, blur and load that would set the appropriate class on the field-item
The javascript approach relies on toggling a class in the DIV that contains both the label and input and set the floating behavior. The class (that we will call 'focused') should be set if the input field is focused and the content of the input field is not empty. Otherwise the class should be removed from the DOM so that the label overlap with the input field.
To achieve this behavior we will need 3 event listeners:
- focus for any input or textarea
- blur for any input or textarea
- on load of the page to determine if the input field has some value or not.
The most efficient approach is to use an event listener set on the body the of document that will listen to the focus or blur events on any input or textarea using event bubbling. This approach is much more efficient than registering a new event listener for every input/textarea displayed on the field.
To execute some code when the page is loaded, we will rely on the 'AfterDCUpdate' event sent by the dynamic container once the page is rendered; using this event is a better approach than using an onload or onready event.
We also register a function handler that will be called on every Ajax interaction - the DOM element reloaded will be passed as parameter and used to reapply the same logic as the page load functionality but only for the input and textarea element of this region.
Note: For the simplicity of this article we will write the code using Jquery.
/* Create a listener at the body that will listen to any input or textarea focus and blur using event bubbling */
/* On focus we always remove the class */
$('body').on("focus", "input,textarea", function() {
$(this).parents('.content-item.content-field').removeClass("focused");
});
/* On blur, we only add back the class if empty */
$('body').on("blur", "input,textarea", function(e) {
if ($(this).val() === "") {
$(this).parents('.content-item.content-field').addClass("focused");
}
});
/* event called by the Dynamic container after every page load to determine if the input has a value or not */
pega.ui.EventsEmitter.subscribe("AfterDCUpdate", function() {
$("input,textarea").each(function() {
if ($(this).val() === "" && !$(this).is(':focus')) {
$(this).parents('.content-item.content-field').addClass("focused");
}
});
}, null, null, null);
/* Handle refresh section and reapply state for the reloaded section */
pega.u.d.attachOnload(function(reloadElement) {
$(reloadElement).find("input,textarea").each(function() {
if ($(this).val() === "" && !$(this).is(':focus')) {
$(this).parents('.content-item.content-field').addClass("focused");
}
});
}, true);
Store this JS code into a rule file text called form-handler.js and attach this file to your main harness portal. For this demo, we are using the Case Worker portal from UI-kit and we are resaving the pyCaseWorker portal harness to add this JS module.
2/ Add some CSS override to your application skin
The floating label is implemented by making the div that holds both the label and input as positioned relative and then positioning the label inside this div as absolute. A transition with animation is used to animate the label depending if the class 'focused' is present in the DOM or not.
The CSS override would be the following:
.content-item.content-field {
position: relative;
}
.content-item.content-field label {
position: absolute;
top: -13px;
left: 1px;
transition: transform 200ms ease;
pointer-events: none;
}
.focused label {
font-size: 0.7em;
transform: translateY(26px);
}
3/ Update your application skin to tweak the default styling of the input field
Override the mixin called 'field' and remove the top, left and right border so that only the bottom border is present. Remove also the padding and border radius set as additional styles inside the background element.
Override the 'Standard' input component to tweak the active/hover and focus styles to be aligned with the material design.
Increase the bottom spacing for the stacked layout from 7px to 21px
After the changes, every input field will render the form as a floating label. The CSS and JS could be improved to only implement this behavior for the 'Standard' format input field and not the other formats by looking at the format class set on the input field.
Here is a demo of a screenflow case type with the floating label.
You can import the attached zip file into your Pega application and add the ruleset floatinglabel:01-01 to your application. Use the AppSkin and pyCaseWorker harness included in this ruleset