boxmoe_header_banner_img

Hello! 欢迎来到悠悠畅享网!

文章导读

整合Luhn算法实现信用卡号表单验证


avatar
作者 2025年8月30日 9

整合Luhn算法实现信用卡号表单验证

本文旨在指导开发者如何在基于html5和JavaScript的表单验证系统中,集成Luhn算法对信用卡号进行实时验证。文章将详细阐述如何正确获取输入字段、应用Luhn算法逻辑,并通过setCustomValidity API管理自定义验证状态及错误提示,确保表单提交前所有字段的有效性,提升用户体验。

1. 理解表单验证与Luhn算法集成挑战

现代web表单通常利用html5内置的验证机制(如required、type=”email”等)来提供基础的用户输入校验。然而,对于某些特定业务逻辑的验证,例如信用卡号的luhn算法校验,需要通过javascript进行自定义实现。核心挑战在于如何将这种自定义验证逻辑无缝地融入到现有的html5验证流程中,并确保当信用卡号不符合luhn算法时,能够像其他无效字段一样,正确地显示错误提示并阻止表单提交。

Luhn算法(也称为模10算法)是一种简单的校验和算法,常用于验证各种识别号码,如信用卡号、IMEI号等。它通过特定的计算规则,判断一串数字是否可能是有效号码,但它并不能验证号码的真实性或是否存在。

2. Luhn算法的JavaScript实现

首先,我们需要一个独立的JavaScript函数来实现Luhn算法。这个函数接收一个字符串作为输入(通常是信用卡号),并返回一个布尔值,表示该号码是否通过Luhn校验。

/**  * 校验给定的字符串是否符合Luhn算法规则  * @param {string} value - 待校验的字符串(通常为信用卡号)  * @returns {boolean} - 如果符合Luhn算法返回true,否则返回false  */ function checkLuhn(value) {   // 1. 移除所有非数字、非破折号、非空格字符。如果存在其他非数字字符,则直接视为无效。   if (/[^0-9-s]+/.test(value)) return false;    // 2. 移除所有非数字字符,只保留数字进行Luhn算法计算   value = value.replace(/D/g, "");    let nCheck = 0; // 校验和   let bEven = false; // 标记是否为偶数位(从右往左数,第一位为奇数位)    // 3. 从右向左遍历数字   for (let n = value.length - 1; n >= 0; n--) {     let cDigit = value.charAt(n);     let nDigit = parseInt(cDigit, 10);      // 4. 偶数位数字翻倍,如果大于9则减去9     if (bEven) {       if ((nDigit *= 2) > 9) {         nDigit -= 9;       }     }      // 5. 累加到校验和     nCheck += nDigit;     bEven = !bEven; // 切换奇偶位标记   }    // 6. 如果校验和能被10整除,则通过Luhn算法   return (nCheck % 10) === 0; }

Luhn算法原理简述:

  1. 从号码最右边一位开始,将其视为第一位(奇数位)。
  2. 从右向左,每隔一位的数字乘以2(即所有偶数位的数字)。
  3. 如果乘以2的结果是两位数(即大于9),则将这个两位数的两个数字相加(例如,12变成1+2=3)。
  4. 将所有(未翻倍的奇数位和经过处理的偶数位)数字相加,得到一个总和。
  5. 如果这个总和能被10整除,则该号码通过Luhn校验。

3. 集成Luhn算法到表单验证流程

为了将Luhn算法集成到现有表单的html5验证流程中,我们需要监听信用卡输入字段的input事件,并在每次输入时执行Luhn校验。关键在于使用HTMLElement.setCustomValidity()方法来控制字段的验证状态和自定义错误消息。

3.1 JavaScript核心集成逻辑

在表单初始化脚本中,添加对信用卡输入字段的事件监听:

(function () {   "use strict";    // ... 现有表单验证逻辑 ...    // 监听信用卡输入字段的输入事件   // 注意:这里使用了jquery的on('input')方法   $('#cc').on('input', function() {     // 1. 获取信用卡输入字段的dom元素     // 确保这里的选择器能够准确地获取到你的input元素。     // 在本例中,HTML结构是 <div id="cardContainer"><input id="cc" ...></div>     // 所以可以使用 document.querySelector('#cardContainer input') 或更直接的 document.getElementById('cc')     const field = document.getElementById('cc'); // 或者 document.querySelector('#cardContainer input');      // 2. 执行Luhn算法校验     if (checkLuhn($('#cc').val())) {       // 如果校验通过,清除自定义验证消息,使字段变为有效状态       field.setCustomValidity("");     } else {       // 如果校验失败,设置自定义验证消息,使字段变为无效状态       field.setCustomValidity("Invalid field."); // 这条消息将显示在浏览器默认的错误提示中     }   });    // ... 现有Luhn算法函数 checkLuhn ...  })();

关键点解释:

  • field变量的正确获取:原代码中field变量未定义,导致setCustomValidity无法调用。必须先通过document.getElementById(‘cc’)或document.querySelector(‘#cardContainer input’)等方式获取到实际的DOM元素。
  • setCustomValidity(“”):当字段通过自定义校验时,必须调用setCustomValidity(“”)并传入空字符串,以清除任何先前设置的自定义错误,并使该字段被浏览器视为有效。如果传入非空字符串,字段将被视为无效,并显示该字符串作为错误消息。
  • input事件:选择input事件而不是change事件,可以实现实时反馈,提升用户体验。

3.2 HTML结构调整

为了确保setCustomValidity设置的错误消息能够被正确地显示,并且与现有的css样式协同工作,我们需要确保HTML结构中包含用于显示错误消息的元素,并且CSS样式能够响应字段的验证状态。

在信用卡输入字段的HTML部分,确保包含id属性,并且有empty-feedback和invalid-feedback的div:

<div class="flex mb-6 space-x-4">   <div id="cardContainer" class="w-full md:w-1/2">     <label for="cc" class="block mb-2 text-sm text-gray-600 dark:text-gray-400">Credit Card Number</label>     <input type="text" id="cc" placeholder="XXXX XXXX XXXX XXXX" required            class="w-full px-3 py-2 placeholder-gray-300 border-2 border-gray-200 rounded-md focus:outline-none focus:ring focus:ring-indigo-100 focus:border-indigo-300" />      <div class="empty-feedback text-red-400 text-sm mt-1">       Please provide your credit card number.     </div>     <div class="invalid-feedback text-red-400 text-sm mt-1">       Please provide a valid credit card number.     </div>   </div> </div>

这里为信用卡输入字段的父容器添加了id=”cardContainer”,虽然在document.getElementById(‘cc’)的情况下不是必需的,但如果你的选择器是document.querySelector(‘#cardContainer input’),则该ID是必要的。

3.3 CSS样式

确保以下CSS样式存在,它们负责根据表单的验证状态显示或隐藏错误消息:

.invalid-feedback, .empty-feedback {   display: none; /* 默认隐藏错误消息 */ }  /* 当表单被标记为was-validated且输入框为空时,显示empty-feedback */ .was-validated :placeholder-shown:invalid ~ .empty-feedback {   display: block; }  /* 当表单被标记为was-validated且输入框不为空但无效时,显示invalid-feedback */ .was-validated :not(:placeholder-shown):invalid ~ .invalid-feedback {   display: block; }  /* 无效字段的边框样式 */ .is-invalid, .was-validated :invalid {   border-color: #dc3545; /* 红色边框 */ }

这些CSS规则利用了:invalid伪类和通用兄弟选择器(~)来根据字段的验证状态和是否为空来显示不同的错误提示。当setCustomValidity设置了错误消息时,浏览器会将该字段标记为:invalid。

4. 完整代码示例

以下是整合了Luhn算法验证的完整HTML、CSS和JavaScript代码:

HTML (index.html)

<!DOCTYPE html> <html lang="zh-CN"> <head>   <meta charset="UTF-8">   <meta name="viewport" content="width=device-width, initial-scale=1.0">   <title>信用卡号Luhn算法表单验证</title>   <link href="https://cdnJS.cloudflare.com/ajax/libs/tailwindcss/2.0.4/tailwind.min.css" rel="stylesheet"/>   <style>     /* 自定义验证反馈样式 */     .invalid-feedback,     .empty-feedback {       display: none;     }      .was-validated :placeholder-shown:invalid ~ .empty-feedback {       display: block;     }      .was-validated :not(:placeholder-shown):invalid ~ .invalid-feedback {       display: block;     }      .is-invalid,     .was-validated :invalid {       border-color: #dc3545;     }   </style> </head> <body class="flex items-center min-h-screen bg-gray-100 dark:bg-gray-900">   <div class="container mx-auto">     <div class="max-w-xl mx-auto my-10 bg-white p-5 rounded-md shadow-sm">       <div class="text-center">         <h1 class="my-3 text-3xl font-semibold text-gray-700 dark:text-gray-200">           联系我们         </h1>         <p class="text-gray-400 dark:text-gray-400">           填写以下表格发送消息。         </p>       </div>       <div class="m-7">         <form action="https://api.web3forms.com/submit" method="POST" id="form" class="needs-validation" novalidate>           <input type="hidden" name="access_key" value="YOUR_ACCESS_KEY_HERE" />           <input type="hidden" name="subject" value="New Submission from Web3Forms" />           <input type="checkbox" name="botcheck" id="" style="display: none;" />            <div class="flex mb-6 space-x-4">             <div class="w-full md:w-1/2">               <label for="first_name" class="block mb-2 text-sm text-gray-600 dark:text-gray-400">First Name</label>               <input type="text" name="name" id="first_name" placeholder="John" required                      class="w-full px-3 py-2 placeholder-gray-300 border-2 border-gray-200 rounded-md focus:outline-none focus:ring focus:ring-indigo-100 focus:border-indigo-300" />               <div class="empty-feedback invalid-feedback text-red-400 text-sm mt-1">                 Please provide your first name.               </div>             </div>           </div>            <div class="flex mb-6 space-x-4">             <div class="w-full md:w-1/2">               <label for="email" class="block mb-2 text-sm text-gray-600 dark:text-gray-400">Email Address</label>               <input type="email" name="email" id="email" placeholder="email@example.com" required                      class="w-full px-3 py-2 placeholder-gray-300 border-2 border-gray-200 rounded-md focus:outline-none focus:ring focus:ring-indigo-100 focus:border-indigo-300" />               <div class="empty-feedback text-red-400 text-sm mt-1">                 Please provide your email address.               </div>               <div class="invalid-feedback text-red-400 text-sm mt-1">                 Please provide a valid email address.               </div>             </div>           </div>            <div class="flex mb-6 space-x-4">             <div id="cardContainer" class="w-full md:w-1/2">               <label for="cc" class="block mb-2 text-sm text-gray-600 dark:text-gray-400">Credit Card Number</label>               <input type="text" id="cc" placeholder="XXXX XXXX XXXX XXXX" required                      class="w-full px-3 py-2 placeholder-gray-300 border-2 border-gray-200 rounded-md focus:outline-none focus:ring focus:ring-indigo-100 focus:border-indigo-300" />               <div class="empty-feedback text-red-400 text-sm mt-1">                 Please provide your credit card number.               </div>               <div class="invalid-feedback text-red-400 text-sm mt-1">                 Please provide a valid credit card number.               </div>             </div>           </div>            <div class="mb-6">             <button type="submit" class="w-full px-3 py-4 text-white bg-indigo-500 rounded-md focus:bg-indigo-600 focus:outline-none">               发送消息             </button>           </div>           <p class="text-base text-center text-gray-400" id="result"></p>         </form>       </div>     </div>   </div>   <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>   <script src="main.js"></script> <!-- 引用你的JavaScript文件 --> </body> </html>

JavaScript (main.js)

(function () {   "use strict";    /*    * 表单验证初始化    */   const forms = document.querySelectorAll(".needs-validation");   const result = document.getElementById("result");    Array.prototype.slice.call(forms).forEach(function (form) {     form.addEventListener(       "submit",       function (event) {         // 在提交前再次检查所有字段的有效性         if (!form.checkValidity()) {           event.preventDefault();           event.stopPropagation();           // 将焦点设置到第一个无效字段           form.querySelectorAll(":invalid")[0].focus();         } else {           /*            * 使用fetch()进行表单提交            */           const formData = new FormData(form);           event.preventDefault();           event.stopPropagation();           const object = {};           formData.forEach((value, key) => {             object[key] = value;           });           const json = JSON.stringify(object);           result.innerHTML = "请稍候...";            fetch("https://api.web3forms.com/submit", {             method: "POST",             headers: {               "Content-Type": "application/json",               Accept: "application/json"             },             body: json           })             .then(async (response) => {               let jsonResponse = await response.json();               if (response.status === 200) {                 result.innerHTML = jsonResponse.message;                 result.classList.remove("text-gray-500");                 result.classList.add("text-green-500");               } else {                 console.error(response);                 result.innerHTML = jsonResponse.message;                 result.classList.remove("text-gray-500");                 result.classList.add("text-red-500");               }             })             .catch((error) => {               console.error(error);               result.innerHTML = "发生了一些错误!";             })             .then(function () {               form.reset();               form.classList.remove("was-validated");               setTimeout(() => {                 result.style.display = "none";               }, 5000);             });         }         form.classList.add("was-validated"); // 标记表单为已验证,触发CSS显示错误       },       false     );   });    // 信用卡输入字段的Luhn算法验证   $('#cc').on('input', function(){     // 获取信用卡输入字段的DOM元素     const field = document.getElementById('cc'); // 或者 document.querySelector('#cardContainer input');     const inputValue = $(this).val(); // 获取当前输入值      if (checkLuhn(inputValue)) {       field.setCustomValidity(""); // 校验通过,清除自定义错误     } else {       field.setCustomValidity("Invalid field."); // 校验失败,设置自定义错误     }   });    /**    * Luhn算法校验函数    * @param {string} value - 待校验的字符串    * @returns {boolean} - 如果符合Luhn算法返回true,否则返回false    */   function checkLuhn(value) {     // 接受只包含数字、破折号或空格的字符串     if (/[^0-9-s]+/.test(value)) return false;      // 移除所有非数字字符     value = value.replace(/D/g, "");      let nCheck = 0, bEven = false;     for (let n = value.length - 1; n >= 0; n--) {       let cDigit = value.charAt(n);       let nDigit = parseInt(cDigit, 10);        if (bEven) {         if ((nDigit *= 2) > 9) nDigit -= 9;       }       nCheck += nDigit;       bEven = !bEven;     }     return (nCheck % 10) === 0;   } })();

5. 注意事项与最佳实践

  • 客户端验证与服务器端验证:Luhn算法在客户端的实现主要是为了提供即时用户反馈和提升用户体验。安全性要求高的场景下,服务器端必须对所有提交的数据进行再次验证,因为客户端验证可以被绕过。
  • 错误消息的清晰性与多语言支持:setCustomValidity()中的错误消息应该清晰明了,指导用户如何修正。对于国际化应用,这些消息应支持多语言。
  • 用户体验:实时验证(oninput事件)可以显著改善用户体验,让用户在输入过程中就能得到反馈,而不是等到提交时才发现错误。
  • 与其他验证的协同:当一个字段有多个验证规则时(例如,既required又需要Luhn校验),setCustomValidity会覆盖或与浏览器默认的错误消息协同。如果setCustomValidity设置了非空字符串,它将优先显示。
  • CSS样式匹配:确保你的自定义错误消息样式(如.invalid-feedback)能够正确响应setCustomValidity触发的:invalid状态。

总结

通过将Luhn算法封装为独立的JavaScript函数,并结合HTMLElement.setCustomValidity() API,我们可以有效地将自定义验证逻辑



评论(已关闭)

评论已关闭

text=ZqhQzanResources