Mở đầu — vì sao bài này quan trọng
Hãy tưởng tượng bạn vừa dành mười lăm phút điền một biểu mẫu đăng ký mở thẻ tín dụng: họ tên, số CMND/CCCD, địa chỉ, thu nhập, người tham chiếu. Bạn nhấn nút "Gửi hồ sơ", và màn hình đỏ lòm hiện lên dòng chữ "Đã có lỗi xảy ra. Vui lòng kiểm tra lại thông tin." Nhưng lỗi ở đâu? Trường nào sai? Bạn phải tự mò từng ô một, cuộn lên cuộn xuống, và cảm giác bực bội dâng lên. Rất nhiều người ở khoảnh khắc đó sẽ đóng tab và bỏ đi.
Đây chính là lý do bài học này quan trọng. Trong toàn bộ trải nghiệm của một biểu mẫu, khoảnh khắc "báo lỗi" là khoảnh khắc mong manh nhất. Người dùng đang ở trạng thái đã bỏ công sức, đang kỳ vọng được tiếp tục, thì bị chặn lại. Nếu bạn xử lý tốt, họ sửa nhanh và đi tiếp. Nếu bạn xử lý dở, bạn mất họ — và với các form quan trọng như thanh toán, đăng ký, mở tài khoản, mỗi lần mất người dùng là mất tiền thật.
Ở các bài trước trong khóa, chúng ta đã nói về error messages nói chung (Bài 10) và về form copy như label, placeholder, helper text (Bài 15). Bài 29 này đi sâu riêng vào một câu hỏi hẹp nhưng cực kỳ quyết định: khi nào và bằng cách nào chúng ta báo lỗi trên một biểu mẫu? Timing của validation, vị trí đặt thông báo, thứ tự ưu tiên khi có nhiều lỗi, và các pattern cụ thể để người dùng sửa lỗi mà không thấy bị "phạt". Đây là phần kỹ thuật-thiết-kế mà một UX Writer giỏi phải nắm chắc để làm việc cùng developer và designer.
Khái niệm cốt lõi
Ba thời điểm validate: on submit, on blur, on change
Câu hỏi đầu tiên và quan trọng nhất không phải là "viết gì" mà là "báo lỗi lúc nào". Có ba chiến lược chính, mỗi cái có ưu và nhược điểm rõ ràng.
On submit (khi nhấn nút gửi) là cách cổ điển và đơn giản nhất. Người dùng điền toàn bộ form, nhấn gửi, hệ thống mới kiểm tra và báo tất cả lỗi cùng lúc. Ưu điểm: không làm phiền người dùng trong lúc họ đang gõ, dễ triển khai, phù hợp với các form ngắn 2-3 trường. Nhược điểm: nếu form dài, người dùng phải điền hết rồi mới biết mình sai từ trường thứ hai — rất tốn công quay lại sửa. Với form nhiều bước, đây có thể là trải nghiệm tệ.
On blur (khi rời khỏi trường — focus out) là cách được khuyến nghị nhiều nhất cho phần lớn trường hợp. Ngay khi người dùng gõ xong một ô email và nhảy sang ô tiếp theo, hệ thống kiểm tra ô email đó. Ưu điểm lớn: bắt lỗi sớm, đúng bối cảnh — người dùng vừa gõ xong email nên vẫn còn "trong đầu" cái email đó, sửa rất nhanh. Nhược điểm: nếu trường chưa gõ xong mà người dùng vô tình click ra ngoài rồi quay lại, có thể báo lỗi hơi sớm gây khó chịu. Nhưng nhìn chung, on blur là điểm cân bằng tốt nhất.
On change (thời gian thực — real-time) kiểm tra ngay khi người dùng gõ từng ký tự. Cách này chỉ nên dùng cho các trường có yêu cầu định dạng phức tạp mà người dùng cần được hướng dẫn liên tục — điển hình là ô mật khẩu (báo "đủ 8 ký tự", "đã có chữ số"), hoặc ô username kiểm tra trùng ("tên này đã có người dùng"). Ưu điểm: phản hồi tức thì, hướng dẫn người dùng tiến tới đúng. Nhược điểm: nếu áp dụng cho ô email hay số điện thoại, bạn sẽ báo "email không hợp lệ" ngay khi họ vừa gõ chữ "n" của "nguyen@..." — cực kỳ khó chịu vì họ còn chưa gõ xong.
Nguyên tắc vàng: báo lỗi khi người dùng đã "hoàn thành ý định" ở trường đó, chứ không phải khi họ đang giữa chừng. On change chỉ dùng khi phản hồi liên tục thực sự có ích.
Một quy tắc quan trọng: "reward early, punish late"
Có một pattern nâng cao mà nhóm Nielsen Norman và nhiều team sản phẩm lớn khuyến nghị: với một trường chưa từng có lỗi, hãy validate on blur. Nhưng một khi trường đó ĐÃ báo lỗi và người dùng đang sửa, hãy chuyển sang validate on change để họ thấy ngay lúc nào lỗi biến mất. Nói cách khác: "trừng phạt muộn, khen thưởng sớm". Đừng vội báo lỗi khi họ đang gõ lần đầu, nhưng khi họ đang sửa lỗi thì hãy xác nhận ngay khoảnh khắc họ sửa đúng. Cái nút "gửi" chuyển từ mờ sang sáng, cái viền đỏ chuyển sang xanh — những tín hiệu đó cực kỳ động viên.
Vị trí và cấu trúc thông báo lỗi
Thông báo lỗi cần đặt ngay cạnh trường bị lỗi, thường là ngay dưới ô nhập, không phải gom hết lên đầu form. Người dùng nhìn vào đâu thì thấy lỗi ở đó. Với form dài, có thể kết hợp: một tóm tắt lỗi ở đầu (kiểu "Có 3 trường cần sửa") với các link nhảy tới từng trường, đồng thời vẫn có thông báo chi tiết tại chỗ. Đừng chỉ dùng màu đỏ để báo lỗi — người mù màu sẽ không thấy; hãy kèm biểu tượng và chữ (chúng ta sẽ bàn kỹ hơn ở Bài 21 về accessibility).
Tình huống thực tế
Ví dụ 1 — Momo và ô số điện thoại real-time gây khó chịu
Giả sử một phiên bản đầu của luồng chuyển tiền trên ví MoMo áp dụng validation on change cho ô số điện thoại người nhận. Người dùng bắt đầu gõ "09", ngay lập tức dưới ô hiện dòng đỏ "Số điện thoại không hợp lệ". Họ gõ tiếp "0987", vẫn đỏ. Phải đến khi gõ đủ 10 số thì mới hết đỏ. Trong suốt quá trình gõ, người dùng liên tục nhìn thấy màu đỏ và dòng chữ lỗi nhấp nháy.
Kết quả từ dữ liệu định tính: nhiều người dùng lớn tuổi hiểu nhầm rằng họ đang gõ sai và dừng lại giữa chừng để kiểm tra, làm gián đoạn thao tác. Tỷ lệ hoàn thành ô này chậm hơn hẳn.
Cách sửa: chuyển sang validate on blur. Chỉ khi người dùng gõ xong và rời ô (chuẩn bị nhập số tiền), hệ thống mới kiểm tra. Nếu thiếu số, báo "Số điện thoại cần đủ 10 chữ số (ví dụ 0912 345 678)". Sau khi đã báo lỗi một lần, chuyển ô đó sang on change để người dùng thấy màu đỏ biến mất ngay khi họ bổ sung đủ số.
Bài học: Đừng bắt người dùng nhìn thấy lỗi khi họ chưa gõ xong. Real-time validation cho định dạng "phải đủ mới đúng" gần như luôn phản tác dụng.
Ví dụ 2 — Techcombank và form mở tài khoản báo lỗi kiểu "bức tường đỏ"
Giả định một ngân hàng như Techcombank có form đăng ký mở tài khoản online 12 trường trên một trang. Phiên bản cũ dùng on submit: người dùng điền hết, nhấn "Tiếp tục", và nếu sai, cả form hiện lên một hộp đỏ ở đầu trang liệt kê "Vui lòng kiểm tra: Họ tên, Ngày sinh, Số CCCD, Email". Người dùng phải tự cuộn xuống dò từng trường, mà các trường không được đánh dấu rõ, nên họ mất thời gian và bực bội. Đội sản phẩm phát hiện tỷ lệ bỏ giữa chừng ở bước này lên tới khoảng 40%.
Cách sửa họ áp dụng: kết hợp on blur cho từng trường (bắt lỗi ngay khi người dùng rời ô CCCD sai định dạng) với một tóm tắt on submit ở đầu form có link nhảy trực tiếp tới trường lỗi. Mỗi thông báo lỗi được viết cụ thể: thay vì "Số CCCD không hợp lệ" thì viết "Số CCCD phải gồm đúng 12 chữ số". Sau khi triển khai, tỷ lệ bỏ cuộc giảm đáng kể vì người dùng sửa từng lỗi ngay khi phát sinh, không dồn lại thành "bức tường đỏ" ở cuối.
Bài học: Với form dài và quan trọng, đừng dồn hết lỗi vào một lần submit. On blur giúp người dùng sửa lỗi nhỏ giọt, dễ chịu hơn nhiều. Và luôn viết lỗi cụ thể để họ biết chính xác phải làm gì.
Ví dụ 3 — Shopee và ô mật khẩu dùng real-time đúng cách
Ở luồng đăng ký tài khoản Shopee, ô tạo mật khẩu là ví dụ real-time validation dùng đúng. Ngay dưới ô, có một danh sách yêu cầu: "Ít nhất 8 ký tự", "Có chữ hoa và chữ thường", "Có ít nhất một chữ số". Khi người dùng gõ từng ký tự, các mục này lần lượt chuyển từ xám sang xanh với dấu tích. Người dùng thấy rõ mình còn thiếu gì và tiến tới đủ điều kiện trong thời gian thực.
Đây là on change được dùng đúng bối cảnh: mật khẩu là trường mà người dùng cần hướng dẫn liên tục, và mỗi ký tự gõ vào đều làm thay đổi trạng thái "đủ/chưa đủ". Nếu để on blur, người dùng gõ xong một mật khẩu yếu, rời ô, mới bị báo lỗi và phải nghĩ lại từ đầu — kém hiệu quả hơn nhiều.
Bài học: Real-time validation tỏa sáng khi phản hồi từng-ký-tự thực sự giúp người dùng. Điểm mấu chốt là nó "hướng dẫn tiến tới" chứ không "trừng phạt khi chưa xong".
Hướng dẫn từng bước
Khi bạn thiết kế copy và pattern báo lỗi cho một form, hãy đi theo trình tự sau:
Bước 1 — Phân loại từng trường theo độ phức tạp validation. Với mỗi trường, tự hỏi: trường này chỉ cần "đúng định dạng khi hoàn thành" (email, số điện thoại, CCCD) hay cần "hướng dẫn liên tục" (mật khẩu, username kiểm tra trùng)? Nhóm đầu dùng on blur, nhóm sau dùng on change.
Bước 2 — Quyết định chiến lược tổng thể theo độ dài form. Form ngắn 2-3 trường: on submit là đủ. Form vừa và dài: on blur cho từng trường, kèm tóm tắt on submit. Đây là quy tắc mặc định an toàn.
Bước 3 — Áp dụng "reward early, punish late" cho các trường đã báo lỗi. Sau khi một trường đã báo lỗi, chuyển nó sang on change để xác nhận ngay khi người dùng sửa đúng. Đây là chi tiết bạn cần trao đổi rõ với developer.
Bước 4 — Viết nội dung mỗi thông báo lỗi theo công thức cụ thể. Mỗi lỗi cần trả lời: cái gì sai, và cách sửa. Tránh chung chung. "Email không hợp lệ" thay bằng "Email cần có dạng ten@example.com". "Trường bắt buộc" thay bằng "Vui lòng nhập họ và tên".
Bước 5 — Đặt thông báo đúng vị trí và không chỉ dựa vào màu. Ngay dưới trường lỗi, kèm biểu tượng cảnh báo và chữ, để người mù màu vẫn nhận biết. Với form dài, thêm tóm tắt đầu form có link nhảy.
Bước 6 — Xử lý lỗi cấp hệ thống riêng. Lỗi mạng, lỗi server (khác với lỗi nhập liệu) cần thông báo riêng ở cấp form, giữ nguyên dữ liệu người dùng đã nhập, và cho nút "Thử lại". Đừng bao giờ xóa trắng form khi có lỗi server — đó là tội lỗi lớn nhất.
Bước 7 — Kiểm tra thứ tự ưu tiên khi nhiều lỗi cùng lúc. Khi submit và có nhiều lỗi, đưa focus (con trỏ) tới trường lỗi đầu tiên và cuộn tới đó. Đừng bắt người dùng tự tìm.
Lỗi thường gặp & mẹo
Lỗi 1 — Dùng on change cho mọi trường. Đây là lỗi phổ biến nhất. Developer thấy real-time "hiện đại" nên áp cho tất cả, khiến người dùng bị báo lỗi khi còn đang gõ email. Mẹo: chỉ dùng on change cho password và username-check.
Lỗi 2 — Xóa trắng dữ liệu khi báo lỗi. Khi form submit lỗi mà toàn bộ ô bị reset trắng, người dùng phải nhập lại từ đầu — nhiều người bỏ luôn. Luôn giữ nguyên dữ liệu, đặc biệt các trường dài như địa chỉ.
Lỗi 3 — Thông báo lỗi chung chung. "Đã có lỗi", "Thông tin không hợp lệ", "Sai định dạng" không cho biết trường nào và sửa thế nào. Luôn nêu rõ trường và hành động cần làm.
Lỗi 4 — Chỉ dùng màu đỏ, không có icon và chữ. Khoảng 8% nam giới có dạng mù màu nhất định. Viền đỏ đơn thuần vô nghĩa với họ. Luôn kèm biểu tượng và văn bản.
Lỗi 5 — Giọng văn trách móc. "Bạn đã nhập sai", "Lỗi của bạn" tạo cảm giác bị đổ lỗi. Viết trung tính, hướng giải pháp: "Email cần có dạng ten@example.com" thay vì "Bạn nhập email sai rồi".
Mẹo 1 — Với inline validation on blur, đừng validate khi trường còn trống mà người dùng chưa từng chạm vào. Chỉ validate on blur nếu trường đã được focus và có nội dung, hoặc để dành lỗi "bắt buộc" cho lúc submit.
Mẹo 2 — Đồng bộ trạng thái nút submit. Nút nên phản ánh trạng thái form, nhưng đừng khóa nút khi form chưa hợp lệ mà không giải thích — người dùng sẽ không hiểu vì sao bấm không được. Tốt hơn là cho bấm và báo lỗi cụ thể.
Mẹo 3 — Test với dữ liệu tiếng Việt thật. Tên có dấu, địa chỉ dài kiểu "Số 12, ngõ 34, phố Hàng Bài, phường Hàng Bài, quận Hoàn Kiếm" — nhiều regex validation viết cho tiếng Anh sẽ báo lỗi sai với dữ liệu Việt.
Bài tập thực hành
Bài 1 — Phân loại timing. Cho một form đăng ký sự kiện gồm các trường: Họ tên, Email, Số điện thoại, Mật khẩu tạo tài khoản, Mã giảm giá (tùy chọn). Với mỗi trường, hãy ghi rõ bạn chọn validate on submit, on blur hay on change, và giải thích một câu lý do.
Bài 2 — Viết lại thông báo lỗi. Viết lại các thông báo sau cho cụ thể và trung tính hơn:
- "Email không hợp lệ"
- "Trường này là bắt buộc"
- "Sai định dạng số điện thoại"
- "Đã có lỗi xảy ra"
Tóm tắt
Báo lỗi trên form là khoảnh khắc mong manh nhất trong trải nghiệm biểu mẫu, và cách bạn xử lý nó quyết định người dùng đi tiếp hay bỏ đi. Ba thời điểm validate cần nhớ: on submit đơn giản, hợp form ngắn; on blur là mặc định an toàn cho hầu hết trường hợp, bắt lỗi sớm đúng bối cảnh; on change chỉ dùng khi phản hồi từng ký tự thực sự có ích như mật khẩu hay kiểm tra trùng username. Nguyên tắc "reward early, punish late" — báo lỗi muộn, xác nhận đúng sớm — giúp trải nghiệm sửa lỗi dễ chịu hơn hẳn.
Về nội dung, mỗi thông báo lỗi phải trả lời được cái gì sai và cách sửa, đặt ngay cạnh trường lỗi, kèm icon và chữ chứ không chỉ màu đỏ, giọng văn trung tính hướng giải pháp. Và ba điều đừng bao giờ làm: đừng dùng real-time cho mọi trường, đừng xóa trắng dữ liệu khi báo lỗi, đừng viết thông báo chung chung. Nắm chắc timing và pattern là bạn đã làm chủ được phần khó nhất của form copy.