Make your own conditional fields in Drupal 7 by "hacking" the hook_form_alter() function with JavaScript injection

Submitted by Dan on Fri, 04/14/2017 - 16:44

When I needed a conditional field for a Drupal 7 content type, of course I just googled "drupal conditional fields" to see if a module exists. One did exist, but its D7 release came with a big, red flag warning any would-be downloaders that it's not supported any longer and is vulnerable to security issues. Instead of risking it on the entrerprise-level website, I decided to code up a solution myself. And I would use JavaScript inserted by the PHP code. Easy!

Disclaimer: You should never "hack" your Drupal site. Primarily, what I mean by that is that you should never change any of the PHP code in the Drupal core modules and contributed Drupal modules (with the exception of community-tested code patches). The correct way to alter any of the PHP logic of a core or contributed module is to create a custom module that uses the "hook" functions provided by Drupal, such as the hook_form_alter() function demostrated in this article. Therefor, I'm not actually hacking Drupal and this is the preferred method for achieving my goal. 

First, I needed a custom module. Actually, I already had one for this website, but it's a first step non-the-less for anyone following from home.

Second, I began inserting the basic boilerplate code:

 
/* 
 * Implementation of hook_form_alter() 
 */ 
function my_custom_module_form_alter(&$form, &$form_state, $form_id){ 
  // blog nodes
  if ($form_id == "blog_node_form") {
  } 
} 

Next, I began poking around and seeing what I could get away with. Sure enough, JavaScript inserted anywhere into the form would work just fine. 

 
/* 
 * Implementation of hook_form_alter() 
 */ 
function my_custom_module_form_alter(&$form, &$form_state, $form_id){ 
  // blog nodes if ($form_id == "blog_node_form") { 
    $form['body'][LANGUAGE_NONE][0]['#description'] .= "<script>alert('hello world?')</script>"; 
  } 
} 

Now it was just a matter of forming the JavaScript code, which is JQuery 101. Here's what I came up with:

 <script>
    jQuery( document ).ready(function() {
        // default is the second set of radio inputs are hidden because they're irrelevant
        jQuery('#edit-field-conversations-category').hide();
 
    	// but if the "conversations" input is checked, then the second set of radio inputs are now relevant so show them
    	// first, check if it's already checked/selected when the page is loaded
        jQuery('#edit-field-blog-category .form-radio:checked').map(function(){
    	    var value = this.value;
    		if (value == 6166) { // 6166 is the Drupal TID for the "conversations" taxonomy term
	    	    jQuery('#edit-field-conversations-category').show();
    		}
    	});
 
        // now, any time the blog category changes, see if it's the "conversations" one that is selected and act accordingly
        jQuery('#edit-field-blog-category .form-radio').change(function(){
            var value = this.value;
            if (value == 6166) {
                jQuery('#edit-field-conversations-category').show();
            } else {
                jQuery('#edit-field-conversations-category').hide();
            }
        });
    });
</script> 

 

However, to inject it into the Drupal PHP framework, I needed to minimize it to get it all on one line and use the 'jQuery' variable name rather than '$'. I then put the un-minifed JS code in a comment block in case I needed to edit it later on.

 

So, all together, the whole thing looks like this:

 

 
// blog nodes
  if ($form_id == "blog_node_form") {
 
    // for Conversations with Dan Skinner, make a conditional taxonomy field
    $conditionalFieldsJS = 'jQuery(document).ready(function(){jQuery("#edit-field-conversations-category").hide(),jQuery("#edit-field-blog-category .form-radio:checked").map(function(){6166==this.value&&jQuery("#edit-field-conversations-category").show()}),jQuery("#edit-field-blog-category .form-radio").change(function(){6166==this.value?jQuery("#edit-field-conversations-category").show():jQuery("#edit-field-conversations-category").hide()})});';
 
    /* 
     * un-minified JS for above:
     *
    jQuery( document ).ready(function() {
        // default is the second set of radio inputs are hidden because they're irrelevant
        jQuery('#edit-field-conversations-category').hide();
 
    	// but if the "conversations" input is checked, then the second set of radio inputs are now relevent so show them
    	// first, check if it's already checked/selected when the page is loaded
        jQuery('#edit-field-blog-category .form-radio:checked').map(function(){
    	    var value = this.value;
    		if (value == 6166) {
	    	    jQuery('#edit-field-conversations-category').show();
    		}
    	});
 
        // now, any time the blog category changes, see if it's the "conversations" one that is selected and act accordingly
        jQuery('#edit-field-blog-category .form-radio').change(function(){
            var value = this.value;
            if (value == 6166) {
                jQuery('#edit-field-conversations-category').show();
            } else {
                jQuery('#edit-field-conversations-category').hide();
            }
        });
    });
    */
    $form['body'][LANGUAGE_NONE][0]['#description'] = "<script>" . $conditionalFieldsJS . "</script>";
  }
}