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

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:

  1. /*
  2.  * Implementation of hook_form_alter()
  3.  */
  4. function my_custom_module_form_alter(&$form, &$form_state, $form_id){
  5.   // blog nodes
  6.   if ($form_id == "blog_node_form") {
  7.  
  8.   }
  9. }

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. 

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

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

  1. <script>
  2.     jQuery( document ).ready(function() {
  3.         // default is the second set of radio inputs are hidden because they're irrelevant
  4.         jQuery('#edit-field-conversations-category').hide();
  5.        
  6.         // but if the "conversations" input is checked, then the second set of radio inputs are now relevant so show them
  7.         // first, check if it's already checked/selected when the page is loaded
  8.         jQuery('#edit-field-blog-category .form-radio:checked').map(function(){
  9.             var value = this.value;
  10.                 if (value == 6166) { // 6166 is the Drupal TID for the "conversations" taxonomy term
  11.                     jQuery('#edit-field-conversations-category').show();
  12.                 }
  13.         });
  14.        
  15.         // now, any time the blog category changes, see if it's the "conversations" one that is selected and act accordingly
  16.         jQuery('#edit-field-blog-category .form-radio').change(function(){
  17.             var value = this.value;
  18.             if (value == 6166) {
  19.                 jQuery('#edit-field-conversations-category').show();
  20.             } else {
  21.                 jQuery('#edit-field-conversations-category').hide();
  22.             }
  23.         });
  24.     });
  25. </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:

  1. /*
  2.  * Implementation of hook_form_alter()
  3.  */
  4. function my_custom_module_form_alter(&$form, &$form_state, $form_id){
  5.   // blog nodes
  6.   if ($form_id == "blog_node_form") {
  7.  
  8.     // for Conversations with Dan Skinner, make a conditional taxonomy field
  9.     $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()})});';
  10.  
  11.     /*
  12.      * un-minified JS for above:
  13.      *
  14.     jQuery( document ).ready(function() {
  15.         // default is the second set of radio inputs are hidden because they're irrelevant
  16.         jQuery('#edit-field-conversations-category').hide();
  17.        
  18.         // but if the "conversations" input is checked, then the second set of radio inputs are now relevent so show them
  19.         // first, check if it's already checked/selected when the page is loaded
  20.         jQuery('#edit-field-blog-category .form-radio:checked').map(function(){
  21.             var value = this.value;
  22.                 if (value == 6166) {
  23.                     jQuery('#edit-field-conversations-category').show();
  24.                 }
  25.         });
  26.        
  27.         // now, any time the blog category changes, see if it's the "conversations" one that is selected and act accordingly
  28.         jQuery('#edit-field-blog-category .form-radio').change(function(){
  29.             var value = this.value;
  30.             if (value == 6166) {
  31.                 jQuery('#edit-field-conversations-category').show();
  32.             } else {
  33.                 jQuery('#edit-field-conversations-category').hide();
  34.             }
  35.         });
  36.     });
  37.     */
  38.     $form['body'][LANGUAGE_NONE][0]['#description'] = "<script>" . $conditionalFieldsJS . "</script>";
  39.   }
  40. }