You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在HTML/CSS/JS实现的虚拟键盘中切换符号与字母布局

实现虚拟键盘的符号布局切换功能

Hey there! Let's fix up your virtual keyboard to switch between letter and symbol layouts exactly how you want. Here's a step-by-step solution with modified code and clear explanations:

Modified JavaScript Code

const Keyboard = {
  elements: { main: null, keysContainer: null, keys: [] },
  eventHandlers: { oninput: null, onclose: null },
  properties: { 
    value: "", 
    capsLock: false,
    currentLayout: "letters" // 新增:跟踪当前布局,默认字母模式
  },
  init() {
    // Create main elements
    this.elements.main = document.createElement("div");
    this.elements.keysContainer = document.createElement("div");

    // Setup main elements
    this.elements.main.classList.add("keyboard", "keyboard--hidden");
    this.elements.keysContainer.classList.add("keyboard__keys");
    // 初始化时生成字母布局按键
    this.elements.keysContainer.appendChild(this._createKeys());
    this.elements.keys = this.elements.keysContainer.querySelectorAll(".keyboard__key");

    // Add to DOM
    this.elements.main.appendChild(this.elements.keysContainer);
    document.body.appendChild(this.elements.main);

    // Automatically use keyboard for elements with .use-keyboard-input
    document.querySelectorAll(".use-keyboard-input").forEach(element => {
      element.addEventListener("focus", () => {
        this.open(element.value, currentValue => {
          element.value = currentValue;
        });
      });
    });
  },
  _createKeys() {
    const fragment = document.createDocumentFragment();
    // 定义字母和符号两套布局
    const layouts = {
      letters: [
        "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "backspace",
        "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "caps",
        "a", "s", "d", "f", "g", "h", "j", "k", "l", "enter", "toggle", // 替换done为布局切换键
        "z", "x", "c", "v", "b", "n", "m", ",", ".", "?", "space"
      ],
      symbols: [
        "!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "backspace",
        "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "caps",
        "a", "s", "d", "f", "g", "h", "j", "k", "l", "enter", "toggle", // 替换done为布局切换键
        "z", "x", "c", "v", "b", "n", "m", ",", ".", "?", "space"
      ]
    };
    // 根据当前布局获取对应键数组
    const keyLayout = layouts[this.properties.currentLayout];

    // Creates HTML for an icon
    const createIconHTML = (icon_name) => {
      return `<i class="material-icons">${icon_name}</i>`;
    };

    keyLayout.forEach(key => {
      const keyElement = document.createElement("button");
      const insertLineBreak = ["backspace", "p", "enter", "?"].indexOf(key) !== -1;

      // Add attributes/classes
      keyElement.setAttribute("type", "button");
      keyElement.classList.add("keyboard__key");

      switch (key) {
        case "backspace":
          keyElement.classList.add("keyboard__key--wide");
          keyElement.innerHTML = createIconHTML("backspace");
          keyElement.addEventListener("click", () => {
            this.properties.value = this.properties.value.substring(0, this.properties.value.length - 1);
            this._triggerEvent("oninput");
          });
          break;
        case "caps":
          keyElement.classList.add("keyboard__key--wide", "keyboard__key--activatable");
          keyElement.innerHTML = createIconHTML("keyboard_capslock");
          keyElement.addEventListener("click", () => {
            this._toggleCapsLock();
            keyElement.classList.toggle("keyboard__key--active", this.properties.capsLock);
          });
          break;
        case "enter":
          keyElement.classList.add("keyboard__key--wide");
          keyElement.innerHTML = createIconHTML("keyboard_return");
          keyElement.addEventListener("click", () => {
            this.properties.value += "\n";
            this._triggerEvent("oninput");
          });
          break;
        case "space":
          keyElement.classList.add("keyboard__key--extra-wide");
          keyElement.innerHTML = createIconHTML("space_bar");
          keyElement.addEventListener("click", () => {
            this.properties.value += " ";
            this._triggerEvent("oninput");
          });
          break;
        case "toggle": // 布局切换键逻辑
          keyElement.classList.add("keyboard__key--wide", "keyboard__key--dark");
          // 根据当前布局显示对应图标:字母模式显示符号图标,符号模式显示键盘图标
          const toggleIcon = this.properties.currentLayout === "letters" ? "symbols" : "keyboard";
          keyElement.innerHTML = createIconHTML(toggleIcon);
          keyElement.addEventListener("click", () => {
            this._toggleLayout(); // 调用布局切换方法
          });
          break;
        default:
          keyElement.textContent = key.toLowerCase();
          keyElement.addEventListener("click", () => {
            this.properties.value += this.properties.capsLock ? key.toUpperCase() : key.toLowerCase();
            this._triggerEvent("oninput");
            // 如果当前是符号布局,点击符号后自动切回字母布局
            if (this.properties.currentLayout === "symbols") {
              this._toggleLayout();
            }
          });
          break;
      }

      fragment.appendChild(keyElement);

      if (insertLineBreak) {
        fragment.appendChild(document.createElement("br"));
      }
    });

    return fragment;
  },
  _toggleLayout() {
    // 切换布局类型
    this.properties.currentLayout = this.properties.currentLayout === "letters" ? "symbols" : "letters";
    // 清空当前按键容器,重新生成对应布局的按键
    this.elements.keysContainer.innerHTML = "";
    this.elements.keysContainer.appendChild(this._createKeys());
    this.elements.keys = this.elements.keysContainer.querySelectorAll(".keyboard__key");
    // 保持大写锁定状态(如果之前开启)
    if (this.properties.capsLock) {
      this._toggleCapsLock();
      this._toggleCapsLock();
    }
  },
  _triggerEvent(handlerName) {
    if (typeof this.eventHandlers[handlerName] == "function") {
      this.eventHandlers[handlerName](this.properties.value);
    }
  },
  _toggleCapsLock() {
    this.properties.capsLock = !this.properties.capsLock;
    for (const key of this.elements.keys) {
      if (key.childElementCount === 0) {
        key.textContent = this.properties.capsLock ? key.textContent.toUpperCase() : key.textContent.toLowerCase();
      }
    }
  },
  open(initialValue, oninput, onclose) {
    this.properties.value = initialValue || "";
    this.eventHandlers.oninput = oninput;
    this.eventHandlers.onclose = onclose;
    this.elements.main.classList.remove("keyboard--hidden");
  },
  close() {
    this.properties.value = "";
    this.eventHandlers.oninput = null; // 修复原代码未定义变量的bug
    this.eventHandlers.onclose = null;
    this.elements.main.classList.add("keyboard--hidden");
  }
};

window.addEventListener("DOMContentLoaded", function() {
  Keyboard.init();
});

Key Changes Explained

  1. Layout Tracking: Added a currentLayout property to keep track of whether we're in letter or symbol mode.
  2. Dual Layout Definitions: Created a layouts object to store both key sets, making it easy to switch between them.
  3. Toggle Key Replacement: Replaced the "done" key with a layout toggle button that shows context-aware icons (switches between "symbols" and "keyboard" icons).
  4. Auto-switch Back: When a symbol key is pressed, the keyboard automatically switches back to the letter layout after inserting the symbol.
  5. Layout Refresh: The _toggleLayout method clears existing keys, regenerates the new layout, and preserves caps lock state if it was active.
  6. Bug Fix: Corrected the close method to properly reset event handlers.

Your CSS and HTML code don't need any adjustments—just swap in this modified JavaScript, and your keyboard will work exactly as you requested!

内容的提问来源于stack exchange,提问作者johnrambot

火山引擎 最新活动