Overview

When developing mobile applications or applications that support a responsive design with Oracle ADF, sometimes you will be tasked with styling component to look like the native mobile components. Normally when using plain html this shouldn’t be a problem but when working with ADF you are confined to using faces components to make the design of a page.

Here I will show you how to make an af:selectBooleanCheckbox look like an IOS toggle button. First you need to know some things about the JSF components, mostly the html they generate when being rendered in browsers. For af:selectBooleanCheckbox the html is:

startingHTML

The JSF code looks like this:

<af:selectBooleanCheckbox id="sbc1" autoSubmit="true" styleClass="toggle-button toggle-button-js" clientComponent="true"/>

And the rendered component like this:

startingImage

 

Modifying the generated html

We have 2 style classes on the component, toggle-button toggle-button-js, because the first one will be referenced from the skin and the other from a javascript function. The way ADF behaves is that when content compression is turned on, it compresses the classes that are referenced from a skin file and leaves the ones that are not uncompressed.

The first thing we need to do is to modify the html that is rendered to add 2 <label> elements next to the generated <input> element. Unfortunately there is no JSF component that can do this so we will have to use javascript in order to get the construct we want:

function insertToggleLabel(selector) {
    $(selector).each(function () {
        var clientId = $(this).attr("id");
        var clientComp = document.getElementById(clientId);

        var hasToggleInserted = $(clientComp).attr("insert_toggle");
        if(!hasToggleInserted || hasToggleInserted == undefined) {
            $(this).after("<label for=\"" + $(this).attr("id") + "\" class=\"db-toggle-bg\"><\/label>");
            $(this).after("<label for=\"" + $(this).attr("id") + "\" class=\"db-toggle-label\"><\/label>");
            $(clientComp).attr("insert_toggle", "true");
        }
    });
}

function init() {
  insertToggleLabel('.toggle-button-js input');
};

Here is the resulting html:

jsHTML

 

Styling the component

We can use the label elements to create the button and the background outline. We do this using CSS:

.db-toggle-bg {
    width: 28px;
    height: 16px;
    display: inline-block;
    background: white;
    border-radius: 50px;
    border: solid 1px #d3d3d3;
    position: absolute;
    left: 0px;
    top: 0px;
    z-index: 1;
}

.db-toggle-label {
    width: 16px;
    height: 16px;
    background: white;
    border-radius: 50px;
    border: solid 1px #a9a9a9;
    position: absolute;
    left: 1px;
    top: 0px;
    z-index: 2;
    transition: all .2s ease;
    -webkit-transition: all .2s ease;
}

And here is how it looks like:

renderedButton

Now we can use CSS to position the button over the checkbox and hide it so that when the user clicks the button the checkbox is checked. We also add styling so that the background color and button position change when the button is toggled on:

af|selectBooleanCheckbox.toggle-button .AFContentCell {
    vertical-align: middle;
}

af|selectBooleanCheckbox.toggle-button::content {
    width: 30px;
    height: 18px;
    display: inline-block;
    position: relative;
    top: 2px;
}

af|selectBooleanCheckbox.toggle-button::native-input:checked {
    left: 12px;
}

af|selectBooleanCheckbox.toggle-button::native-input:checked + label {
    left: 12px;
    border-color: #0592D0;
}

af|selectBooleanCheckbox.toggle-button::native-input:checked + label + label {
    background: #0592D0;
    border-color: #0592D0;
}

af|selectBooleanCheckbox.toggle-button::native-input {
    visibility: hidden;
    width: 16px;
    height: 16px;
    z-index: 3;
    position: absolute;
    left: 0px;
    top: 0px;
    margin: 0px;
}

Here is how the toggle button looks like:

buttonFinished

We can also wrap it in an af: panelLabelAndMessage component in order to have a label before it:

buttonFinal