Formulir multi-langkah adalah pilihan yang baik bila formulir Anda besar dan memiliki banyak kontrol. Tidak ada seorang pun yang ingin menelusuri formulir yang sangat panjang di perangkat seluler. Dengan mengelompokkan kontrol berdasarkan layar demi layar, kami dapat meningkatkan pengalaman mengisi formulir yang panjang dan rumit.
Namun kapan terakhir kali Anda mengembangkan formulir multi-langkah? Apakah itu terdengar menyenangkan bagi Anda? Ada begitu banyak hal yang harus dipikirkan dan begitu banyak bagian bergerak yang perlu dikelola sehingga saya tidak akan menyalahkan Anda karena menggunakan perpustakaan formulir atau bahkan beberapa jenis widget formulir yang menangani semuanya untuk Anda.
Namun melakukannya dengan tangan bisa menjadi latihan yang bagus dan cara yang bagus untuk memoles dasar-dasarnya. Saya akan menunjukkan kepada Anda bagaimana saya membuat formulir multi-langkah pertama saya, dan saya harap Anda tidak hanya melihat betapa mudahnya pendekatan itu, tetapi bahkan mungkin menemukan area untuk membuat pekerjaan saya menjadi lebih baik.
Kita akan menelusuri strukturnya bersama-sama. Kami akan membuat lamaran pekerjaan, yang menurut saya banyak dari kita dapat memahaminya akhir-akhir ini. Saya akan menguraikan dasar HTML, CSS, dan JavaScript terlebih dahulu, lalu kita akan melihat pertimbangan untuk aksesibilitas dan validasi.
Saya telah membuat Repo GitHub untuk kode akhir jika Anda ingin merujuknya sepanjang jalan.
Struktur bentuk multi-langkah
Formulir lamaran kerja kami memiliki empat bagian, yang terakhir adalah tampilan ringkasan, tempat kami menampilkan semua jawaban kepada pengguna sebelum mereka mengirimkannya. Untuk mencapai hal ini, kami membagi HTML menjadi empat bagian, masing-masing diidentifikasi dengan ID, dan menambahkan navigasi di bagian bawah halaman. Saya akan memberi Anda HTML dasar itu di bagian selanjutnya.
Menavigasi pengguna untuk berpindah melalui bagian berarti kami juga akan menyertakan indikator visual tentang langkah apa yang mereka ambil dan berapa banyak langkah yang tersisa. Indikator ini dapat berupa teks dinamis sederhana yang diperbarui sesuai dengan langkah aktif atau jenis indikator bilah kemajuan yang lebih menarik. Kami akan melakukan yang pertama untuk menjaga semuanya tetap sederhana dan fokus pada sifat multi-langkah dari formulir.,
Struktur dan gaya dasar
Kami akan lebih fokus pada logikanya, tetapi saya akan memberikan cuplikan kode dan tautan ke kode lengkap di bagian akhir.
Mari kita mulai dengan membuat folder untuk menampung halaman kita. Lalu, buatlah index.html
file dan rekatkan yang berikut ke dalamnya:
Buka HTML
Melihat kodenya, Anda dapat melihat tiga bagian dan grup navigasi. Bagian tersebut berisi input formulir dan tidak ada validasi formulir asli. Hal ini memberi kami kontrol yang lebih baik dalam menampilkan pesan kesalahan karena validasi formulir asli hanya dipicu ketika Anda mengklik tombol kirim.
Selanjutnya, buat a styles.css
file dan rekatkan ini ke dalamnya:
Gaya dasar terbuka
:root {
--primary-color: #8c852a;
--secondary-color: #858034;
}
body {
font-family: sans-serif;
line-height: 1.4;
margin: 0 auto;
padding: 20px;
background-color: #f4f4f4;
max-width: 600px;
}
h1 {
text-align: center;
}
form {
background: #fff;
padding: 40px;
border-radius: 5px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
display: flex;
flex-direction: column;
}
.form-group {
display: flex;
gap: 7%;
}
.form-group > div {
width: 100%;
}
input:not((type="checkbox")),
select,
textarea {
width: 100%;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
}
.form-control {
margin-bottom: 15px;
}
button {
display: block;
width: 100%;
padding: 10px;
color: white;
background-color: var(--primary-color);
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
}
button:hover {
background-color: var(--secondary-color);
}
.group-two, .group-three {
display: none;
}
.arrows {
display: flex;
justify-content: space-between
align-items: center;
margin-top: 10px;
}
#navLeft, #navRight {
width: fit-content;
}
@media screen and (max-width: 600px) {
.form-group {
flex-direction: column;
}
}
Buka file HTML di browser, dan Anda akan mendapatkan sesuatu seperti tata letak dua kolom pada tangkapan layar berikut, lengkap dengan indikator halaman saat ini dan navigasi.
Menambahkan fungsionalitas dengan vanilla JavaScript
Sekarang, buat a script.js
file di direktori yang sama dengan file HTML dan CSS dan tempelkan JavaScript berikut ke dalamnya:
Buka skrip dasar
const stepInfo = document.getElementById("stepInfo");
const navLeft = document.getElementById("navLeft");
const navRight = document.getElementById("navRight");
const form = document.getElementById("myForm");
const formSteps = ("one", "two", "three");
let currentStep = 0;
function updateStepVisibility() {
formSteps.forEach((step) => {
document.getElementById(step).style.display = "none";
});
document.getElementById(formSteps(currentStep)).style.display = "block";
stepInfo.textContent = `Step ${currentStep + 1} of ${formSteps.length}`;
navLeft.style.display = currentStep === 0 ? "none" : "block";
navRight.style.display =
currentStep === formSteps.length - 1 ? "none" : "block";
}
document.addEventListener("DOMContentLoaded", () => {
navLeft.style.display = "none";
updateStepVisibility();
navRight.addEventListener("click", () => {
if (currentStep < formSteps.length - 1) {
currentStep++;
updateStepVisibility();
}
});
navLeft.addEventListener("click", () => {
if (currentStep > 0) {
currentStep--;
updateStepVisibility();
}
});
});
Skrip ini mendefinisikan metode yang menampilkan dan menyembunyikan bagian tergantung pada formStep
nilai yang sesuai dengan ID bagian formulir. Ini diperbarui stepInfo
dengan bagian formulir yang aktif saat ini. Teks dinamis ini bertindak sebagai indikator kemajuan bagi pengguna.
Ini kemudian menambahkan logika yang menunggu halaman dimuat dan mengklik peristiwa ke tombol navigasi untuk mengaktifkan perputaran melalui bagian formulir yang berbeda. Jika Anda menyegarkan halaman, Anda akan melihat bahwa formulir multi-langkah berfungsi seperti yang diharapkan.
Navigasi formulir multi-langkah
Mari selami lebih dalam apa yang dilakukan kode Javascript di atas. Di updateStepVisibility()
fungsinya, pertama-tama kita sembunyikan semua bagian agar terlihat bersih:
formSteps.forEach((step) => {
document.getElementById(step).style.display = "none";
});
Kemudian, kami menampilkan bagian yang sedang aktif:
document.getElementById(formSteps(currentStep)).style.display = "block";`
Selanjutnya, kami memperbarui teks kemajuan indikator melalui formulir:
stepInfo.textContent = `Step ${currentStep + 1} of ${formSteps.length}`;
Terakhir, kita sembunyikan tombol Sebelumnya jika kita berada di langkah pertama dan sembunyikan tombol Berikutnya jika kita berada di bagian terakhir:
navLeft.style.display = currentStep === 0 ? "none" : "block";
navRight.style.display = currentStep === formSteps.length - 1 ? "none" : "block";
Mari kita lihat apa yang terjadi saat halaman dimuat. Kami pertama-tama menyembunyikan tombol Sebelumnya saat formulir dimuat di bagian pertama:
document.addEventListener("DOMContentLoaded", () => {
navLeft.style.display = "none";
updateStepVisibility();
Kemudian kita ambil tombol Berikutnya dan menambahkan acara klik yang secara kondisional menambah jumlah langkah saat ini dan kemudian memanggil updateStepVisibility()
fungsi, yang kemudian memperbarui bagian baru yang akan ditampilkan:
navRight.addEventListener("click", () => {
if (currentStep < formSteps.length - 1) {
currentStep++;
updateStepVisibility();
}
});
Terakhir, kita ambil tombol Sebelumnya dan melakukan hal yang sama tetapi sebaliknya. Di sini, kami mengurangi jumlah langkah secara kondisional dan memanggil updateStepVisibility()
:
navLeft.addEventListener("click", () => {
if (currentStep > 0) {
currentStep--;
updateStepVisibility();
}
});
Kesalahan penanganan
Pernahkah Anda menghabiskan 10+ menit untuk mengisi formulir hanya untuk mengirimkannya dan mendapatkan kesalahan samar yang meminta Anda untuk memperbaiki ini dan itu? Saya lebih suka jika formulir langsung memberi tahu saya bahwa ada sesuatu yang salah sehingga saya bisa memperbaikinya sebelum Saya pernah sampai ke tombol Kirim. Itulah yang akan kami lakukan dalam bentuk kami.
Prinsip kami adalah dengan jelas menunjukkan kontrol mana yang memiliki kesalahan dan memberikan pesan kesalahan yang berarti. Hapus kesalahan saat pengguna mengambil tindakan yang diperlukan. Mari tambahkan beberapa validasi ke formulir kita. Pertama, ambil elemen masukan yang diperlukan dan tambahkan ini ke elemen yang sudah ada:
const nameInput = document.getElementById("name");
const idNumInput = document.getElementById("idNum");
const emailInput = document.getElementById("email");
const birthdateInput = document.getElementById("birthdate")
const documentInput = document.getElementById("document");
const departmentInput = document.getElementById("department");
const termsCheckbox = document.getElementById("terms");
const skillsInput = document.getElementById("skills");
Kemudian, tambahkan fungsi untuk memvalidasi langkah-langkahnya:
Buka skrip validasi
function validateStep(step) {
let isValid = true;
if (step === 0) {
if (nameInput.value.trim() === "")
showError(nameInput, "Name is required");
isValid = false;
}
if (idNumInput.value.trim() === "") {
showError(idNumInput, "ID number is required");
isValid = false;
}
if (emailInput.value.trim() === "" || !emailInput.validity.valid) {
showError(emailInput, "A valid email is required");
isValid = false;
}
if (birthdateInput.value === "") {
showError(birthdateInput, "Date of birth is required");
isValid = false;
}
else if (step === 1) {
if (!documentInput.files(0)) {
showError(documentInput, "CV is required");
isValid = false;
}
if (departmentInput.value === "") {
showError(departmentInput, "Department selection is required");
isValid = false;
}
} else if (step === 2) {
if (!termsCheckbox.checked) {
showError(termsCheckbox, "You must accept the terms and conditions");
isValid = false;
}
}
return isValid;
}
Di sini, kami memeriksa apakah setiap masukan yang diperlukan memiliki nilai tertentu dan apakah masukan email memiliki masukan yang valid. Kemudian, kami mengatur boolean isValid sesuai. Kami juga memanggil a showError()
fungsi yang belum kita definisikan.
Paste kode ini di atas validateStep()
fungsi:
function showError(input, message) {
const formControl = input.parentElement;
const errorSpan = formControl.querySelector(".error-message");
input.classList.add("error");
errorSpan.textContent = message;
}
Sekarang, tambahkan gaya berikut ke stylesheet:
Buka gaya validasi
input:focus, select:focus, textarea:focus {
outline: .5px solid var(--primary-color);
}
input.error, select.error, textarea.error {
outline: .5px solid red;
}
.error-message {
font-size: x-small;
color: red;
display: block;
margin-top: 2px;
}
.arrows {
color: var(--primary-color);
font-size: 18px;
font-weight: 900;
}
#navLeft, #navRight {
display: flex;
align-items: center;
gap: 10px;
}
#stepInfo {
color: var(--primary-color);
}
Jika Anda menyegarkan formulir, Anda akan melihat bahwa tombol tersebut tidak membawa Anda ke bagian berikutnya sampai masukan dianggap valid:
Terakhir, kami ingin menambahkan penanganan error real-time sehingga error hilang saat pengguna mulai memasukkan informasi yang benar. Tambahkan fungsi ini di bawah validateStep()
fungsi:
Buka skrip validasi waktu nyata
function setupRealtimeValidation() {
nameInput.addEventListener("input", () => {
if (nameInput.value.trim() !== "") clearError(nameInput);
});
idNumInput.addEventListener("input", () => {
if (idNumInput.value.trim() !== "") clearError(idNumInput);
});
emailInput.addEventListener("input", () => {
if (emailInput.validity.valid) clearError(emailInput);
});
birthdateInput.addEventListener("change", () => {
if (birthdateInput.value !== "") clearError(birthdateInput);
});
documentInput.addEventListener("change", () => {
if (documentInput.files(0)) clearError(documentInput);
});
departmentInput.addEventListener("change", () => {
if (departmentInput.value !== "") clearError(departmentInput);
});
termsCheckbox.addEventListener("change", () => {
if (termsCheckbox.checked) clearError(termsCheckbox);
});
}
Fungsi ini menghapus kesalahan jika masukan tidak lagi valid dengan mendengarkan masukan dan peristiwa perubahan lalu memanggil fungsi untuk menghapus kesalahan. Tempelkan clearError()
berfungsi di bawah showError()
satu:
function clearError(input) {
const formControl = input.parentElement;
const errorSpan = formControl.querySelector(".error-message");
input.classList.remove("error");
errorSpan.textContent = "";
}
Dan sekarang kesalahannya hilang ketika pengguna mengetikkan nilai yang benar:
Formulir multi-langkah sekarang menangani kesalahan dengan baik. Jika Anda memutuskan untuk menyimpan kesalahan hingga akhir formulir, paling tidak, kembalikan pengguna ke kontrol formulir yang bermasalah dan tunjukkan beberapa indikasi berapa banyak kesalahan yang perlu mereka perbaiki.
Menangani penyerahan formulir
Dalam formulir multi-langkah, penting untuk menunjukkan kepada pengguna ringkasan semua jawaban mereka di akhir sebelum mereka mengirimkannya dan menawarkan opsi untuk mengedit jawaban mereka jika perlu. Orang tersebut tidak dapat melihat langkah sebelumnya tanpa melakukan navigasi mundur, jadi menampilkan ringkasan pada langkah terakhir memberikan jaminan dan kesempatan untuk memperbaiki kesalahan apa pun.
Mari tambahkan bagian keempat ke markup untuk menahan tampilan ringkasan ini dan memindahkan tombol kirim di dalamnya. Rekatkan ini tepat di bawah bagian ketiga index.html
:
Buka HTML
Kemudian perbarui formStep
di Javascript Anda untuk membaca:
const formSteps = ("one", "two", "three", "four");
Terakhir, tambahkan kelas berikut ke styles.css
:
.summary-section {
display: flex;
align-items: center;
gap: 10px;
}
.summary-section p:first-child {
width: 30%;
flex-shrink: 0;
border-right: 1px solid var(--secondary-color);
}
.summary-section p:nth-child(2) {
width: 45%;
flex-shrink: 0;
padding-left: 10px;
}
.edit-btn {
width: 25%;
margin-left: auto;
background-color: transparent;
color: var(--primary-color);
border: .7px solid var(--primary-color);
border-radius: 5px;
padding: 5px;
}
.edit-btn:hover {
border: 2px solid var(--primary-color);
font-weight: bolder;
background-color: transparent;
}
Sekarang, tambahkan yang berikut ini ke atas script.js
file di mana yang lain const
adalah:
const nameVal = document.getElementById("name-val");
const idVal = document.getElementById("id-val");
const emailVal = document.getElementById("email-val");
const bdVal = document.getElementById("bd-val")
const cvVal = document.getElementById("cv-val");
const deptVal = document.getElementById("dept-val");
const skillsVal = document.getElementById("skills-val");
const editButtons =
"name-edit": 0,
"id-edit": 0,
"email-edit": 0,
"bd-edit": 0,
"cv-edit": 1,
"dept-edit": 1,
"skills-edit": 2
};
Kemudian tambahkan fungsi ini scripts.js
:
function updateSummaryValues() {
nameVal.textContent = nameInput.value;
idVal.textContent = idNumInput.value;
emailVal.textContent = emailInput.value;
bdVal.textContent = birthdateInput.value;
const fileName = documentInput.files(0)?.name;
if (fileName)
const extension = fileName.split(".").pop();
const baseName = fileName.split(".")(0);
const truncatedName = baseName.length > 10 ? baseName.substring(0, 10) + "..." : baseName;
cvVal.textContent = `${truncatedName}.${extension}`;
} else {
cvVal.textContent = "No file selected";
}
deptVal.textContent = departmentInput.value;
skillsVal.textContent = skillsInput.value || "No skills submitted";
}
Ini secara dinamis memasukkan nilai masukan ke bagian ringkasan formulir, memotong nama file, dan menawarkan teks cadangan untuk masukan yang tidak diperlukan.
Kemudian perbarui updateStepVisibility()
fungsi untuk memanggil fungsi baru:
function updateStepVisibility() {
formSteps.forEach((step) => {
document.getElementById(step).style.display = "none";
});
document.getElementById(formSteps(currentStep)).style.display = "block";
stepInfo.textContent = `Step ${currentStep + 1} of ${formSteps.length}`;
if (currentStep === 3) {
updateSummaryValues();
}
navLeft.style.display = currentStep === 0 ? "none" : "block";
navRight.style.display = currentStep === formSteps.length - 1 ? "none" : "block";
}
Terakhir, tambahkan ini ke DOMContentLoaded
pendengar acara:
Object.keys(editButtons).forEach((buttonId) => {
const button = document.getElementById(buttonId);
button.addEventListener("click", (e) => {
currentStep = editButtons(buttonId);
updateStepVisibility();
});
});
Saat menjalankan formulir, Anda akan melihat bahwa bagian ringkasan memperlihatkan semua nilai yang dimasukkan dan memungkinkan pengguna untuk mengedit nilai apa pun sebelum mengirimkan informasi:
Dan sekarang, kami dapat mengirimkan formulir kami:
form.addEventListener("submit", (e) => {
e.preventDefault();
if (validateStep(2)) {
alert("Form submitted successfully!");
form.reset();
currentFormStep = 0;
updateStepVisibility();
}
});
Formulir multi-langkah kami sekarang memungkinkan pengguna untuk mengedit dan melihat semua informasi yang mereka berikan sebelum mengirimkannya.
Kiat aksesibilitas
Membuat formulir multi-langkah dapat diakses dimulai dengan hal-hal dasar: menggunakan HTML semantik. Ini adalah setengah dari pertempuran. Hal ini diikuti dengan penggunaan label bentuk yang sesuai.
Cara lain untuk membuat formulir lebih mudah diakses termasuk memberikan ruang yang cukup untuk elemen yang harus diklik di layar kecil dan memberikan deskripsi bermakna pada navigasi formulir dan indikator kemajuan.
Menawarkan umpan balik kepada pengguna adalah bagian penting; tidak bagus jika menolak masukan pengguna secara otomatis setelah jangka waktu tertentu, namun membiarkan pengguna mengabaikannya sendiri. Memperhatikan kontras dan pilihan font juga penting, karena keduanya memengaruhi seberapa mudah dibacanya formulir Anda.
Mari lakukan penyesuaian berikut pada markup untuk aksesibilitas yang lebih teknis:
- Menambahkan
aria-required="true"
untuk semua input kecuali keterampilan. Hal ini memungkinkan pembaca layar mengetahui bahwa bidang tersebut wajib diisi tanpa bergantung pada validasi asli. - Menambahkan
role="alert"
ke rentang kesalahan. Ini membantu pembaca layar mengetahui untuk menganggapnya penting ketika input berada dalam status kesalahan. - Menambahkan
role="status" aria-live="polite"
ke.stepInfo
. Ini akan membantu pembaca layar memahami bahwa info langkah mengawasi suatu keadaan, dan aria-live yang disetel ke sopan menunjukkan bahwa jika nilainya berubah, ia tidak perlu segera mengumumkannya.
Di file skrip, ganti showError()
Dan clearError()
berfungsi dengan berikut ini:
function showError(input, message) {
const formControl = input.parentElement;
const errorSpan = formControl.querySelector(".error-message");
input.classList.add("error");
input.setAttribute("aria-invalid", "true");
input.setAttribute("aria-describedby", errorSpan.id);
errorSpan.textContent = message;
}
function clearError(input) {
const formControl = input.parentElement;
const errorSpan = formControl.querySelector(".error-message");
input.classList.remove("error");
input.removeAttribute("aria-invalid");
input.removeAttribute("aria-describedby");
errorSpan.textContent = "";
}
Di sini, kami secara terprogram menambahkan dan menghapus atribut yang secara eksplisit mengaitkan input dengan rentang kesalahannya dan menunjukkan bahwa input tersebut dalam keadaan tidak valid.
Terakhir, mari tambahkan fokus pada masukan pertama setiap bagian; tambahkan kode berikut di akhir updateStepVisibility()
fungsi:
const currentStepElement = document.getElementById(formSteps(currentStep));
const firstInput = currentStepElement.querySelector(
"input, select, textarea"
);
if (firstInput) {
firstInput.focus();
}
Dan dengan itu, formulir multi-langkah menjadi lebih mudah diakses.
Kesimpulan
Ini dia, formulir multi-langkah empat bagian untuk lamaran pekerjaan! Seperti yang saya katakan di bagian atas artikel ini, ada banyak hal yang harus diselesaikan — begitu banyak hal sehingga saya tidak akan menyalahkan Anda karena mencari solusi yang out-of-the-box.
Namun jika Anda harus menyerahkan formulir multi-langkah, semoga sekarang Anda melihat bahwa itu bukanlah hukuman mati. Ada jalan bahagia yang membawa Anda ke sana, lengkap dengan navigasi dan validasi, tanpa menyimpang dari praktik yang baik dan dapat diakses.
Dan inilah cara saya mendekatinya! Sekali lagi, saya menganggap ini sebagai tantangan pribadi untuk melihat sejauh mana saya bisa melangkah, dan saya cukup senang dengan hal itu. Namun saya ingin tahu apakah Anda melihat peluang tambahan untuk menjadikan hal ini lebih memperhatikan pengalaman pengguna dan mempertimbangkan aksesibilitas.
Referensi
Berikut beberapa tautan relevan yang saya rujuk saat menulis artikel ini:
- Bagaimana Menyusun Formulir Web (MDN)
- Formulir Multi-halaman (W3C.org)
- Buat formulir yang dapat diakses (Proyek A11y)