title: Custom HTML5 form validation errors only happen after first attempt
body: <p>The below code allows my client to create a custom form with different types of fields and then allows their clients to fill that form in and submit it somewhere. Basically, a custom form builder for Wordpress.</p>
  
  <p>I have successfully replaced the custom behavior for the <code>HTML5</code> form validation error messages using the below code. However, the custom message I have set for the different types of form fields only appear the 2nd time the form submit is attempted. The first time the submit button is pressed, the default message appears instead.</p>
  
  <p>How can I get the custom messages I've defined to be used the first time as well?</p>
  
  <p>Sorry for the long code, it's a complicated application. In the <code>PHP</code> section, look under the <code>Start Input Wrap</code> section for where I set the custom error messages like so:</p>
  
  <p><code>onchange="setCustomValidity(\'\')" oninvalid="this.setCustomValidity(\''.$placeholder.' is required.\')"</code></p>
  
  <p><strong>PHP/HTML:</strong></p>
  
  <pre><code>function sdForm($atts) {
      //Check for a form id variable in the shortcode and, if it's exists, put it into the $formID variable
      $shortcodeAtt = shortcode_atts( array(
          'id' =&gt; false,
          ), $atts );
      if ($shortcodeAtt != false) {
          $formID = $shortcodeAtt['id'];
  
          //Setup this form's settings and variables
          global $post;
          $post = get_post($formID);
          setup_postdata($post);
  
          //General Variables
          $formTitle = get_the_title();
          $formSlug = $post-&gt;post_name;
  
          //Form Submit Behavior
          $onSubmit = get_field('on_form_submit');
          if ($onSubmit == 'default') {
              //refresh page, display thank you
          } elseif ($onSubmit == 'message') {
              //refresh page, display custom message
              $message = get_field('custom_submission_message');
          } elseif ($onSubmit == 'url') {
              //send user to this page
              $url = get_field('submission_redirect');
          }
  
          //Form Submit To
          $actionUrl = get_field('form_action_link');
  
          //Set value of submit button
          if (get_field('submit_value')) {
              $submitValue = get_field('submit_value');
          } else {
              $submitValue = 'Submit';
          }
  
          //Set ID for form
          if (get_field('form_id')) {
              $formHtmlId = get_field('form_id');
          } else {
              $formHtmlId = $formSlug;
          }
  
          //Set Classes for form
          if (get_field('form_classes')) {
              $formClasses = get_field('form_classes');
          } else {
              $formClasses = '';
          }
  
          //Set any messages to display
          $messages = '';
          $confirmMessage = get_field('custom_submission_message');
          if ($_GET['confirm-message']) {
              $messages .= $confirmMessage.'&lt;br /&gt;';
          } elseif ($_GET['confirm-submit']) {
              $messages .= 'Thanks for your submission!';
          }
  
          //Start the HTML output
          $output = '';
          //Do some clearing, just in case
          $output .= '&lt;div style="clear:both;"&gt;&lt;/div&gt;';
          $output .= '&lt;div class="sdFormWrapper"&gt;';
          $output .= '&lt;div class="sdFormMessages"&gt;';
          $output .= $messages;
          $output .= '&lt;/div&gt;';
          $output .= '&lt;form name="'.$formSlug.'" id="'.$formHtmlId.'" class="'.$formClasses.' sd_form" method="post" ';
          if (isset($actionUrl)) {
              $output .= 'action="'.$actionUrl.'" ';
          }
          $output .= '&gt;';
  
          //We add a hidden field to identify which form is being processed after submit
          $output .= '&lt;input type="hidden" name="formID" value="'.$formID.'" /&gt;';
  
          //This loops through each added field and displays each one
          if (have_rows('input_type')) {
              while (have_rows('input_type')) { the_row();
  
              //We're putting a uniform wrap around each input element for styling
              if (get_sub_field('fill_row') == true) {
                  $fullWidth = 'fullWidth';
              } else {
                  $fullWidth = '';
              }
              if (get_row_layout() == 'section_header') {
                  $output .= '&lt;div class="sectionHeader"&gt;';
                  $output .= get_sub_field('header_text');
                  $output .= '&lt;/div&gt;';
              } else {
  
  
  
              //We turn the field label into a slug with no spaces or special characters
              $fieldSlug = sanitize_title(get_sub_field('label'));
  
              //Check if this field is required
              if (get_sub_field('required') == true) {
                      $required = 'required';
              } else {
                      $required = '';
              }
              //Check for custom name
              if (get_sub_field('input_name')) {
                  $name = get_sub_field('input_name');
              } else {
                  $name = $fieldSlug;
              }
              //Check for custom html id
              if (get_sub_field('input_id')) {
                  $htmlId = get_sub_field('input_id');
              } else {
                  $htmlId = $name;
              }
              //Check for custom html classes
              if (get_sub_field('input_class')) {
                  $htmlClass = get_sub_field('input_class').' sdForm-input';
              } else {
                  $htmlClass = 'sdForm-input';
              }
              //Check for icons
              if (get_sub_field('icon')) {
                      $icon = get_sub_field('icon');
                  } else {
                      $icon = '';
                  }
              //Generate Placeholder  (this is the field label)
              $placeholder = get_sub_field('label');
  
              //Start input wrap
              $output .= '&lt;div title="'.$placeholder.'" class="sdForm-inputWrap '.$fullWidth.'"&gt;';
  
              //Error message for field
              //$output .= '&lt;div class="inputError"&gt;'.$placeholder.' is required!&lt;/div&gt;'; 
                  $output .= $icon;
                  if (get_row_layout() == 'text') {
                      $output .= '&lt;input type="text" name="'.$name.'" id="'.$htmlId.'" class="'.$htmlClass.'" placeholder="'.$placeholder.'" title="'.$placeholder.'" '.$required.' onchange="setCustomValidity(\'\')" oninvalid="this.setCustomValidity(\''.$placeholder.' is required.\')"  /&gt;';
                  } elseif (get_row_layout() == 'textBox') {
                      $output .= '&lt;textarea rows="4" id="'.$htmlId.'" class="'.$htmlClass.'" placeholder="'.$placeholder.'" title="'.$placeholder.'" '.$required.' onchange="setCustomValidity(\'\')" oninvalid="this.setCustomValidity(\''.$placeholder.' is required.\')"&gt;&lt;/textarea&gt;';
                  } elseif (get_row_layout() == 'email') {
                      $output .= '&lt;input type="email" name="'.$name.'" id="'.$htmlId.'" class="'.$htmlClass.'" placeholder="'.$placeholder.'" title="'.$placeholder.'" '.$required.' onchange="setCustomValidity(\'\')" oninvalid="this.setCustomValidity(\''.$placeholder.' is required and must be valid.\')"/&gt;';
                  } elseif (get_row_layout() == 'phone') {
                      $output .= '&lt;input type="tel" name="'.$name.'" id="'.$htmlId.'" class="'.$htmlClass.'" placeholder="'.$placeholder.'" title="'.$placeholder.'" '.$required.' onchange="setCustomValidity(\'\')" oninvalid="this.setCustomValidity(\''.$placeholder.' is required.\')"/&gt;';
                  } elseif (get_row_layout() == 'url') {
                      $output .= '&lt;input type="url" name="'.$name.'" id="'.$htmlId.'" class="'.$htmlClass.'" placeholder="'.$placeholder.'" title="'.$placeholder.'" '.$required.' onchange="setCustomValidity(\'\')" oninvalid="this.setCustomValidity(\''.$placeholder.' is required.\')"/&gt;';
                  } elseif (get_row_layout() == 'checkboxes') {
                      if (have_rows('checkbox_selections')) {
                          if ($placeholder != '') {
                              $output .= '&lt;label for="'.$htmlId.'"&gt;'.$placeholder.'&lt;/label&gt;&lt;br /&gt;';
                          }
                          while(have_rows('checkbox_selections')) { the_row();
                              $selection = get_sub_field('checkbox_selection');
                              $output .= '&lt;input type="checkbox" name="'.$name.'" value="'.$selection.'" id="'.$htmlId.'" class="'.$htmlClass.'" title="'.$placeholder.'" '.$required.' onchange="setCustomValidity(\'\')" oninvalid="this.setCustomValidity(\''.$placeholder.' is required.\')"/&gt;';
                              $output .= '&amp;nbsp;&amp;nbsp;&lt;label for="'.$htmlId.'"&gt;'.$selection.'&lt;/label&gt;&lt;br /&gt;';
                          }
                      }
                  } elseif (get_row_layout() == 'radios') {
                      if (have_rows('radio_selections')) {
                          if ($placeholder != '') {
                              $output .= '&lt;label for="'.$htmlId.'"&gt;'.$placeholder.'&lt;/label&gt;&lt;br /&gt;';
                          }
                          while(have_rows('radio_selections')) { the_row();
                              $selection = get_sub_field('radio_selection');
                              $output .= '&lt;input type="radio" name="'.$name.'" value="'.$selection.'" id="'.$htmlId.'" class="'.$htmlClass.'" title="'.$placeholder.'" '.$required.' onchange="setCustomValidity(\'\')" oninvalid="this.setCustomValidity(\''.$placeholder.' is required.\')"/&gt;';
                              $output .= '&amp;nbsp;&amp;nbsp;&lt;label for="'.$htmlId.'"&gt;'.$selection.'&lt;/label&gt;&lt;br /&gt;';
                          }
                      }
                  } elseif (get_row_layout() == 'select') {
                      if (get_sub_field('show_label', true)) {
                          $output .= '&lt;span class="dropdownLabel"&gt;'.$placeholder.': &lt;/span&gt;';
                          $showLabel = 'showLabel';
                      } else {
                              $showLabel = '';
                      }
                      $optionSlug = sanitize_title($selection);
                      if (have_rows('dropdown_selections')) {
  
                          $output .= '&lt;select name="'.$name.'" id="'.$htmlId.'" class="'.$htmlClass.' '.$showLabel.'" title="'.$placeholder.'" '.$required.' onchange="setCustomValidity(\'\')" oninvalid="this.setCustomValidity(\''.$placeholder.' is required.\')"&gt;';                  
                          $output .= '&lt;option selected="select" disabled&gt;';
                          if ($showLabel != 'showLabel') {
                              $output .= $placeholder;
                          }
                          $output .= '&lt;/option&gt;';
                          while(have_rows('dropdown_selections')) { the_row();
                              $selection = get_sub_field('dropdown_selection');
                              $optionSlug = sanitize_title($selection);
                              $output .= '&lt;option value="'.$optionSlug.'"&gt;'.$selection.'&lt;/option&gt;';                           
                          }
                          $output .= '&lt;/select&gt;';
                      }
                  } 
              $output .= '&lt;div style="clear:both;"&gt;&lt;/div&gt;';
              $output .= '&lt;/div&gt;'; //.sdForm-inputWrap
              } //end else (if not a message header)
              }//endwhile
  
          }
  
          $output .= '&lt;input type="submit" value="'.$submitValue.'" /&gt;';
  
          $output .= '&lt;/form&gt;&lt;div style="clear: both;"&gt;&lt;/div&gt;&lt;/div&gt;';
  
          wp_reset_postdata();
  
      } else { //There is no ID set, so we can't show any form
          //ERROR!! No form ID specified
      }
      echo $output;
  }
  
  add_shortcode('sdForm', 'sdForm');
  </code></pre>
  
  <p><strong>JS:</strong></p>
  
  <pre><code> (function($) {
       $(document).ready(function() {
           function sd_replaceValidationUI(form) {
               // Suppress the default bubbles
               form.addEventListener("invalid", function(event) {
                   event.preventDefault();
               }, true);
  
               // Support Safari, iOS Safari, and the Android browser—each of which do not prevent
               // form submissions by default
               form.addEventListener("submit", function(event) {
                   if (!this.checkValidity()) {
                       event.preventDefault();
                   }
               });
  
               var submitButton = form.querySelector("button:not([type=button]), input[type=submit]");
               submitButton.addEventListener("click", function(event) {
                   var invalidFields = form.querySelectorAll(":invalid"),
                       errorMessages = form.querySelectorAll(".error-message"),
                       parent;
  
                   // Remove any existing messages
                   for (var i = 0; i &lt; errorMessages.length; i++) {
                       errorMessages[i].parentNode.removeChild(errorMessages[i]);
                   }
  
                   for (var i = 0; i &lt; invalidFields.length; i++) {
                       parent = invalidFields[i].parentNode;
                       parent.insertAdjacentHTML("beforeend", "&lt;div class='error-message'&gt;" +
                           invalidFields[i].validationMessage +
                           "&lt;/div&gt;");
                   }
  
                   // If there are errors, give focus to the first invalid field
                   if (invalidFields.length &gt; 0) {
                       invalidFields[0].focus();
                   }
               });
           }
  
           // Replace the validation UI for all forms
           var forms = document.querySelectorAll("form");
           for (var i = 0; i &lt; forms.length; i++) {
               sd_replaceValidationUI(forms[i]);
           }
  
  
           //Changes the text of the dropdown to #666 when a selection is made
           $('.sdForm-inputWrap select').change(function() {
               $(this).css('color', '#666');
           });
  
       })
   })(jQuery);
  </code></pre>