Conditional Required Contact Form 7 Fields

Sometimes you may want to have a required field in Contact Form 7 only when another form field has a certain value. For example, if someone has an option to choose their preferred method of contact between email and telephone, you’d want to make the email address field or the phone call field required accordingly. This isn’t too difficult to accomplish in Contact Form 7. You can look at how Contact Form 7 validates text and email fields by viewing its code in:

contact-form-7/modules/text.php

Around line 91, you will see the validation filters defined by Contact Form 7:

add_filter( 'wpcf7_validate_text', 'wpcf7_text_validation_filter', 10, 2 );
add_filter( 'wpcf7_validate_text*', 'wpcf7_text_validation_filter', 10, 2 );
add_filter( 'wpcf7_validate_email', 'wpcf7_text_validation_filter', 10, 2 );
add_filter( 'wpcf7_validate_email*', 'wpcf7_text_validation_filter', 10, 2 );

Then, you can look at the function that handles the validation, wpcf7_text_validation_filter. The function accepts two parameters – $result and $tag. The $tag is the field being validated and the $result is, well, the result of the validation. In other words, did something go wrong?

The user-entered text of the field being checked is assigned to $value. Then, the function checks the current tag type. Is it text? Email? And it will process it accordingly. If a value is empty but the field is required, for example, the result will be invalid:

$result->invalidate( $tag, wpcf7_get_message( 'invalid_required' ) );

In this case the Contact Form 7 error message for required fields is given as an error (wpcf7_get_message( ‘invalid_required’ )). If $result-invalidate() is called, that field will fail validation and you will get a form error.

We’re not, of course, going to modify the plug-in’s files directly. We want to make our plug-in modifications safe from future plug-in updates so we will tie into the same filters that the plug-in uses. I can put this code in my theme’s functions.php file or put it into a my own plug-in. In my example, I am setting two conditional required fields – email and phone number. So, I will need to set my own custom validations for both text and email fields:

add_filter( 'wpcf7_validate_text', 'my_site_conditional_required', 10, 2 );
add_filter( 'wpcf7_validate_text*', 'my_site_conditional_required', 10, 2 );
add_filter( 'wpcf7_validate_email', 'my_site_conditional_required', 10, 2 );
add_filter( 'wpcf7_validate_email*', 'my_site_conditional_required', 10, 2 );

For both the text (phone) and email fields, I am using the function, my_site_conditional_required, for my own custom validations. Here is my complete function:

function my_site_conditional_required($result, $tag) {
    $tag = new WPCF7_Shortcode( $tag );
    $name = $tag->name;
    $value = isset( $_POST[$name] )
        ? trim( wp_unslash( strtr( (string) $_POST[$name], "\n", " " ) ) )
        : '';
    if ( "phone" == $name && 'Phone' == $_POST['contact-method'] ) :
        if ( '' == $value  ) :
            $result->invalidate( $tag, wpcf7_get_message( 'invalid_required' ) );
        endif;
    endif;
    if ( "email" == $name && 'Email' == $_POST['contact-method'] ) :
        if ( '' == $value ) :
            $result->invalidate( $tag, wpcf7_get_message( 'invalid_required' ) );
        elseif ( ! wpcf7_is_email( $value ) ) :
            $result->invalidate( $tag, wpcf7_get_message( 'invalid_email' ) );
        endif;
    endif;
    return $result;
}

Here is how it works. I am doing the same thing that Contact Form 7 does with setting the $value. Then, instead of checking the tag type, I want to check the tag name. I specifically want to look for the fields named “phone” or “email.” I also have a dropdown named “contact-form” so I can check the $_POST value of contact-form and use that to see if the field being checked should be required. If $_POST[‘contact-form’] is equal to “Phone,” I want the phone field to be required:

if ( "phone" == $name && 'Phone' == $_POST['contact-method'] ) :
        if ( '' == $value  ) :
            $result->invalidate( $tag, wpcf7_get_message( 'invalid_required' ) );
        endif;
endif;

I have an extra check for the email. I don’t just want to require the email address, I want to make sure it is a valid email address as well. Contact Form 7 provides a function to check an email, wpcf7_is_email:

elseif ( ! wpcf7_is_email( $value ) ) :

As you can see, my custom function works very similarly to the code in the plug-in. Finally, you probably want to show the user the field that is required based on their downdown selection. I added empty spans after the field labels that I can use to add and remove informational asterisks from:

<label class="email-label">Email</label>

Then, I can use jQuery to add and remove the asterisks:

jQuery(function() {
  add_required_asterisks();
  jQuery("select[name='contact-method']").on('change', function() {
    add_required_asterisks();
  });
});
function add_required_asterisks() {
  if ( jQuery("select[name='contact-method']").val() == "Phone" ) {
    jQuery(".phone-label .asterisk").html("*");
    jQuery(".email-label .asterisk").html("");
  }
  else {
    jQuery(".phone-label .asterisk").html("");
    jQuery(".email-label .asterisk").html("*");
  }
}

I created a JavaScript function that will see what my dropdown menu (named “contact-method”) is set to and then, based on that, turn the asterisks on or off. Then, I call that function on page load and, whenever the dropdown changes:

jQuery("select[name='contact-method']").on('change', function() {
    add_required_asterisks();
});

23 comments

  1. How could you accomplish the same result but instead of a dropdown selection it was a checkbox?
    Exp. textarea_B is only required if checkbox_A is selected.

    1. Hello Justin,

      To check against a checkbox, you would use a different filter:

      add_filter( ‘wpcf7_validate_textarea’, ‘my_site_conditional_required’, 10, 2 );
      add_filter( ‘wpcf7_validate_textarea*’, ‘my_site_conditional_required’, 10, 2 );

      Checkboxes are sent through as arrays in the POST, so you would check against the first element in the array. For example, let’s say that your checkbox was set up this way in Contact Form 7:

      Checkbox
      [checkbox need-message “true”]

      So, the array element would be $_POST[‘need-message’][0] and the value, if checked, would be true. In your my_site_conditional_required function, you can then check for that to be true:

      if ( “your-message” == $name && “true” == $_POST[‘need-message’][0]) :
      if ( ” == $value ) :
      $result->invalidate( $tag, wpcf7_get_message( ‘invalid_required’ ) );
      endif;
      endif;

      Then, you can change the jQuery to trigger on the checkbox click and if it is checked, add the asterisk. Hope this helps!

        1. Hello Piers,

          I was wondering if you would mind providing me with the Contact Form 7 code you are using and then I can look into updating my code. Thanks and thank for letting me know.

          Scott

  2. This is great! Do you know if there is a way to modify this so that if a field IS valid then change the value of the field?

    I am using jQuery to mask the phone field but I need to remove some characters when the field is valid, or on submission.

    Any thoughts?

  3. I feel like the answer to my problem may be here but I cannot sort it out yet!

    How would I add validation to ensure that at least one checkbox is checked in a form where there are several checkboxes? So something like this,

    What is your favorite drink?
    ___ soda
    ___ tea
    ___coffee

    And I want to be sure they check at least one of those boxes. Thanks in advance for any assistance!

    1. Hello Jim,

      The checkbox validation filter for Contact Form 7 is found in contact-form-7/modules/checkbox.php around line 198. You can add your own filter in your functions.php file or in your own plug-in for checkboxes:

      function my_site_checkbox_validation_filter( $result, $tag ) {
      	$tag = new WPCF7_FormTag( $tag );
      	$type = $tag->type;
      	$name = $tag->name;
      	$value = isset( $_POST[$name] ) ? (array) $_POST[$name] : array();
      	if ( 'No' == $_POST['have-water'] && "favorite-drink" == $name && empty( $value ) ) {
      		$result->invalidate( $tag, wpcf7_get_message( 'invalid_required' ) );
      	}
      	return $result;
      }
      add_filter( 'wpcf7_validate_checkbox', 'my_site_checkbox_validation_filter', 10, 2 );
      add_filter( 'wpcf7_validate_checkbox*', 'my_site_checkbox_validation_filter', 10, 2 );
      

      In my example, I am making the favorite drink option required if the user selects that they are not having water ('No' == $_POST['have-water']). If the user is not having water, and we’re checking the “favorite-drink” checkboxes, and the checkboxes are empty, we will display the error. I hope this helps! If you have any questions, let me know.

      Scott

  4. Good Morning,
    I’m developing a form, where I have two options on a radio button “Yes” and “No”, by default the “No” option is checked, but when the user checks yes it displays a new input file field to attach a file to the form (This option is displayed only if “Yes on the radio button” is marked).
    This is all done in Jquery ..
    However, if you check the “Yes” option, the input file field is mandatory .. (required)
    And I still can not figure it out ..
    Does anyone have any ideas?
    Remembering that the form is from contact form 7.

  5. Hi there,

    I was quite happy seeing there is a way to hack into CF7’s validation. Currently tried to do it with a checkbox that makes a telephone field required. It actually works, when I a) have the fields (i.e. fields are not missing on a form) and b) check the checkbox that makes the phone field required. As long as I don’t have those fields or don’t check the checkbox, the form just won’t validate. Did something change about the code for current CF7 versions?

    The code in use:

    add_filter( ‘wpcf7_validate_tel’, ‘xxx_conditional_required’, 9, 2 );
    add_filter( ‘wpcf7_validate_tel*’, ‘xxx_conditional_required’, 9, 2 );

    function xxx_conditional_required($result, $tag) {
    $tag = new WPCF7_Shortcode( $tag );

    $name = $tag[‘name’];

    $value = isset( $_POST[$name] )
    ? trim( wp_unslash( strtr( (string) $_POST[$name], “\n”, ” ” ) ) )
    : ”;

    if ( ‘Phone’ == $name && ‘Rückruf gewünscht’ == $_POST[‘Callback’][0] ) :
    if ( ” == $value ) :
    $result->invalidate( $tag, wpcf7_get_message( ‘invalid_required’ ) );
    endif;
    endif;

    return $result;
    }

    Thanks in advance

    Eno

  6. This is almost what I wanted to achieve, but I was hoping to do it without selecting a contact method.

    ie. If they enter an email then phone isn’t required, and if they enter a phone then email isn’t required.

    1. Sorry for the delay getting back to you, Adam. I believe you can just check for blanks in both instead of looking at a contact method. Here is an example:

      function my_site_conditional_required($result, $tag) {
      $tag = new WPCF7_Shortcode( $tag );
      $name = $tag->name;
      $value = isset( $_POST[$name] )
      ? trim( wp_unslash( strtr( (string) $_POST[$name], “\n”, ” ” ) ) )
      : ”;
      if ( “phone-number” == $name ) :
      if ( ” == $value && ” == $_POST[’email’] ) :
      $result->invalidate( $tag, wpcf7_get_message( ‘invalid_required’ ) );
      endif;
      endif;
      if ( “email” == $name ) :
      if ( ” == $value && ” == $_POST[‘phone-number’] ) :
      $result->invalidate( $tag, wpcf7_get_message( ‘invalid_required’ ) );
      endif;
      endif;
      return $result;
      }

      I hope this helps! If you have any questions, let me know.

      1. I just want to say Thank You. This worked great. However, for those looking to use it, remember to fix the apostropes etc. At least that is what I had to do.

  7. Hello

    I have file field and that depends on another dropdown field. How can i add condition logic on file field

    1. Thanks for the comment. If you were looking to make a file upload required when a select is set to a certain value, here is an example:


      function my_site_conditional_required($result, $tag) {
      $name = $tag->name;
      if ( $name == 'menu-868' ) {
      if( 'Upload required' == $_POST[$name] && $_FILES['file-597']['size'] == 0 ) {
      $result->invalidate( $tag, wpcf7_get_message( 'invalid_required' ) );
      }
      }
      return $result;
      }
      add_filter( 'wpcf7_validate_select', 'my_site_conditional_required', 10, 2 );
      add_filter( 'wpcf7_validate_select*', 'my_site_conditional_required', 10, 2 );

      In this example, when a select named ‘menu-868’ is set to ‘Upload required’, the file field ‘file-597’ needs to have something uploaded. You could also check other attributes on the uploaded file like file type or anything else. Take a look at Contact Form 7’s modules/file.php file for more information on how the file validation works in Contact Form 7. Hope this helps!

  8. Hi,
    First of all thank you for sharing this, it helped me a lot to modify the form as the client wanted.

    Here is what I did and how I did it :

    The client wanted for the website visitor to enter either an email or a telephone. If one of them is entered, he should be able to send his request.

    What I did is add this in functions.php of my theme :

    // remove the contact form 7 original filters
    remove_filter( ‘wpcf7_validate_tel’, ‘wpcf7_text_validation_filter’, 10, 2 );
    remove_filter( ‘wpcf7_validate_tel*’, ‘wpcf7_text_validation_filter’, 10, 2 );
    remove_filter( ‘wpcf7_validate_email’, ‘wpcf7_text_validation_filter’, 10, 2 );
    remove_filter( ‘wpcf7_validate_email*’, ‘wpcf7_text_validation_filter’, 10, 2 );

    // add my own filters
    add_filter( ‘wpcf7_validate_tel’, ‘ideead_champ_conditionnel’, 10, 2 );
    add_filter( ‘wpcf7_validate_tel*’, ‘ideead_champ_conditionnel’, 10, 2 );
    add_filter( ‘wpcf7_validate_email’, ‘ideead_champ_conditionnel’, 10, 2 );
    add_filter( ‘wpcf7_validate_email*’, ‘ideead_champ_conditionnel’, 10, 2 );

    function ideead_champ_conditionnel($result, $tag) {

    $tag = new WPCF7_Shortcode( $tag );

    $name = $tag->name;

    $value = isset( $_POST[$name] )
    ? trim( wp_unslash( strtr( (string) $_POST[$name], “\n”, ” ” ) ) )
    : ”;

    if ( ‘text’ == $tag->basetype ) {
    if ( $tag->is_required() && ” == $value ) {
    $result->invalidate( $tag, wpcf7_get_message( ‘invalid_required’ ) );
    }
    }

    if ( ’email’ == $tag->basetype ) {
    if ( $tag->is_required() && ” == $value && !$_POST[‘contrat’] ) { /added a condition !$_POST[‘contrat’] to oly apply if there is not the “contrat” field
    $result->invalidate( $tag, wpcf7_get_message( ‘invalid_required’ ) );
    } elseif ( ” != $value && ! wpcf7_is_email( $value ) ) {
    $result->invalidate( $tag, wpcf7_get_message( ‘invalid_email’ ) );
    }
    }

    if ( ‘url’ == $tag->basetype ) {
    if ( $tag->is_required() && ” == $value ) {
    $result->invalidate( $tag, wpcf7_get_message( ‘invalid_required’ ) );
    } elseif ( ” != $value && ! wpcf7_is_url( $value ) ) {
    $result->invalidate( $tag, wpcf7_get_message( ‘invalid_url’ ) );
    }
    }

    if ( ‘tel’ == $tag->basetype ) {
    if ( $tag->is_required() && ” == $value && !$_POST[‘contrat’]) { // added a condition !$_POST[‘contrat’] to oly apply if there is not the “contrat” field
    $result->invalidate( $tag, wpcf7_get_message( ‘invalid_required’ ) );
    } elseif ( ” != $value && ! wpcf7_is_tel( $value ) ) {
    $result->invalidate( $tag, wpcf7_get_message( ‘invalid_tel’ ) );
    }
    }

    if ( ” !== $value ) {
    $maxlength = $tag->get_maxlength_option();
    $minlength = $tag->get_minlength_option();

    if ( $maxlength && $minlength && $maxlength < $minlength ) {
    $maxlength = $minlength = null;
    }

    $code_units = wpcf7_count_code_units( stripslashes( $value ) );

    if ( false !== $code_units ) {
    if ( $maxlength && $maxlength invalidate( $tag, wpcf7_get_message( ‘invalid_too_long’ ) );
    } elseif ( $minlength && $code_units invalidate( $tag, wpcf7_get_message( ‘invalid_too_short’ ) );
    }
    }
    }

    return $result;

    }

    And then on the front end, I used a javascript file :

    // if tghe contrat field is there
    if (jQuery(“select[name=contrat]”).length > 0) {

    // we select the email and tel fields
    jQuery(document).on(“blur”, “input[type=email],input[type=tel]”, function () {
    // we get the fields data
    var input = jQuery(this);
    // we call the function below
    changeRequired(input);
    });
    }

    function changeRequired(input) {

    // we get the name and value of the field
    var valeur = input.val(), nom = input.context.name;

    //if the field is email and has a valuer, we delete the required class, put aria required to false and change the placeholder on tel field
    if (nom == ’email’ && valeur) {

    jQuery(‘input[type=tel]’).removeClass(‘wpcf7-validates-as-required’);
    jQuery(‘input[type=tel]’).attr(“aria-required”, “false”);
    jQuery(‘input[type=tel]’).attr(“placeholder”, “Téléphone”);

    }
    // else if the field is tel and has a value, we delete the required class, put aria required to false and change the placeholder on email field
    else if (nom == ‘tel’ && valeur) {
    jQuery(‘input[type=email]’).removeClass(‘wpcf7-validates-as-required’);
    jQuery(‘input[type=email]’).attr(“aria-required”, “false”);
    jQuery(‘input[type=email]’).attr(“placeholder”, “Email”);
    }

    else {
    // If the email field doesn’t have any value, we put back the tel field to what it was
    if (nom == ’email’) {

    jQuery(‘input[type=tel]’).addClass(‘wpcf7-validates-as-required’);
    jQuery(‘input[type=tel]’).attr(“aria-required”, “true”);
    jQuery(‘input[type=tel]’).attr(“placeholder”, “Téléphone*”);

    }
    // If the tel field doesn’t have any value, we put back the email field to what it was
    else if (nom == ‘tel’) {
    jQuery(‘input[type=email]’).addClass(‘wpcf7-validates-as-required’);
    jQuery(‘input[type=email]’).attr(“aria-required”, “true”);
    jQuery(‘input[type=email]’).attr(“placeholder”, “Email*”);
    }

    }

    }

  9. Meanwhile, thank you for your immense patience. Answer everyone by discussing everyone’s problems. Congratulations, you are a hero!
    So now I try too? 🙂

    I have two fields, one text field with Linkedin url and another File upload field.
    When I open the form, the File upload field is required, but if I enter the url in the linkedin text field, the file upload field must no longer be required.

    code:
    Linkedin[text linkedin placeholder”Linkedin url”]

    Upload CV
    [file* curriculum limit:5000000 filetypes:pdf|docx id:file-upload]

    Thank you from Rome, if you come I offer you a beer 😉
    Lorenzo

  10. Hi Scott,
    your code is very helpful and still works in 2020 – thanks a lot for sharing!

    I had some trouble getting the correct value from a checkbox “group”. The field definition in the form was [checkbox* booking id:bookng exclusive “no” “yes”]. I’m not sure if its due to the “exclusive” attribute (tying multiple checkboxes together like radio buttons), but with $_POST[‘booking’][0] I only got the first character of the value (i.e. “n” or “y”). Using just $_POST[‘booking’] retrieved the complete value.

    Otherwise your code example perfectly solved my problem at hand.
    Thanks again,
    Chris

    1. Hello Chris,

      I believe you are correct that the exclusive option will send through just the string value of the selected checkbox. Because it is a string, and not an array, you are just grabbing the first letter of each. Using the Web Inspector in Chrome, when I submitted the form using exclusive, as you sent, the POST was just: booking: yes. Then I removed the exclusive attribute and saw that it was getting passed as:


      booking[]: yes
      booking[]: no

      So I believe you have correctly identified the issue.

      Sincerely,
      Scott

Leave a comment

Your email address will not be published. Required fields are marked *