Bootstrap 4表单验证在使用无效反馈时不起作用,即使输入'is-valid'

Posted

技术标签:

【中文标题】Bootstrap 4表单验证在使用无效反馈时不起作用,即使输入\'is-valid\'【英文标题】:Bootstrap 4 form validation not working while Using invalid-feedback even when input 'is-valid'Bootstrap 4表单验证在使用无效反馈时不起作用,即使输入'is-valid' 【发布时间】:2020-10-31 01:39:38 【问题描述】:

我正在尝试使用 Bootstrap 4 进行非常基本的表单验证。出于某种原因,在我的示例中将“is-valid”类添加到我的第二个输入时,因为第一个输入具有“is-invalid”类,第二个输入将有绿色边框(因为它应该是有效的!),但是它也会显示无效反馈 div(“此字段是必需的”消息!)。

见:

我不确定我在这里做错了什么......这是代码:

/**
 * AJAX Post script
 */

const ERROR_TYPE_FATALERROR = 1;
const ERROR_TYPE_INPUTERROR = 2;
const ERROR_TYPE_GLOBALMESSAGE = 3;


// Run through the validate() function ONLY once the form as been submitted once!
// Otherwise, user will get validation right away as he types! Just a visual thing ...
var formSubmittedOnce = false;

/**
 * submitFormData()
 * Serialize and post form data with an AJAX call
 *
 * Example: onClick="submitFormData('frm1',['username','email'])"
 * 
 * @param string formid essentially the 'id' of the container holding all form elements (i.e. <tr id="rowfrm_1">, <form id='frm1'>, etc.)
 * @param array fields list of field names that may produce input errors (i.e. ['username','email'] )
 */
 function submitFormData(formid, fields) 
  
    // flag form was submitted once!
    formSubmittedOnce = true;
  
    // ----------------------------------
    // first rehide all error containers
    // ----------------------------------    
    $('#fatalError').removeClass('d-block');
    $('#fatalErrorID').removeClass('d-block');
    $('#fatalErrorTrace').removeClass('d-block');
    $('#fatalErrorGoBack').removeClass('d-block');
    
    $('#globalMessage').removeClass('d-block');
    $('#globalMessageID').removeClass('d-block');
    $('#globalMessageTrace').removeClass('d-block');
    $('#globalMessageFooter').removeClass('d-block');
    $('#globalMessageMailLink').removeClass('d-block');
    $('#globalMessageGoBackLink').removeClass('d-block');

    // rehide error containers of all inputs that might produce errors 
    if (fields != null) 
      for (const f of fields) 
        $('#' + f + '_inputError').removeClass('d-block');
      
    


    // ----------------------------------
    // loop form elements and validate required fields
    // ----------------------------------
    var formNode = $("#"+formid);
    var formInputs = formNode.find("select, textarea, input");    
    var submit = true;

    for(var i = 0; i < formInputs.length; ++i) 
      var input = formInputs[i];

      // validate fields
      if( validate(input) === false )
        submit = false;
      
     


    if(submit === true) 

    // ----------------------------------
    // get form data and serialize it!
    // ----------------------------------
  
      // formid comes from a <form>. just serialize it
      if( formNode.prop("tagName") === "FORM" )
        var formData = formNode.serialize();
      
      // formid doesn't come from a <form>
      else 
  
        // get all form control
        var myInputs = formNode.clone();
  
        // bug with clone() and SELECT controls: it'll only get the value of the option having the 'selected' attribute (the default value)
        // this hack will change the value clone() got to the actual user selected value, and not the default set value!
        formNode.find('select').each(function(i) 
          myInputs.find('select').eq(i).val($(this).val());
        )
  
        // create a dummy form, append all inputs to it and serialize it.
        var formData = $('<form>').append(myInputs).serialize();
      


      // ----------------------------------
      // POST !
      // ----------------------------------
      $.ajax(
        type: 'POST',
        url: $(location).attr('href'),
        data: formData,
        dataType : "json",
      ).done(function(response) 
  
        // get response
        if(response) 
    
          // if we got success, redirect if we got a redirect url!
          if ( response.success != null ) 
            if (typeof response.success === "string") 
              window.location.replace(response.success);
            
          
          
          // if anything else, php returned some errors or a message to display (i.e. 'data saved!')
          else  
            showMessages(response);
          
        
  
        // Successful post, but no response came back !?
        // assume success, since no 'success' response came back, thus keeping same page as is
        else 
          console.warn("Post sent, but no response came back!? Assuming successful post...");
        
  
      ).fail(function(xhr, status, error)  // we get here if we don't have a proper response/json sent!
        
        console.error("Ajax failed: " + xhr.statusText);
        console.error(status);
        console.error(error);
  
        var ajaxError = 
          'type' : ERROR_TYPE_FATALERROR,
          'message' : '<strong>Ajax failure!</strong><br/><br/>' + status + '<br/><br/>' + error, 
          'trace' : null, 
          'id' : null,
          'goback' : null,
          'adminMailtoLnk' : 'mailto:' + 'ravenlost2@gmail.com'
        ;
  
        showMessages(ajaxError);
        
      );
    



/**
 * showMessages()
 * show error messages in page based on JSON response
 * @param response JSON object holding response with (error) messages to display
 */
function showMessages(response)
  
  // error type
  switch (response.type) 
    
    // ----------------------------
    // GLOBAL MESSAGE
    // ----------------------------
    case ERROR_TYPE_GLOBALMESSAGE:
      $('#globalMessage').addClass('d-block');
    
      // set global message header message type 
      $('#globalMessage').removeClass("error warning info");           
      $('#globalMessage').addClass(response.gmType);

      $('#globalMessageIcon').removeClass("fa-exclamation-triangle fa-info-circle");
      $('#globalMessageIcon').addClass(response.gmIcon);
                      
      $('#globalMessageTitle').empty();
      $('#globalMessageTitle').append(response.gmTitle);
        
      // set message
      $('#globalMessagePH').empty();
      $('#globalMessagePH').append(response.message);
        
      // set uniq error id
      if (response.id != null) 
        $('#globalMessageID').addClass('d-block');
        $('#globalMessageIDPH').empty();
        $('#globalMessageIDPH').append(response.id);
      
  
      // set stacktrace
      if(response.trace != null) 
      $('#globalMessageTrace').addClass('d-block');
        $('#globalMessageTracePH').empty();
        $('#globalMessageTracePH').append(response.trace);
      
        
      // set footer
      if( (response.showContactAdmin == true) || (response.goback != null) ) 
         
        $('#globalMessageFooter').addClass('d-block');

        // contact admin
        if(response.showContactAdmin == true) 
          $('#globalMessageMailLink').addClass('d-block');
          $('#globalMessageMailLinkPH').attr('href', response.adminMailtoLnk);
        

        // go back 
        if(response.goback != null)
          $('#globalMessageGoBackLink').addClass('d-block');
          $('#globalMessageGoBackLinkPH').attr('href', response.goback);
        

      

      break;

    // ----------------------------
    // FATAL ERROR
    // ----------------------------              
    case ERROR_TYPE_FATALERROR:

      // hide content if we got a fatal as to prevent user from fiddling around and not reading the message!
      $('#content').addClass('d-none'); 

      $('#fatalError').addClass('d-block');

      // set message
      $('#fatalErrorMessagePH').empty();
      $('#fatalErrorMessagePH').append(response.message);

      // reset mailto link
      $('#fatalErrorMailLink').attr('href', response.adminMailtoLnk);

      // set stacktrace
      if (response.trace != null) 
      $('#fatalErrorTrace').addClass('d-block');
        $('#fatalErrorTracePH').empty();
        $('#fatalErrorTracePH').append(response.trace);
      

      // set uniq error id
      if (response.id != null) 
        $('#fatalErrorID').addClass('d-block');
        $('#fatalErrorIDPH').empty();
        $('#fatalErrorIDPH').append(response.id);
                    

      // set 'go back' url
      if(response.goback != null) 
        $('#fatalErrorGoBack').addClass('d-block');
        $('#fatalErrorGoBackLink').attr('href', response.goback);
      

      break;

    // ----------------------------
    // INPUT ERROR
    // ---------------------------- 
    case ERROR_TYPE_INPUTERROR:

      for (var field in response.fields) 
        var msg = eval('response.fields.' + field);
        $('#' + field + '_inputError').addClass('d-block')
        $('#' + field + '_inputError_message').empty();
        $('#' + field + '_inputError_message').append(msg);
      

      break;

    default:
      console.error('Got an invalid error type from the response!');

  


 
 /**
  * validate()
  * Validate if field is empty or not
  * @param input form element
  * @return boolean
  */
function validate(input) 

  if(formSubmittedOnce === true) 
    
    if( input.hasAttribute('required') ) 
      if(input.value.trim() === '') 
        input.classList.remove('is-valid');
        input.classList.add('is-invalid');
        return false;
      
      else 
        input.classList.remove('is-invalid');
        input.classList.add('is-valid');
        return true;
      
    
  
    else 
      // if we get here, then any other inputs not marked as 'required' are valid
      input.classList.add('is-valid');
    

  
  
<html>

<head>
  <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" rel="stylesheet" type="text/css">
  <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
</script>
</head>

<body>

<form id="testfrm" class="form-group">

Username: <input type="text" name="username" aria-describedby="username_required username_inputError" class="form-control is-invalid" oninput="validate(this)" required/><br>
<div id="username_required" class="pl-1 invalid-feedback">
  This field is required!
</div>

<!-- if bad username format or already taken, print form input error -->
<div id="username_inputError" class="col alert alert-danger alert-dismissible fade show mt-2 py-2 pl-3 pr-5 text-left d-none">
  <small>
    <strong>Error!</strong> <span id="username_inputError_message"></span>
    <button type="button" aria-label="Close" class="close pt-1 pr-2" onclick="$('#username_inputError').removeClass('d-block').addClass('d-none');">×</button>
  </small>
</div>

Email: <input type="text" name="email" aria-describedby="email_required email_inputError" class="form-control is-valid" oninput="validate(this)" required/><br>
<div id="email_required" class="pl-1 invalid-feedback">
  This field is required!
</div>

<!-- if bad email format or already taken, print form input error -->
<div id="email_inputError" class="col alert alert-danger alert-dismissible fade show mt-2 py-2 pl-3 pr-5 text-left d-none">
  <small>
    <strong>Error!</strong> <span id="email_inputError_message"></span>
    <button type="button" aria-label="Close" class="close pt-1 pr-2" onclick="$('#email_inputError').removeClass('d-block').addClass('d-none');">×</button>
  </small>
</div>

Comment: <input type="text" name="comment" class="form-control is-valid"><br>

<input type="button" value="Submit" onclick="submitFormData('testfrm',['username','email'])" class="is-valid">
</form>

<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.bundle.min.js" integrity="sha384-6khuMg9gaYr5AxOqhkVIODVIvm9ynTT5J4V1cfthmT+emCG6yVmEZsRHdxlotUnm" crossorigin="anonymous"></script>



</body>
</html>

如果有人能阐明这一点.. 干杯!帕特

【问题讨论】:

JS在哪里? 对不起,如果你在谈论引导 JS,那么它通常就在我的结束体标签之前。我已经编辑了 sn-p 以反映我所拥有的。另一方面,如果您正在谈论 submitFormData() JS,则此处不需要它来找出问题所在。放弃它会有点矫枉过正,更令人困惑。我只需要弄清楚如何/何时放置“is-(in)valid”类等。 addClass 当提交表单的值为空或空时。如果不向我们展示实际的过度杀伤 JS 函数submitFormData 我会发布它...但我应该能够仅使用这个 sn-p html 来重现它...我不想/需要使用提交按钮进行测试...sn -p,按原样,应该按原样显示正确的无效反馈,但它没有......你明白我的意思吗? 不,我没有! 【参考方案1】:

它不起作用的原因是您没有包装标签并输入form-group div

验证已经改变了它过去用于引导的方式 - 这意味着 is-valid 和 is invalid 不知道在哪里看,所以当它不在 form-group div 中时,它会应用 is-invalid 消息给所有匹配的divs

我添加了label 以使其更好,而不是仅使用电子邮件:&lt;input&gt;

如果您现在提交没有任何值的表单,它将显示错误 _ 只要您输入输入,错误就会消失。

运行下面的代码片段以查看它的工作情况。

/**
 * AJAX Post script
 */

const ERROR_TYPE_FATALERROR = 1;
const ERROR_TYPE_INPUTERROR = 2;
const ERROR_TYPE_GLOBALMESSAGE = 3;


// Run through the validate() function ONLY once the form as been submitted once!
// Otherwise, user will get validation right away as he types! Just a visual thing ...
var formSubmittedOnce = false;

/**
 * submitFormData()
 * Serialize and post form data with an AJAX call
 *
 * Example: onClick="submitFormData('frm1',['username','email'])"
 * 
 * @param string formid essentially the 'id' of the container holding all form elements (i.e. <tr id="rowfrm_1">, <form id='frm1'>, etc.)
 * @param array fields list of field names that may produce input errors (i.e. ['username','email'] )
 */
function submitFormData(formid, fields) 

  // flag form was submitted once!
  formSubmittedOnce = true;

  // ----------------------------------
  // first rehide all error containers
  // ----------------------------------    
  $('#fatalError').removeClass('d-block');
  $('#fatalErrorID').removeClass('d-block');
  $('#fatalErrorTrace').removeClass('d-block');
  $('#fatalErrorGoBack').removeClass('d-block');

  $('#globalMessage').removeClass('d-block');
  $('#globalMessageID').removeClass('d-block');
  $('#globalMessageTrace').removeClass('d-block');
  $('#globalMessageFooter').removeClass('d-block');
  $('#globalMessageMailLink').removeClass('d-block');
  $('#globalMessageGoBackLink').removeClass('d-block');

  // rehide error containers of all inputs that might produce errors 
  if (fields != null) 
    for (const f of fields) 
      $('#' + f + '_inputError').removeClass('d-block');
    
  


  // ----------------------------------
  // loop form elements and validate required fields
  // ----------------------------------
  var formNode = $("#" + formid);
  var formInputs = formNode.find("select, textarea, input");
  var submit = true;

  for (var i = 0; i < formInputs.length; ++i) 
    var input = formInputs[i];

    // validate fields
    if (validate(input) === false) 
      submit = false;
    
  


  if (submit === true) 

    // ----------------------------------
    // get form data and serialize it!
    // ----------------------------------

    // formid comes from a <form>. just serialize it
    if (formNode.prop("tagName") === "FORM") 
      var formData = formNode.serialize();
    
    // formid doesn't come from a <form>
    else 

      // get all form control
      var myInputs = formNode.clone();

      // bug with clone() and SELECT controls: it'll only get the value of the option having the 'selected' attribute (the default value)
      // this hack will change the value clone() got to the actual user selected value, and not the default set value!
      formNode.find('select').each(function(i) 
        myInputs.find('select').eq(i).val($(this).val());
      )

      // create a dummy form, append all inputs to it and serialize it.
      var formData = $('<form>').append(myInputs).serialize();
    


    // ----------------------------------
    // POST !
    // ----------------------------------
    $.ajax(
      type: 'POST',
      url: $(location).attr('href'),
      data: formData,
      dataType: "json",
    ).done(function(response) 

      // get response
      if (response) 

        // if we got success, redirect if we got a redirect url!
        if (response.success != null) 
          if (typeof response.success === "string") 
            window.location.replace(response.success);
          
        

        // if anything else, PHP returned some errors or a message to display (i.e. 'data saved!')
        else 
          showMessages(response);
        
      

      // Successful post, but no response came back !?
      // assume success, since no 'success' response came back, thus keeping same page as is
      else 
        console.warn("Post sent, but no response came back!? Assuming successful post...");
      

    ).fail(function(xhr, status, error)  // we get here if we don't have a proper response/json sent!

      console.error("Ajax failed: " + xhr.statusText);
      console.error(status);
      console.error(error);

      var ajaxError = 
        'type': ERROR_TYPE_FATALERROR,
        'message': '<strong>Ajax failure!</strong><br/><br/>' + status + '<br/><br/>' + error,
        'trace': null,
        'id': null,
        'goback': null,
        'adminMailtoLnk': 'mailto:' + 'ravenlost2@gmail.com'
      ;

      showMessages(ajaxError);

    );
  



/**
 * showMessages()
 * show error messages in page based on JSON response
 * @param response JSON object holding response with (error) messages to display
 */
function showMessages(response) 

  // error type
  switch (response.type) 

    // ----------------------------
    // GLOBAL MESSAGE
    // ----------------------------
    case ERROR_TYPE_GLOBALMESSAGE:
      $('#globalMessage').addClass('d-block');

      // set global message header message type 
      $('#globalMessage').removeClass("error warning info");
      $('#globalMessage').addClass(response.gmType);

      $('#globalMessageIcon').removeClass("fa-exclamation-triangle fa-info-circle");
      $('#globalMessageIcon').addClass(response.gmIcon);

      $('#globalMessageTitle').empty();
      $('#globalMessageTitle').append(response.gmTitle);

      // set message
      $('#globalMessagePH').empty();
      $('#globalMessagePH').append(response.message);

      // set uniq error id
      if (response.id != null) 
        $('#globalMessageID').addClass('d-block');
        $('#globalMessageIDPH').empty();
        $('#globalMessageIDPH').append(response.id);
      

      // set stacktrace
      if (response.trace != null) 
        $('#globalMessageTrace').addClass('d-block');
        $('#globalMessageTracePH').empty();
        $('#globalMessageTracePH').append(response.trace);
      

      // set footer
      if ((response.showContactAdmin == true) || (response.goback != null)) 

        $('#globalMessageFooter').addClass('d-block');

        // contact admin
        if (response.showContactAdmin == true) 
          $('#globalMessageMailLink').addClass('d-block');
          $('#globalMessageMailLinkPH').attr('href', response.adminMailtoLnk);
        

        // go back 
        if (response.goback != null) 
          $('#globalMessageGoBackLink').addClass('d-block');
          $('#globalMessageGoBackLinkPH').attr('href', response.goback);
        

      

      break;

      // ----------------------------
      // FATAL ERROR
      // ----------------------------              
    case ERROR_TYPE_FATALERROR:

      // hide content if we got a fatal as to prevent user from fiddling around and not reading the message!
      $('#content').addClass('d-none');

      $('#fatalError').addClass('d-block');

      // set message
      $('#fatalErrorMessagePH').empty();
      $('#fatalErrorMessagePH').append(response.message);

      // reset mailto link
      $('#fatalErrorMailLink').attr('href', response.adminMailtoLnk);

      // set stacktrace
      if (response.trace != null) 
        $('#fatalErrorTrace').addClass('d-block');
        $('#fatalErrorTracePH').empty();
        $('#fatalErrorTracePH').append(response.trace);
      

      // set uniq error id
      if (response.id != null) 
        $('#fatalErrorID').addClass('d-block');
        $('#fatalErrorIDPH').empty();
        $('#fatalErrorIDPH').append(response.id);
      

      // set 'go back' url
      if (response.goback != null) 
        $('#fatalErrorGoBack').addClass('d-block');
        $('#fatalErrorGoBackLink').attr('href', response.goback);
      

      break;

      // ----------------------------
      // INPUT ERROR
      // ---------------------------- 
    case ERROR_TYPE_INPUTERROR:

      for (var field in response.fields) 
        var msg = eval('response.fields.' + field);
        $('#' + field + '_inputError').addClass('d-block')
        $('#' + field + '_inputError_message').empty();
        $('#' + field + '_inputError_message').append(msg);
      

      break;

    default:
      console.error('Got an invalid error type from the response!');

  



/**
 * validate()
 * Validate if field is empty or not
 * @param input form element
 * @return boolean
 */
function validate(input) 

  if (formSubmittedOnce === true) 

    if (input.hasAttribute('required')) 
      if (input.value.trim() == '') 
        input.classList.remove('is-valid');
        input.classList.add('is-invalid');
        return false;
       else 
        input.classList.remove('is-invalid');
        input.classList.add('is-valid');
        return true;
      
     else 
      // if we get here, then any other inputs not marked as 'required' are valid
      input.classList.add('is-valid');
    

  

<html>

<head>

  <!-- Latest compiled and minified CSS -->
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css">

  <!-- jQuery library -->
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>

  <!-- Popper JS -->
  <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js"></script>

  <!-- Latest compiled javascript -->
  <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js"></script>
</head>

<body>

  <form id="testfrm" class="form-group">

    <div class="form-group">

      <label class="form-control-label" for="username_required">Username</label>
      <input type="text" name="username" aria-describedby="username_required username_inputError" class="form-control is-invalid" oninput="validate(this)" required/><br>
      <div id="username_required" class="pl-1 invalid-feedback">
        This field is required!
      </div>

      <!-- if bad username format or already taken, print form input error -->
      <div id="username_inputError" class="col alert alert-danger alert-dismissible fade show mt-2 py-2 pl-3 pr-5 text-left d-none">
        <small>
    <strong>Error!</strong> <span id="username_inputError_message"></span>
    <button type="button" aria-label="Close" class="close pt-1 pr-2" onclick="$('#username_inputError').removeClass('d-block').addClass('d-none');">×</button>
  </small>
      </div>

      <div class="form-group">
        <label class="form-control-label" for="email_required">Email</label>

        <input type="text" name="email" aria-describedby="email_required email_inputError" class="form-control is-valid" oninput="validate(this)" required/><br>

        <div id="email_required" class="pl-1 invalid-feedback">
          This field is required!
        </div>
      </div>


      <!-- if bad email format or already taken, print form input error -->
      <div id="email_inputError" class="col alert alert-danger alert-dismissible fade show mt-2 py-2 pl-3 pr-5 text-left d-none">
        <small>
    <strong>Error!</strong> <span id="email_inputError_message"></span>
    <button type="button" aria-label="Close" class="close pt-1 pr-2" onclick="$('#email_inputError').removeClass('d-block').addClass('d-none');">×</button>
  </small>
      </div>

      Comment: <input type="text" name="comment" class="form-control is-valid"><br>

      <input type="button" value="Submit" onclick="submitFormData('testfrm',['username','email'])" class="is-valid">
  </form>





</body>

</html>

【讨论】:

好吧,基本上,从阅读更多你的答案开始,我最终需要做的就是将表单的输入与无效反馈 div 一起封装在一个简单的 div 中, 等等,无论泡沫是否有“col”、“form-group”等类别。 【参考方案2】:

为了让其他人更清楚:

<div>
  Username: <input type="text" name="username" aria-describedby="username_required" class="form-control is-invalid" oninput="validate(this)" required/><br>
  <div id="username_required" class="pl-1 invalid-feedback">
    This field is required!
  </div>
</div>

<div>
  Email: <input type="text" name="email" aria-describedby="email_required" class="form-control is-valid" oninput="validate(this)" required/><br>
  <div id="email_required" class="pl-1 invalid-feedback">
    This field is required!
  </div>
</div>

似乎有效!只需要用无效反馈封装输入......

感谢您的帮助 AlwaysHelping!

【讨论】:

很高兴能帮到你。干杯。

以上是关于Bootstrap 4表单验证在使用无效反馈时不起作用,即使输入'is-valid'的主要内容,如果未能解决你的问题,请参考以下文章

Bootstrap 4 本机手动使输入字段无效

在 Bootstrap Modal 中使用 django 清晰表单的 AJAX 反馈表单

如何在 Django 中修改 Bootstrap 4 表单验证信息呈现方式?

Bootstrap 4 和使用图标的表单控制反馈

Bootstrap 4展开全部/全部折叠按钮在折叠元素已经显示时不起作用

Bootstrap 和 Select2 表单验证