Thiết kế xác thực số ký tự nhập liệu biểu mẫu - Triển khai giới hạn mà không ảnh hưởng UX

Khoảng 9 phút đọc

Xác thực số ký tự biểu mẫu là thách thức thiết kế cần cân bằng giữa duy trì tính toàn vẹn dữ liệu và bảo toàn trải nghiệm người dùng. Chỉ đặt thuộc tính maxlength là chưa đủ - cần xem xét bộ đếm thời gian thực, xử lý cặp thay thế, xác thực phía máy chủ và phản hồi phù hợp khi xảy ra lỗi. Bài viết này trình bày các mẫu thiết kế xác thực thực tế, bao gồm sự phù hợp với độ dài VARCHAR cơ sở dữ liệu. Hãy nắm vững khái niệm cơ bản về giới hạn ký tự trước khi đọc tiếp.

Cạm bẫy của thuộc tính maxlength

Thuộc tính maxlength của HTML là cách đơn giản nhất để giới hạn số ký tự. Tuy nhiên, nó có một số vấn đề mà nhà phát triển thường bỏ qua.

Vấn đề lớn nhất là maxlength giới hạn theo đơn vị mã UTF-16. Ký tự trong Mặt phẳng đa ngôn ngữ cơ bản (BMP) dùng 1 đơn vị mã, nhưng emoji và ký tự cặp thay thế tiêu thụ 2 đơn vị mã. Nghĩa là trong trường có maxlength="10", người dùng chỉ có thể nhập 5 emoji về mặt trực quan.

Loại ký tựĐơn vị mã UTF-16Tiêu thụ maxlengthVí dụ
Ký tự ASCII11A, 1, @
Tiếng Nhật (BMP)11あ, 漢, カ
Emoji cơ bản22😀, 🎉, ❤️
Emoji chuỗi ZWJ7-117-11👨‍👩‍👧‍👦, 🏳️‍🌈
Emoji cờ quốc gia44🇯🇵, 🇺🇸
CJK Thống nhất Hán tự Mở rộng B22𠮷 (tsuchiyoshi)

Vấn đề này đặc biệt nổi bật ở trường nhập tên. Một số chữ Hán đăng ký trong hộ tịch Nhật Bản thuộc CJK Thống nhất Hán tự Mở rộng B và tiêu thụ 2 đơn vị mã dưới maxlength. Họ "𠮷田" trông như 2 ký tự nhưng tiêu thụ 3 đơn vị mã. Điều này liên quan chặt chẽ đến vấn đề đếm ký tự toàn chiều rộng/nửa chiều rộng.

Vấn đề khác là maxlength từ chối nhập liệu một cách im lặng. Khi đạt giới hạn, không thể nhập thêm ký tự nhưng không có phản hồi giải thích lý do. Người dùng có thể nghi ngờ bàn phím hỏng hoặc lỗi trình duyệt.

Đếm ký tự chính xác bằng JavaScript

Để tránh vấn đề UTF-16 của maxlength, cần triển khai đếm theo cụm tự vị (Grapheme Cluster) trong JavaScript. Cụm tự vị là đơn vị mà con người nhận thức là "một ký tự", đếm chính xác cặp thay thế, ký tự kết hợp và chuỗi ZWJ là một ký tự đơn.

Phương pháp đáng tin cậy nhất là sử dụng API Intl.Segmenter.

// Đếm cụm tự vị bằng Intl.Segmenter
function countGraphemes(text) {
  const segmenter = new Intl.Segmenter('ja', { granularity: 'grapheme' });
  return [...segmenter.segment(text)].length;
}

// Ví dụ sử dụng
countGraphemes('Hello');        // 5
countGraphemes('こんにちは');    // 5
countGraphemes('👨‍👩‍👧‍👦');          // 1
countGraphemes('🇯🇵');           // 1
countGraphemes('𠮷田太郎');      // 4

Intl.Segmenter được hỗ trợ trên các trình duyệt chính tính đến 2024 (Chrome 87+, Firefox 125+, Safari 15.4+). Để hỗ trợ trình duyệt cũ, thư viện grapheme-splitter có thể dùng làm phương án dự phòng.

Tuy nhiên, không phải mọi biểu mẫu đều cần đếm cụm tự vị. Các trường chỉ chấp nhận ký tự ASCII như địa chỉ email hoặc URL hoạt động tốt với maxlength. Đếm cụm tự vị chỉ cần cho các trường người dùng nhập văn bản tự do (tên, bình luận, giới thiệu bản thân, v.v.).

Thiết kế bộ đếm ký tự thời gian thực

Bộ đếm ký tự thời gian thực là thành phần UI phản hồi trực quan số ký tự còn lại cho người dùng. Chỉ báo tiến trình hình tròn được sử dụng trong giới hạn ký tự của X (trước đây là Twitter) được công nhận rộng rãi là thực hành tốt nhất trong lĩnh vực này.

Yếu tố thiết kếMẫu khuyến nghịMẫu nên tránhLý do
Định dạng hiển thị"Còn 42 ký tự" hoặc "158/200"Chỉ "Đã nhập 158 ký tự"Số còn lại thúc đẩy hành động người dùng tốt hơn
Vị tríGóc dưới bên phải trường nhậpPhía trên trường hoặc xaGiảm thiểu di chuyển mắt
Thay đổi màuVàng khi còn 20%, đỏ khi hếtLuôn cùng màuTruyền tải mức độ khẩn cấp bằng thị giác
Hành vi vượt giới hạnTô đỏ phần vượt + hiển thị số âmChặn nhập im lặngCho người dùng không gian chỉnh sửa
Khả năng tiếp cậnThông báo số còn lại bằng aria-live="polite"Chỉ hiển thị trực quanCung cấp thông tin cho người dùng trình đọc màn hình

Thiết kế của X xuất sắc vì chỉ báo hình tròn đổi màu khi tiến gần giới hạn, và khi vượt quá, số âm hiển thị bằng màu đỏ. Thay vì chặn nhập, nó cho phép tiếp tục chỉnh sửa và vô hiệu hóa nút đăng, cho người dùng thời gian quyết định cắt bỏ phần nào.

Sự cần thiết của xác thực phía máy chủ

Xác thực phía máy khách tồn tại vì mục đích UX và không đảm bảo bảo mật hay tính toàn vẹn dữ liệu. Cả thuộc tính maxlength và bộ đếm JavaScript đều có thể dễ dàng vô hiệu hóa qua công cụ phát triển trình duyệt. Gọi API trực tiếp hoàn toàn bỏ qua xác thực frontend.

Xác thực số ký tự phía máy chủ, giống như bảo mật độ dài mật khẩu, là tuyến phòng thủ cuối cùng. Hãy đảm bảo triển khai các điểm sau:

# Ví dụ xác thực phía máy chủ bằng Python
import unicodedata

def validate_text_length(text, max_chars=200, max_bytes=800):
    # Loại bỏ ký tự điều khiển
    cleaned = ''.join(c for c in text if unicodedata.category(c) != 'Cc')
    # Chuẩn hóa NFC
    normalized = unicodedata.normalize('NFC', cleaned.strip())
    # Kiểm tra số ký tự
    char_count = len(normalized)
    # Kiểm tra số byte UTF-8
    byte_count = len(normalized.encode('utf-8'))

    if char_count > max_chars:
        return False, f'Số ký tự vượt giới hạn ({char_count}/{max_chars})'
    if byte_count > max_bytes:
        return False, f'Kích thước dữ liệu vượt giới hạn'
    return True, None

Mẫu triển khai theo framework

FrameworkThư viện xác thựcTriển khai giới hạn ký tựTích hợp bộ đếm thời gian thực
ReactReact Hook Form + ZodĐịnh nghĩa schema bằng .max() của ZodTheo dõi giá trị nhập bằng watch() và hiển thị đếm
VueVeeValidate + YupĐịnh nghĩa schema bằng .max() của YupĐếm từ giá trị phản ứng v-model
AngularReactive FormsValidators.maxLength()Đếm qua Observable valueChanges
SvelteSuperforms + ZodĐịnh nghĩa schema bằng .max() của ZodĐếm bằng khai báo phản ứng $:

Thiết kế thông báo lỗi cho giới hạn ký tự

MẫuVí dụ thông báoĐánh giá
Chỉ nêu vấn đề"Vượt giới hạn ký tự"Trung bình - không rõ vượt bao nhiêu
Vấn đề + trạng thái"523/500 ký tự - vượt 23 ký tự"Tốt - số vượt rõ ràng
Vấn đề + giải pháp"Vượt 23 ký tự. Vui lòng xóa phần không cần thiết"Tốt - hành động tiếp theo rõ ràng
Cảnh báo tiến triểnVàng khi còn 50, đỏ khi vượt + thông báoXuất sắc - cảnh báo trước và hướng dẫn cụ thể sau khi vượt

Phương pháp cảnh báo tiến triển hiệu quả nhất. Cung cấp phản hồi trực quan khi tiến gần giới hạn và trình bày số vượt cụ thể cùng giải pháp khi vượt quá.

Bạn cũng có thể tìm sách thiết kế UX trên Amazon để tham khảo thiết kế biểu mẫu.

Textarea tự động thay đổi kích thước và giới hạn ký tự

Textarea tự động thay đổi kích thước mở rộng chiều cao tự động theo nội dung nhập. Khi kết hợp với giới hạn ký tự, cần một số quyết định thiết kế. Không đặt chiều cao tối đa có thể phá vỡ bố cục trang. Với trường giới hạn 500 ký tự, đặt tối đa khoảng 400px là thực tế. CSS field-sizing: content được triển khai trong Chrome 123 năm 2024 cho phép tự động thay đổi kích thước không cần JavaScript, nhưng Firefox và Safari chưa hỗ trợ.

Giới hạn ký tự biểu mẫu di động - Thách thức riêng

// Kiểm soát xác thực trong quá trình soạn IME
let isComposing = false;

textarea.addEventListener('compositionstart', () => {
  isComposing = true;
});

textarea.addEventListener('compositionend', () => {
  isComposing = false;
  validateLength(textarea.value);
});

textarea.addEventListener('input', () => {
  if (!isComposing) {
    validateLength(textarea.value);
  }
  updateCounter(textarea.value);
});

Thiết kế phù hợp với cơ sở dữ liệu

Sự không phù hợp giữa giới hạn ký tự frontend và định nghĩa cột cơ sở dữ liệu gây ra cắt ngắn dữ liệu hoặc lỗi trong môi trường sản xuất. Như trình bày chi tiết trong thiết kế độ dài VARCHAR cơ sở dữ liệu, VARCHAR(255) của MySQL dựa trên ký tự, nhưng tiêu thụ lưu trữ thực tế phụ thuộc vào mã hóa.

Cơ sở dữ liệuĐơn vị VARCHAR100 ký tự tiếng NhậtPhù hợp giới hạn frontend
MySQL (utf8mb4)Ký tựVừa VARCHAR(100)Dễ khớp với giới hạn ký tự frontend
PostgreSQLKý tựVừa VARCHAR(100)Dễ khớp với giới hạn ký tự frontend
SQL ServerKý tự (NVARCHAR)Vừa NVARCHAR(100)Dễ khớp với giới hạn ký tự frontend
OracleByte (mặc định)Cần VARCHAR2(300)Cần chuyển đổi byte
DynamoDBKích thước mục (400 KB)Không giới hạn theo thuộc tínhĐặt giới hạn ở tầng ứng dụng

Thực hành thiết kế an toàn là đặt giới hạn ký tự frontend nghiêm ngặt hơn định nghĩa cột cơ sở dữ liệu. Ví dụ, nếu cơ sở dữ liệu là VARCHAR(500), đặt giới hạn frontend khoảng 450 ký tự, tạo bộ đệm cho thay đổi số ký tự từ chuẩn hóa Unicode và cắt bỏ.

Chia sẻ bài viết này