JS 原生实现车牌号输入键盘 适用与H5+ App

820 阅读3分钟

前言:最近接手了一个10年的老前端项目,使用D-cloud MUI 开发的H5+ App(纯js+html+css),讲真,除了N年前自学前端的时候写过几个demo,后来在没码过原生的代码了

上需求:

一个车牌号输入功能,前身是 input实现的没有任何限制,现场(给物流仓库使用的)用起来随意输入,容易输错,要求改进,那来吧一句话的需求,开发跑断腿(没有交互,没有UI,都是前端的活)

先给大家看下实现效果

image.png

代码部分

HTML
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <link rel="stylesheet" href="./index.css" />
    <script src="./index.js" defer></script>
  </head>
  <body>
    <div class="license-plate-keyboard">
      <div class="license-input-container">
        <!-- 创建七个输入框 -->
        <div class="license-input" data-index="0" id="char1"></div>
        <div class="license-input" data-index="1" id="char2"></div>
        <div class="license-input" data-index="2" id="char3"></div>
        <div class="license-input" data-index="3" id="char4"></div>
        <div class="license-input" data-index="4" id="char5"></div>
        <div class="license-input" data-index="5" id="char6"></div>
        <div class="license-input" data-index="6" id="char7"></div>
        <div
          class="license-input default-content last-input"
          data-index="7"
          id="char8"
        ></div>
      </div>
      <div class="keyboard hidden" id="customKeyboard">
        <div id="provinces" class="keyboard-grid">
          <!-- 省份 -->
        </div>

        <div id="letters" class="keyboard-grid">
          <!-- 字母 -->
        </div>
        <div id="digits" class="keyboard-grid-num keyboard-grid">
          <!-- 数字 -->
        </div>

        <div class="keyboard-controls">
          <button id="toggle-language">切换中文/英文</button>
          <button id="delete-btn">删除</button>
        </div>
      </div>
    </div>
  </body>
</html>

CSS
.license-plate-keyboard {
  width: 100%;
  /* max-width: 400px; */
  margin: 20px auto;
  color: #000;
}

.license-input-container {
  display: flex;
  justify-content: space-between;
  /* margin-bottom: 15px; */
}

.license-input {
  width: 32px !important;
  height: 32px !important;
  line-height: 32px;
  text-align: center;
  font-size: 18px;
  border: 1px solid #ccc;
  border-radius: 5px;
  background-color: #f8f8f8;
}
.license-input:first-child {
  width: 32px;
  height: 32px;
  text-align: center;
  font-size: 18px;
  /* color: #FFF; */
  border: 1px solid #ccc;
  border-radius: 5px;
  background-color: #1890ff;
}

.default-content::before {
  content: "新"; /* 提示的默认内容 */
  color: #79e951; /* 提示文字颜色 */
}

.keyboard {
  display: flex;
  flex-direction: column;
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  background-color: #fff;
  border-top: 1px solid #ccc;
  padding: 32px 12px 12px;
  z-index: 99;
}

.hidden {
  display: none;
}

.keyboard-grid {
  display: grid;
  grid-template-columns: repeat(8, 1fr);
  gap: 10px;
}
.keyboard-grid-num {
  display: grid;
  grid-template-columns: repeat(10, 1fr);
  gap: 10px;
  margin-bottom: 10px;
}

.keyboard-grid button {
  width: 100%;
  height: 40px;
  font-size: 14px;
  text-align: center;
  background-color: #f0f0f0;
  border: 1px solid #ddd;
  border-radius: 5px;
  cursor: pointer;
}

.keyboard-grid button:hover {
  background-color: #e0e0e0;
}

.keyboard-controls {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 10px;
  margin-top: 10px;
}

.keyboard-controls button {
  flex: 1;
  padding: 10px;
  font-size: 16px;
  background-color: #1890ff;
  color: white;
  border: none;
  border-radius: 5px;
  cursor: pointer;
}

.keyboard-controls button:hover {
  background-color: #40a9ff;
}

Js
const provinces = ["京", "津", "渝", "沪", "冀", "晋", "辽", "吉", "黑", "苏", "浙", "皖", "闽", "赣", "鲁", "豫", "鄂", "湘", "粤", "琼", "川", "贵", "云", "陕", "甘", "青", "蒙", "桂", "宁", "新", "藏", "使", "领", "警", "学", "港", "澳"];
const letters = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0, "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"];

let currentLanguage = 'zh'; // 当前语言模式 'zh'为中文,'en'为英文
let lastFocusedInput = null; // 记录最后聚焦的输入框
const inputBoxes = document.querySelectorAll(".license-input"); // 所有输入框
const keyboard = document.getElementById("customKeyboard"); // 键盘容器

// 根据输入框索引切换语言
function toggleLanguageBasedOnInput(index) {
    const newLanguage = index === 0 ? 'zh' : 'en';
    if (currentLanguage !== newLanguage) {
        currentLanguage = newLanguage;
        updateKeyboard();
    }
}

// 显示键盘
function showKeyboard() {
    if (keyboard.classList.contains("hidden")) {
        keyboard.classList.remove("hidden");
        console.log("Keyboard shown");
    }
}

// 隐藏键盘
function hideKeyboard() {
    if (!keyboard.classList.contains("hidden")) {
        keyboard.classList.add("hidden");
    }
}

// 初始化事件监听
function initializeInputEvents() {
    inputBoxes.forEach((box, index) => {
        // 设置点击时显示键盘并切换语言
        box.addEventListener("click", (event) => {
            lastFocusedInput = box;
            toggleLanguageBasedOnInput(index); // 根据输入框索引切换语言
            showKeyboard(); // 显示键盘
            event.stopPropagation(); // 防止触发文档点击事件
        });

        // 保存最后聚焦的输入框
        box.addEventListener("focus", (event) => {
            lastFocusedInput = event.target; // 记录当前聚焦的输入框
        });
    });

    // 点击文档其他区域隐藏键盘
    document.addEventListener("click", hideKeyboard);

    // 防止点击键盘本身隐藏
    keyboard.addEventListener("click", (event) => event.stopPropagation());
}




// 更新键盘内容
function updateKeyboard() {
    // 获取id为provinces的元素
    const provinceRow = document.getElementById('provinces');
    // 获取id为letters的元素
    const letterRow = document.getElementById('letters');

    provinceRow.innerHTML = '';
    letterRow.innerHTML = '';

    const keys = currentLanguage === 'zh' ? provinces : letters;

    keys.forEach((key) => {
        const button = createKeyboardButton(key); // // 创建键盘按钮
        currentLanguage === 'zh' ? provinceRow.appendChild(button) : letterRow.appendChild(button);
    });
}

// 创建键盘按钮
function createKeyboardButton(content) {
    const button = document.createElement('button');
    button.textContent = content;
    button.addEventListener('click', () => appendLicensePlate(content));
    return button;
}

// 填充字符到输入框
function appendLicensePlate(char) {
    if (!lastFocusedInput) return;

    if (lastFocusedInput.classList.contains('default-content')) {
        lastFocusedInput.classList.remove('default-content');
    }

    lastFocusedInput.textContent = char;

    // 自动切换为英文键盘后移动到下一个输入框
    if (currentLanguage === 'zh') {
        currentLanguage = 'en';
        updateKeyboard();
    }

    const nextInput = lastFocusedInput.nextElementSibling;
    if (nextInput && nextInput.classList.contains('license-input')) {
        lastFocusedInput = nextInput;
    }
}

// 删除最后一个字符
function deleteLicensePlate() {
    if (!lastFocusedInput) return;

    if (!lastFocusedInput.classList.contains('default-content') && lastFocusedInput.classList.contains('last-input')) {
        lastFocusedInput.classList.add('default-content');
    }

    // 当前输入框为空时,聚焦到前一个输入框
    if (!lastFocusedInput.textContent && lastFocusedInput.previousElementSibling?.classList.contains('license-input')) {
        lastFocusedInput = lastFocusedInput.previousElementSibling;
    }

    // 清除当前焦点输入框的值
    lastFocusedInput.textContent = '';

}

// 获取车牌号
function getLicensePlate() {
    return Array.from(inputBoxes).map((input) => input.textContent.trim()).join('');
}

// 删除按钮事件
document.getElementById('delete-btn').addEventListener('click', deleteLicensePlate);

// 切换语言按钮事件
document.getElementById('toggle-language').addEventListener('click', () => {
    currentLanguage = currentLanguage === 'zh' ? 'en' : 'zh';
    updateKeyboard();
});

// 初始化键盘和输入框
document.addEventListener("DOMContentLoaded", () => {
    initializeInputEvents();
    updateKeyboard();
});