Cascading Select Lists in WordPress (sometimes called chained selects)
Let me preface this by saying I'm pretty new to WordPress. I've not done anything with jQuery. I've used php for years, but only off and on. My hobby web site, www.OrcSports.com, was written using php.I have been working on a project for a while now that makes use of custom post types. For these custom post types I wanted to include specific fields. Enter the Advanced Custom Fields plugin. I'm using version 3.1.7. Using this robust plugin, I was able to define the list of fields I wanted to display including select lists. Unfortunately there wasn't an easy way to have the options presented in a select list dependent on a previous select list. So I started working on that.
I found an example of cascading select boxes and tried to incorporate the script there into the output from Advanced Custom Fields. After losing a weekend and then some, in part because of the conflict between WordPress and using $() for jQuery that causes your functions to not work, here's what I came up with (file edits below refer to changes in Advanced Custom Fields files).
In js/input-actions.js:
After validation for select (line 96) add:
// parent_select
if ($(this).find('parent_select').exists())
{
if ($(this).find('parent_select').val() == "null" || !$(this).find('parent_select').val())
{
validation = false;
}
}
// child_select
if ($(this).find('acf_child_select').exists())
{
if ($(this).find('acf_child_select').val() == "null" || !$(this).find('acf_child_select').val())
{
validation = false;
}
}
In acf.php:
After add_meta_box in admin_head() (line 432) add:
if ($show == 'true')
{
$this->script_cascading_selects($acf['fields']);
}
After adding the select field (line 125) add:
include_once('core/fields/child_select.php');
include_once('core/fields/parent_select.php');
After instantiating the select field (line 140) add:
$return['child_select'] = new acf_Child_select($this);
$return['parent_select'] = new acf_Parent_select($this);
Add the following method:
/*--------------------------------------------------------------------------------------
*
* script_cascading_selects
*
* @author Brett Paul
* @since 3.1.7
*
*-------------------------------------------------------------------------------------*/
function script_cascading_selects($fields)
{
$cascade = array();
$field_keys = array();
if($fields)
{
foreach($fields as $field)
{
if ($field['type'] == 'parent_select')
{
$field_keys[$field['name']] = $field['key'];
}
if ($field['type'] == 'child_select')
{
$field_keys[$field['name']] = $field['key'];
if (isset($field['parent_field_name']))
{
$cascade[$field['name']] = $field['parent_field_name'];
if (!isset($cascade[$field['parent_field_name']]))
{
$cascade[$field['parent_field_name']] = "*";
}
}
}
}
if (count($cascade) > 0)
{
echo "<script type=\"text/javascript\" defer=\"defer\">
function cascadeSelect(parent, child)
{
var childOptions = child.find('option:not(.static)');
child.data('options',childOptions);
parent.change(function(){
childOptions.remove();
child.append(child.data('options').filter('.sub_' + this.value)).change();
})
childOptions.not('.static, .sub_' + parent.val()).remove();
}\n";
$cascade_function_header = "jQuery(function(){
cascadeForm = jQuery('form.post');
";
$cascade_function_body = '';
// Need to include javascript to handle cascading
foreach ($cascade as $key => $parent_name)
{
$cascade_function_header .= $key . "Select = cascadeForm.find('." . $field_keys[$key] . "Class');\n";
if ($parent_name != '*')
{
$cascade_function_body .= "cascadeSelect(" . $parent_name . "Select, " . $key . "Select);\n";
}
}
echo $cascade_function_header . $cascade_function_body . '});</script>';
}
}
}
Finally, add these two files (child_select.php and parent_select.php) to the core/fields folder. These started out as copies of select.php and were modified for specifics around parent and child selects.
Now that the file changes have been made, go into your Advanced Custom Fields interface in WordPress and set up your fields. The big distinctions here are:
For parent_select, your key values should only contain alphanumeric characters.
For child_select, you will include three columns. The first is the parent key associated with the select value and must match exactly to what you entered for the parent. A "*" here will show that selection for all parent values.
When you open up a post with a set of Advanced Custom Fields attached to it, you will see your select lists. Child select lists are not populated at the start. You have to select something from the parent before you will see options in the child select.
It seems to work pretty well, but I really just got done making changes for it. It's got to be tested!
Labels: WordPress

