Tuesday, July 10, 2012

Generalizing visibility control of an HTML span depending on form field state

Often a form user's answer to a question determines whether parts of a form are relevant, e.g., is the billing address the same as the shipping address? Earlier I wrote about a span attribute which leads to the automatic presentation of a checkbox to control a span's visibility. But in other circumstances it can be nice to have the ability to control a span's visibility depending on some arbitrary form field elsewhere in the page. As usual I am hoping to be able to implement this linkage in markup without a rat's nest of onchange JavaScript peeking around to see what the form looks like.
Using jquery and a small amount of code it is easy to do this. In my implementation described below, I support an attribute visibility_tied_to_field on spans; the attribute's value is the DOM ID of a check box form field whose state will determine the span's visibility. So, for example, the billing address versus shipping address situation could be handled as so:
Billing address different from shipping address? <input type=checkbox id='xyz'>
<span visibility_tied_to_field='xyz'>

... billing address form fields...

</span>

I also support an inverse linking between checkboxes and spans' visibility with an analogous span attribute visibility_inversely_tied_to_field. When this latter attribute is used, the span is visible if the checkbox is not checked, and invisible if the checkbox is checked (the logical inverse of visibility_tied_to_field):
Billing address same as shipping address? <input type=checkbox id='xyz'>
<span visibility_inversely_tied_to_field='xyz'>

... billing address form fields...

</span>

I implement this functionality using just the following code:

function visibility_ties_init()
{
    var init_onchange_show_if_checked = function(index, span)
    {
        init_onchange(true, span, "visibility_tied_to_field")
    }
    
    var init_onchange_show_if_NOT_checked = function(index, span)
    {
        init_onchange(false, span, "visibility_inversely_tied_to_field")
    }
    
    var init_onchange = function(show_if_checked, span, span_attr_that_points_to_field)
    {
        var checkbox_id_attr = span.attributes.getNamedItem(span_attr_that_points_to_field)
        if (checkbox_id_attr != null)
        {
            var checkbox_id = checkbox_id_attr.value
            var checkbox = $('#' + checkbox_id)
            var show_it = (show_if_checked && checkbox.is(':checked')) || (!show_if_checked && !checkbox.is(':checked'))
            span.hidden = !show_it

            checkbox.change(function()
            {
                span.hidden = (show_if_checked ? !this.checked : this.checked)
            })
        }
    }
    
    $("span[visibility_tied_to_field]").each(          init_onchange_show_if_checked)
    $("span[visibility_inversely_tied_to_field]").each(init_onchange_show_if_NOT_checked)
}

The code iterates across spans with the visibility_tied_to_field or visibility_inversely_tied_to_field attributes, looking up the checkboxes which are pointed to. For each one the code hangs a new onchange handler to take care of adjusting the corresponding span's visibility depending on the checkbox state.

No comments:

Post a Comment