Từ năm 2025, Nghị định 168/2023/NĐ-CP về xử phạt vi phạm hành chính trong lĩnh vực giao thông đường bộ và đường sắt chính thức có hiệu lực. Nghị định này không chỉ siết chặt các quy định, nâng cao mức phạt mà còn đẩy mạnh ứng dụng công nghệ để quản lý và giám sát vi phạm. Hôm nay, chúng ta sẽ cùng nhau xây dựng một công cụ tra cứu phạt nguội đơn giản nhưng hiệu quả bằng HTML, CSS và JavaScript, giúp người dùng dễ dàng kiểm tra thông tin vi phạm giao thông.
Công cụ này cho phép người dùng nhập biển số xe, chọn loại xe và nhận kết quả tức thì từ API, mang đến sự tiện lợi trong việc theo dõi và tuân thủ các quy định giao thông hiện hành. Hãy cùng tìm hiểu cách thực hiện nhé!
Các bước thực hiện
Bước 1: Tạo Khung HTML
Đây là nơi chúng ta đặt các thành phần giao diện: trường chọn loại xe, trường nhập biển số, nút bấm “Tra cứu” và phần hiển thị kết quả.
<div id="tra-cuu-container">
<label for="loaixe"><b><span style="color:red">(*)</span> Loại xe:</b></label>
<select id="tra-cuu-loaixe" class="tool-input form-control">
<option value="1">Ô Tô</option>
<option value="2">Xe máy</option>
</select>
<label for="bsx"><b><span style="color:red">(*)</span> Nhập biển kiểm soát (định dạng 29B14712 hoặc 99K135353):</b></label>
<input id="tra-cuu-bsx" class="tool-input form-control" placeholder="Số lượng" type="text" value="99K135353"/>
<button class="tool-action" onclick="traCuu()">Tra cứu</button>
<label for="ketqua"><b>Kết quả:</b></label>
<div id="tra-cuu-ketqua">Kết quả hiển thị ở đây...</div>
</div>
Giải thích:
- Thẻ
<select>
vớiid="tra-cuu-loaixe"
cho phép người dùng lựa chọn loại xe: 1 (Ô Tô) hoặc 2 (Xe Máy). - Thẻ
<input>
vớiid="tra-cuu-bsx"
là nơi người dùng nhập biển kiểm soát mà họ muốn tra cứu. - Thẻ
<button>
gọi hàm JavaScripttraCuu()
để gửi yêu cầu truy vấn khi người dùng nhấn nút. - Khu vực
<div id="tra-cuu-ketqua">
chịu trách nhiệm hiển thị kết quả phản hồi từ API.
Bước 2: Thêm JavaScript Gọi API Và Xử Lý Dữ Liệu
JavaScript giúp tương tác với người dùng, gửi yêu cầu đến API và hiển thị thông tin lên giao diện.
<script>
// Đối tượng chứa các nhãn tiếng Việt tương ứng với trường dữ liệu trong kết quả
var vietSub = {
"trang_thai": "Trạng thái",
"bien_kiem_sat": "Biển kiểm soát",
"mau_bien": "Màu biển",
"loai_phuong_tien": "Loại phương tiện",
"thoi_gian_vi_pham": "Thời gian vi phạm",
"dia_diem_vi_pham": "Địa điểm vi phạm",
"hanh_vi_vi_pham": "Hành vi vi phạm",
"don_vi_phat_hien_vi_pham": "Đơn vị phát hiện",
"noi_giai_quyet_vu_viec": "Nơi giải quyết vụ việc",
"so_dien_thoai": "Số điện thoại",
"muc_phat": "Mức phạt"
};
// Hàm chính để thực hiện tra cứu phạt nguội
function traCuu() {
// Đặt nội dung tạm thời (spinner) để báo hiệu đang tải dữ liệu
document.querySelector("#tra-cuu-ketqua").innerHTML = `<center>
<svg class="spinner" height="12" style="text-align: center;" viewbox="-1 0 33 12" width="34">
<circle class="loading-dot" cx="4" cy="6" fill="#ee4d2d" r="4"></circle>
<circle class="loading-dot" cx="16" cy="6" fill="#ee4d2d" r="4"></circle>
<circle class="loading-dot" cx="28" cy="6" fill="#ee4d2d" r="4"></circle>
</svg>
</center>`;
// Lấy dữ liệu từ thẻ <select> và <input>
var loaixe = document.querySelector("#tra-cuu-loaixe").value; // 1 = Ô tô, 2 = Xe máy
var bsx = document.querySelector("#tra-cuu-bsx").value.replaceAll('-','').replaceAll('.','');
// Gọi API "vietcheckcar.com" với các tham số loaixe, bsx...
fetch(`https://vietcheckcar.com/api/api.php?api_key=sfund&bsx=${bsx}&bypass_cache=0&loaixe=${loaixe}&vip=0`)
.then(cc => cc.json()) // parse JSON từ phản hồi
.then(data => {
// Tạo biến content_html để lưu trữ thông tin sẽ hiển thị ra màn hình
var content_html = `
<div class="info"><b>Cập nhật lúc:</b> ${data.updated_at}</div>
<div class="info"><b>Trạng thái:</b>
${data.code == 2 ? data.message : "Đã phát hiện <span style='font-weight:900;color:red'> " + data.totalViolations + ' </span> lỗi'}
</div>
<div id="listViolation">
`;
// Nếu data.code == 1, thì có danh sách vi phạm
if (data.code == 1) {
data.violations.forEach(violation => {
content_html += `<div class="table violation"><table>`;
// Lặp qua các trường của một vi phạm bất kỳ
for (const [key, value] of Object.entries(violation)) {
content_html += `<tr>
<td class="table_key"><b>${vietSub[key]}</b></td>
<td class="table_value"><p alt="${value}">
${(value ? String(value) : "Chưa có dữ liệu...").replaceAll("\\n", "<br/>")}
</p></td>
</tr>`;
}
content_html += `</table></div><br/>`;
});
}
// Kết thúc danh sách vi phạm
content_html += `</div>`;
// Loại bỏ các <br/> dư thừa để tránh lỗi hiển thị
content_html = content_html.replaceAll('<td class="table_value"><br/>', '<td>');
// Gán kết quả cuối cùng vào phần tử #tra-cuu-ketqua
document.querySelector("#tra-cuu-ketqua").innerHTML = content_html;
});
}
</script>
Giải thích:
vietSub
đóng vai trò “từ điển” quy đổi key trả về từ API sang tiếng Việt, tăng tính trực quan.- Hàm
traCuu()
gọi API bằngfetch(...)
, sau đó chuyển dữ liệu phản hồi sang dạng JSON và hiển thị lên giao diện. - Người dùng có thể thấy ngay một “loading spinner” (vòng tròn tải) trong lúc chờ API hồi đáp, tạo trải nghiệm tốt hơn.
- Đoạn mã kết thúc bằng việc chèn dữ liệu vào
#tra-cuu-ketqua
.
Bước 3: Thêm Định Dạng CSS
CSS giúp giao diện đẹp và trực quan hơn. Bạn có thể nhúng trực tiếp vào file .html (giữa cặp <style>
) hoặc tách thành file riêng .css.
<style>
#tra-cuu-container {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
width: 400px;
}
#tra-cuu-container .tool-input,
#tra-cuu-container .tool-action {
width: 100%;
padding: 10px;
margin-top: 5px;
border: 1px solid #ccc;
border-radius: 4px;
}
#tra-cuu-container .tool-action {
background-color: #ee4d2d;
color: white;
font-size: 16px;
border: none;
cursor: pointer;
transition: 0.3s;
font-weight: bold;
}
#tra-cuu-container .tool-action:hover {
background-color: #d84315;
}
#tra-cuu-ketqua {
margin-top: 15px;
padding: 10px;
background: #fff3cd;
border: 1px solid #ffecb5;
border-radius: 4px;
font-size: 14px;
}
#tra-cuu-container .table {
width: 100%;
border-collapse: collapse;
margin-top: 10px;
}
#tra-cuu-container .table_key {
font-weight: bold;
background: #eee;
padding: 8px;
}
#tra-cuu-container .table_value {
padding: 8px;
}
#tra-cuu-container .table tr:nth-child(even) {
background: #f9f9f9;
}
</style>
Giải thích:
#tra-cuu-container
: Khung trắng với khoảng đệm (padding) 20px, bo tròn góc (border-radius: 8px) và hiệu ứng đổ bóng (box-shadow)..tool-input
và.tool-action
: Áp dụng cho ô nhập liệu và nút bấm, đảm bảo kích thước đồng nhất, dễ thao tác.- Trạng thái hover của nút
.tool-action
giúp người dùng nhận biết có thể thao tác. #tra-cuu-ketqua
có màu nền vàng nhạt, giúp nổi bật nội dung kết quả tra cứu..table_key
và.table_value
quản lý ô trong bảng để thông tin rõ ràng, có sự phân biệt giữa nhãn và giá trị.
Demo
Kết Luận
Như vậy, mình đã trình bày chi tiết từ tạo khung HTML, viết hàm JavaScript xử lý API, đến thiết kế CSS cho giao diện của hệ thống tra cứu phạt nguội đơn giản này rồi. Nếu bạn thấy hữu ích, hãy chia sẻ hoặc để lại bình luận trên blog nhé. Chúc bạn áp dụng thành công và hẹn gặp lại ở những bài viết tiếp theo!