Toggle control visibility with jquery
This article explains a simple way to toggle controls in a asp.net application using jquery. This article will show an example on how to set up the rules so that in future if rules change, it is easy to make the necessary changes to the rule.
Why is this technique useful
jquery has made it easier to toggle control visibility based on the value selected on another control. This is very convenient and provides a robust way to apply business logic to asp.net applications. However, when it comes to applications with large number of controls, the task of applying the business logic and maintainability becomes cumbersome.
This is where the technique shown in this article comes to the rescue. This technique can be applied to existing applications or new applications waiting to be born. How to apply this technique
To apply the technique, only necessity is to add a javascript file to the application and copy the code from the jquerybehavior javascript file attached to this article. The only change you should have to make in this file will be to set up the ancestary variable. Every attempt has been made to easily understand the set-up of this variable.
var idSelFormat = '#{0}';
var radioSelFormat = 'input[name*=\'{0}\']';
var displayModes = {
Show: 0,
Hide : 1
}
$(function () {
///
/// Set up control ancestary.
///
///
/// ancestary variable setup.
///
/// var ancestary = {
/// parents: [
/// {
/// id: [Ist Parent Id],
/// type: [Ist Parent Control Type: radio, select, checkbox etc.],
/// children:
/// [
/// { id: [1st Child Control Id], value: [Comma delimited values of the Ist parent the Ist child depends on], mode: [Display mode of Ist child when the specified value is selected on Ist parent: Show, hide]},
/// { id: [nth Child Control Id], value: [Comma delimited values of the Ist parent the nth child depends on], mode: [Display mode of nth child when the specified value is selected on Ist parent: Show, hide]}
/// ]
/// },
/// {
/// id: [nth Parent Id],
/// type: [nth Parent Control Type: radio, select, checkbox etc.],
/// children:
/// [
/// { id: [1st Child Control Id], value: [Comma delimited values of the nth parent the Ist child depends on], mode: [Display mode of Ist child when the specified value is selected on nth parent: Show, hide]},
/// { id: [nth Child Control Id], value: [Comma delimited values of the nth parent the nth child depends on], mode: [Display mode of nth child when the specified value is selected on nth parent: Show, hide]}
/// ]
/// }
/// PS: This logic only supports the direct hierarchy between parent and children and does not support the condition when a control depends on more than one parent control.
///
// NOTE: This should be the only code you have to change when business rules change in your application.
var ancestary = {
parents: [
{
id: 'rblBegin',
type: 'radio',
children:
[
{ id: 'pnlQuestionaire', value: '1', mode: displayModes.Show }, //Show Questionaire panel when "Yes" selected for rblBegin RadioButtonList
{ id: 'pnlThankYou', value: '1', mode: displayModes.Show } //Show Thank You panel when "Yes" selected for rblBegin RadioButtonList
]
},
{
id: 'rblName',
type: 'radio',
children:
[
{ id: 'pnlName', value: '1', mode: displayModes.Show } //Show Name panel when "Yes" selected on rblName RadioButtonList
]
},
{
id: 'ddlSource',
type: 'select',
children:
[
{ id: 'pnlFriend', value: '1', mode: displayModes.Show } //Show Friend panel when "Friend" selected on ddlSource DropdownList
]
},
{
id: 'rblFriendName',
type: 'radio',
children:
[
{ id: 'pnlFriendName', value: '1', mode: displayModes.Show } //Show Friend Name Panel when "Yes" selected on rblFriendName RadioButtonList
]
},
{
id: 'cbUpdates',
type: 'checkbox',
children:
[
{ id: 'lblUpdateConfirmation', value: 'checked', mode: displayModes.Show }, //Show Update Confirmation label when cbUpdates CheckBox is checked.
{ id: 'lblNoUpdateConfirmation', value: 'checked', mode: displayModes.Hide } //Hide No Update Confirmation label when cbUpdates CheckBox is checked.
]
}
]
};
// NOTE: The following sections should not have to change, unless, there is a need to add additional functionality.
//Iterate through each parent and toggle its children based on the value selected on the parent
//Also attach an event to the parent to toggle its children when parent is changed.
for (var i = 0; i < ancestary.parents.length; i++) {
var parentId = ancestary.parents[i].id;
var id = idSelFormat.f(parentId);
switch (ancestary.parents[i].type) {
case 'select':
var element = $(id);
Toggle(ancestary.parents[i].children, $(element).val());
$(id).change(function () {
for (var j = 0; j < ancestary.parents.length; j++) {
if (ancestary.parents[j].id == $(this).attr('id')) {
Toggle(ancestary.parents[j].children, $(this).val());
break;
}
}
});
break;
case 'radio':
var id = radioSelFormat.f(ancestary.parents[i].id);
Toggle(ancestary.parents[i].children, GetRBLValue(parentId));
$(id).click(function () {
for (var j = 0; j < ancestary.parents.length; j++) {
if ($(this).attr('id').startsWith(ancestary.parents[j].id)) {
Toggle(ancestary.parents[j].children, $(this).val());
}
}
});
break;
case 'checkbox':
var element = $(id);
Toggle(ancestary.parents[i].children, $(element).attr('checked'));
$(id).click(function () {
for (var j = 0; j < ancestary.parents.length; j++) {
if (ancestary.parents[j].id == $(this).attr('id')) {
Toggle(ancestary.parents[j].children, $(this).attr('checked'));
break;
}
}
});
break;
}
}
})
function Toggle(dependencies, value) {
///
/// This toggles controls based on the value selected on the parent control
///
if (dependencies && dependencies.length > 0) {
var times = '';
for (var i = 0; i < dependencies.length; i++) {
var id = idSelFormat.f(dependencies[i].id);
var exists = $.inArray(value, dependencies[i].value.split(',')) > -1;
if (exists) {
//If the value selected of the parent control exists in the child's dependent values and if mode is show, then Show control, else Hide control
if (dependencies[i].mode == displayModes.Show) {
$(id).show();
} else {
$(id).hide();
}
} else {
//If the value selected of the parent control DOES NOT exist in the child's dependent values and if mode is show, then Hide control, else Show control
if (dependencies[i].mode == displayModes.Show) {
$(id).hide();
} else {
$(id).show();
}
}
}
}
}
function GetRBLValue(id) {
///
/// Gets the selected value of a radiobutton list. The Id passed in is the Id set on
/// Since radiobutton list are rendered as table or with span tags, the way to get their value is to find all radio input controls inside the container and find the one selected.
///
var v = '';
var rbl = $get(id);
if (rbl) {
var nodes = rbl.getElementsByTagName('INPUT');
for (var i = 0; i < nodes.length; i++) {
if ((nodes[i]) && (nodes[i].type == 'radio') && (nodes[i].checked)) {
v = nodes[i].value;
break; //break all the loops
}
}
}
return v;
}
function clearControl(control) {
///
/// Clear the control value and raise appropriate event so that any controls that depend on this can toggle their visibility and clear their value.
///
switch (control.tagName.toLowerCase()) {
case 'input':
switch (control.type.toLowerCase()) {
case 'checkbox':
case 'radio':
//Uncheck the control, and raise the click event.
//These controls have click event handlers which do not run automatically
if (control.checked) {
control.checked = false;
tryFireEvent(control, "click");
}
break;
case 'text':
case 'password':
if (control.value.trim() !== '') { control.value = ''; }
break;
default:
break;
}
break;
case 'textarea':
if (control.value.trim() !== '') { control.value = ''; }
break;
case 'select':
//Select the first item from the list, if not the item selected
if (control.selectedIndex !== 0) {
control.selectedIndex = 0;
tryFireEvent(control, "change");
}
break;
default:
break;
}
}
function ClearHiddenControls(container) {
///
/// Clear Textboxes, Dropdowns, Multiline Textboxes, Checkboxes.
///
var inputNodes = container.getElementsByTagName('INPUT');
var selectNodes = container.getElementsByTagName('SELECT');
var textareaNodes = container.getElementsByTagName('TEXTAREA');
var i = 0;
for (i = 0; i < inputNodes.length; i++) { this.clearControl(inputNodes[i]); }
for (i = 0; i < selectNodes.length; i++) { this.clearControl(selectNodes[i]); }
for (i = 0; i < textareaNodes.length; i++) { this.clearControl(textareaNodes[i]); }
}
String.prototype.format = String.prototype.f = function () {
///
/// javascript substitution for .net String.format("{0}",value);
///
var s = this,
i = arguments.length;
while (i--) {
s = s.replace(new RegExp('\\{' + i + '\\}', 'gm'), arguments[i]);
}
return s;
};
String.prototype.endsWith = function (suffix) {
///
/// javascript substitution for .net string.EndsWith("[string]")
///
return (this.substr(this.length - suffix.length) === suffix);
}
String.prototype.startsWith = function (prefix) {
///
/// javascript substitution for .net string.StartsWith("[string]")
///
return (this.substr(0, prefix.length) === prefix);
}Sample
The attachment is a sample asp.net 4.5 application (this technique is not dependent on any asp.net framework version) that shows a simple set up of this technique. This can be used as a building block for any asp.net application.