JavaScript Customizations for Collections
Collections group together related sets of fields on a form, making it easy to repeat those sets. Addresses are a very common kind of collection, as they normally consist of several fields that always go together. In this section, we explain how you can determine the selectors you need for customizations on collections, and provide examples of some common customizations.
Selectors
The region taken up by the entire collection is a li element with a name and id in the format qN
(q67
in the example below). The class of this element is cf-collection-block
, which distinguishes the collection from other field types. The following screenshot shows the area occupied by a collection with two fields that are repeated. The li element representing the element is highlighted in blue in the right pane.
Within the li element that comprises the entire collection, we have the following elements:
- The collection title: A div element with the class
cf-section-header
. - Another div element with the classes
cf-collection
andkx-repeatable
. This is the element that contains the sets of fields.
We will focus on how to select elements within the latter element. Within this element, we have div elements of class form-q
. Each of these represents a set of fields—in our example, a pair of fields "Apples and Oranges" and "Just Apples". Our example has two sets in the collection, so there are two div elements with class form-q
.
Now we expand these class form-q
elements to examine their inner structure. Both fields are contained in an unordered list element (ul) with the class rpx
. Each field is represented by a li element with one part corresponding to the field label container and another to the input box container. The structure of each field is the same as that of conventional fields. Each field is wrapped in a field container with an ID of the format qN
. In a collection with repeating sets of fields, this ID will not be unique—for example, all "Just Apples" field containers in our collection will have the same id, q72
.
For fields in collections, input boxes also have different ID assignment rules compared to conventional fields. To aid users in determining the patterns of these IDs, we have labeled each input box in our sample collection with its corresponding id:
The format for fields in collections with repeating sets is FieldN(setNumber)
. With each additional set, the FieldN
prefixes stay the same, but the setNumber
s increment by one.
Reusing Inputs for Repeating Sets
When you have repeating sets of fields in a collection, you may want certain fields in later sets to be automatically filled with the values of their counterparts in previous sets. The following animation shows the desired behavior, as applied to the "Just Apples" field in the collection:
The following JavaScript snippet implements this customization.
$(document).ready(function() {
$('a[ref-id="q20"]').click(function() { //after "Add" button is clicked
var setCount = $(".cf-collection-block .rpx").length; //count number of repeating sets
var newfieldid = "Field72(" + setCount + ")"; //id of "Just Apples" field that was just added
var oldfieldid = "Field72(" + (setCount-1) + ")"; //id of previous "just Apples" field
//set value of new field to value of previous field
$('input[id="' + newfieldid + '"]').val($('input[id="' + oldfieldid + '"]').val());
});
});
When the document is loaded, the browser waits for a click on the "Add" button, which has the attribute ref-id equal to q20.
After the click occurs, the following operations take place:
- The number of sets in the collection is counted. If you had started with one collection and clicked "Add", this number will now be 2. The count works by looking in the collection block, which is identified by its class
cf-collection-block
, and counting the number of elements with classrpx
in this block. This works because each set is contained within a ul element with classrpx
, and there are no other elements in the collection block that have that class. If you have multiple collections on the same page, you should alter this code to use a hierarchical selector with the collection's id rather than its class. Otherwise,setCount
will be the total number of sets on the page, counted across all collections on the page. - The variable
newfieldid
is constructed to represent the id of the newly added field that we want to copy a value over to. In this example, we want to copy the old value of "Just Apples" over to the newly added instance of "Just Apples". The format of all "Just Apples" fields in this collection isField72(setNumber)
. The value ofsetNumber
for the newly added field will just be the number of sets, i.e.setCount
.
- The variable
oldfieldid
is constructed to represent the id of the instance of "Just Apples" preceding the most recently added instance. This will have the formatField72(setCount-1)
. - Using
newfieldid
andoldfieldid
, we set the latest instance of "Just Apples" to have the same value as its preceding instance.
Reusing Inputs Conditional on a Checkbox
Sometimes, you may want the automatic filling in the previous example to occur only when a box is checked. This is common, for instance, in online transactions where one has to specify a billing address and a shipping address. To save users time, you can have them check a box to indicate that the second address is the same as the first address. The collection corresponding to the second address will then be automatically filled with the values from the first collection.
The desired behavior is something like the following:
The JavaScript we used to implement this is as follows. It assumes that the Billing Address collection has been assigned the custom class billing
, the Shipping Address has been assigned the custom class shipping
, and the checkbox has been assigned the class sameAddress
.
$(document).ready(function () {
$('.sameAddress').change(function() { //checks for changes in the checkbox
if ($(this).find('input[value="Yes"]').is(':checked')) {
//make list of all inputs in Shipping Address collection
var shippingAddress = $('.shipping').find('input');
//make list of all inputs in Billing Address collection
var billingAddress = $('.billing').find('input');
//make list of all dropdown choices in Shipping Address collection
var shippingDropdowns = $('.shipping').find('select');
//make list of all dropdown choices in Billing Address collection
var billingDropdowns = $('.billing').find('select');
for (i=0; i<3; i++) { //iterate over the max. number of fields for each field type
$(shippingAddress[i]).val($(billingAddress[i]).val()); //copy text inputs
$(shippingDropdowns[i]).val($(billingDropdowns[i]).val()); //copy dropdown choices
}
}
});
});
The code works as follows. When the document is loaded, the browser waits for changes made to the checkbox that indicates whether the shipping address is the same as the billing address. When a change occurs, the browser does the following if the checkbox is selected:
- Make a list of all inputs in the Shipping Address collection
- Make a list of all inputs in the Billing Address collection
- Do the same two actions, but for drop-down menus instead
- Copy the values from the Billing Address collection over to the Shipping address collection.
Targeting the Same Fields in Every Set
In some situations, two fields in a set may be systematically related, such that every set added to that collection should obey that relationship. To enforce this, you may want to carry out the same JavaScript operation in every set within a repeating collection. In the following example, we have repeating sets where we want "Not Applicable" to be filled in whenever "No" is selected in the previous field.
Our desired behavior is shown in the following animation:
We implement this using the following JavaScript snippet. The code assumes that you have assigned the custom class reportInfo
to the collection and that the id of the "Reviewer" field is q58
.
$(document).ready(function () {
$('.reportInfo').on('change', '[type="radio"]', reqChange);
function reqChange() {
$('.reportInfo .rpx .radio-checkbox-fieldset').each(function () {
$(this).find('input:checked').each(function() {
if ($(this).val() == "No") {
var reviewerField = $(this).closest('.rpx').find('[name="q58"]');
$(reviewerField).find('input').val('Not Applicable');
}
})
})
}
})
When the document has loaded, the browser waits for a change in the radio buttons in the collection. When a change happens, the function reqChange
is called. This function loops through every set of Yes/No radio buttons ($('.reportInfo .rpx .radio-checkbox.fieldset')
selects for these sets) and does the following:
- It finds all radio buttons that are selected, using
.find('input:checked')
. - Within these buttons, it searches for those that have the value "No".
- For the selected buttons with the value "No", it searches its ancestors for the closest element with class
rpx
. This element is the container for a set of fields. Within the container, it then searches for the Reviewer field, which is identified in our example by having aname
attribute ofq58
. - Within the Reviewer field, the function looks for the input element and sets its value to "Not Applicable."