در قسمت قبلی با مفهوم HTML، ساختار تگها و اتریبیوتها آشنا شدید. حالا میرسیم به یکی از مهمترین تگهای HTML برای ما توسعه دهندگان بکند، تگ <form>. این تگ دقیقاً نقطهٔ اتصال مرورگر و سرور است؛ جایی که دادههای کاربر جمعآوری میشود و طی یک درخواست HTTP به سمت بکاند حرکت میکند.
اگر تا به حال برایتان سؤال بوده که «چرا بعضی وقتها دادهها درست به سرور نمیرسند؟» یا «چرا فایل آپلود نمیشود؟»، پاسخ اغلب در نحوهٔ تنظیم همین تگ نهفته است.
فرم جستجو
یکی از ساده ترین فرم ها، فرم جستجو است. برای اینکه یک فرم جستجو داشته باشید یک فایل به نام search.html بسازید و کد زیر را در آن وارد کنید.
<form>
<input />
<button type="submit">search</button>
</form>اگر این فایل را در مرورگر باز کنید خروجی مشابه زیر خواهد بود.

اگر چیزی در آن تایپ کنید و روی دکمه search کلیک کنید، یک علامت سوال به انتهای آدرس صفحه اضافه میشود. این یعنی فرم فهمیده که شما به دنبال چیزی میگردید اما نفهمیده که به دنبال چه چیزی میگردید.

برای حل این مشکل، کد را به شکل زیر اصلاح کنید.
<form>
<input name="search-for" />
<button type="submit">search</button>
</form>اکنون اگر دوباره اقدام به جستجو کنید، چیزی شبیه تصویر زیر را خواهید دید. این یعنی فرم فهمیده که شما به دنبال چیزی با مقدار test هستید. اکنون این فرم search-for=test را به سمت سرور ارسال میکند.

با یک قدم بیشتر، میتوان این فرم را یک مرحله بهتر کرد. کافیست کد را به شکل زیر اصلاح کنید تا یک مقدار برای راهنمایی کاربر در فیلد جستجو نمایش داده شود.
<form>
<input name="search-for" placeholder="Search for something ..." />
<button type="submit">search</button>
</form>این مقدار به شکل زیر در فرم نمایش داده خواهد شد.

فرم لاگین
یک فایل به نام login.html درست کنید و کد زیر را در آن قرار دهید.
<form>
<label>username:</label>
<input>
<label>password:</label>
<input>
<button type="submit">Login</button>
</form>اکنون فایل را در مرورگر باز کنید. خروجی به شکل زیر خواهد بود. این ساده ترین حالت ممکن برای ساختن یک فرم لاگین است.

ظاهر این فرم زیاد جالب نیست زیرا معمولا فیلد username و password را زیر یکدیگر قرار میدهند. بدین منظور، فیلد ها را در تگ div قرار میدهیم.
<form>
<div>
<label>username:</label>
<input>
</div>
</div>
<label>password:</label>
<input>
</div>
<div>
<button type="submit">Login</button>
</div>
</form>اکنون اگر مرورگر را رفرش کنید، فرم به شکل زیر خواهد بود. اکنون ظاهر آن کمی قابل قبول تر شد.

حال username و password را وارد کنید. خروجی مشابه زیر خواهد بود

مشکل فرم در تصویر بالا این است که پسورد را نمایش میدهد. این از نظر امنیتی درست نیست. اکنون با اصلاح کردن فرم به شکل زیر، این مشکل را حل خواهیم کرد.
<form>
<div>
<label>username:</label>
<input>
</div>
</div>
<label>password:</label>
<input type="password">
</div>
<div>
<button type="submit">Login</button>
</div>
</form>در تکه کد بالا، با قرار دادن type="password" به html گفتیم که این فیلد، یک فیلد پسورد است و نباید متنی که در آن نوشته میشود نمایش داده شود. اکنون اگر مرورگر خود را رفرش کنید و دوباره username و password را وارد کنید، خروجی به شکل زیر خواهد بود.

اگر روی دکمه Login کلیک کنید خواهید دید که اتفاق خاصی نمیافتد و تنها یک علامت سوال در انتهای آدرس صفحه اضافه خواهد شد.

اکنون ویژگی name را مطابق زیر به فرم اضافه میکنیم.
<form>
<div>
<label>username:</label>
<input name="username">
</div>
</div>
<label>password:</label>
<input type="password" name="password">
</div>
<div>
<button type="submit">Login</button>
</div>
</form>اکنون دوباره username و password را وارد کرده و دوباره روی دکمه Login کلیک کنید.

این بار خواهید دید که username و password ای که وارد کردیم، به آدرس صفحه اضافه شده اند. این بدان معنی است که قرار دادن ویژگی name در فیلد ها، اطلاعاتشان را هنگام ثبت فرم، به سمت بکند ارسال میکند.
اکنون همچنان یک مشکل بزرگ وجود دارد. username و password فیلد های مهمی هستند و اطلاعاتشان نباید در آدرس صفحه قرار بگیرد زیرا در اینصورت به راحتی قابل مشاهده و سرقت هستند.
برای جلوگیری از نمایش اطلاعات حساس در آدرس صفحه، باید به تگ form بگوییم که این اطلاعات را با متد post ارسال کند. بدین منظور کافیست کد را به شکل زیر اصلاح کنیم.
<form method="post">
<div>
<label>username:</label>
<input name="username">
</div>
</div>
<label>password:</label>
<input type="password" name="password">
</div>
<div>
<button type="submit">Login</button>
</div>
</form>اکنون:
- با استفاده از دکمه F12 بخش Developer Tools را در کروم یا فایرفاکس باز کنید.
- سپس روی تب Network کلیک کنید. (شماره ۱ در تصویر زیر)
- سپس هر آنچه بعد از علامت سوال در آدرس صفحه قرار دارد را پاک کنید.
- سپس username و password را وارد کنید و روی دکمه Login کلیک کنید.
- سپس روی لاگی که در تب Network افتاده کلیک کنید (شماره ۲ در تصویر زیر)
- سپس میتوانید ببینید که username و password به عنوان Form Data در قسمت payload ارسال شده است.

نکته مهم
- اگر اطلاعات فرم با متد post ارسال شود و سایت نیز از https استفاده کند، اطلاعاتی که در payload به سمت سرور ارسال میشوند رمزگذاری شده و به صورت کاملا امن به سرور ارسال میشوند.
- اگر اطلاعات فرم با متد post ارسال شود ولی سایت از https استفاده نکند، اطلاعاتی که در payload به سمت سرور ارسال میشوند رمزگذاری نشده و قابل شنود یا سرقت هستند.
- اگر اطلاعات فرم با متد get ارسال شود و سایت نیز از https استفاده کند، اطلاعاتی که به سمت سرور ارسال میشوند قابل شنود هستند زیرا در url قرار میگیرند و url در https رمزگذاری نمیشود.
- فرم به صورت پیشفرض، اطلاعات را با متد get ارسال میکند. به عبارت دیگر، مقدار پیشفرض اتریبیوت method برابر get است.
در حال حاضر این فرم اطلاعاتش را به همین آدرس ارسال میکند. یعنی اگر در سمت بکند بخواهیم اطلاعاتی که این فرم ارسال میکند را بگیریم، باید یک تابع (view در جنگو) بنویسیم که در همین آدرس منتظر دریافت اطلاعات باشد. اگر بخواهیم این اطلاعات را به آدرس دیگری ارسال کنیم، کافیست ویژگی action را براین این فرم تعریف کنیم و آدرس مد نظر را در آن قرار دهیم.
<form method="post" action="login">
<div>
<label>username:</label>
<input name="username">
</div>
</div>
<label>password:</label>
<input type="password" name="password">
</div>
<div>
<button type="submit">Login</button>
</div>
</form>در کد بالا به فرم میگوییم که اطلاعات را به آدرس login بفرستد.
یک مشکلی که همچنان در مورد این فرم وجود دارد این است که کاربر میتواند مقدار username و password را وارد نکند و روی دکمه Login کلیک کند. این مشکل را با تنظیم ویژگی required حل میکنیم.
<form method="post" action="login">
<div>
<label>username:</label>
<input name="username" required>
</div>
</div>
<label>password:</label>
<input type="password" name="password" required>
</div>
<div>
<button type="submit">Login</button>
</div>
</form>اکنون اگر مقدار فیلد ها را پر نکنید و روی دکمه Login کلیک کنید فرم ثبت نشده و خطایی به شکل زیر نمایش داده خواهد شد.

این فرم از نظر عملکرد مشکلی ندارد ولی با تنظیم ویژگی for برای label و id برای فیلد متناظر آن میتوان تجربه کاربری آن را بهبود داد.
<form method="post" action="login">
<div>
<label for="username">username:</label>
<input id="username" name="username" required>
</div>
</div>
<label for="password">password:</label>
<input id="password" type="password" name="password" required>
</div>
<div>
<button type="submit">Login</button>
</div>
</form>در تکه کد بالا:
- مقدار username را به عنوان for برای label و id برای input تنظیم کردیم.
- مقدار password را به عنوان for برای label و id برای input تنظیم کردیم.
این کار دو مزیت ایجاد میکند:
- اگر روی لیبل کلیک شود، input فعال شده و امکان نوشتن در آن فعال میشود. این مورد سبب بهبود تجربه کاربری برای کاربرانی میشود که از توانایی حرکتی کمتری نسبت به سایرین برخوردارند.
- نرم افزار های صفحه خوان (screen readers) بهتر میتوانند صفحه را برای افرادی که محدودیت های بینایی دارند بخوانند.
فرم ثبت نام
یک فایل html بسازید و کد زیر را در آن قرار داده و سپس در مرورگر باز کنید.
<form method="post" enctype="multipart/form-data">
<div>
<label for="fullname">نام کامل:</label>
<input type="text" id="fullname" name="fullname" required>
</div>
<div>
<label for="email">ایمیل:</label>
<input type="email" id="email" name="email" required>
</div>
<div>
<label for="avatar">عکس پروفایل:</label>
<input type="file" id="avatar" name="avatar" accept="image/*">
</div>
<button type="submit">ثبتنام</button>
</form>خروجی کد بالا مشابه زیر خواهد بود.

در این فرم، برای ارسال فایل (عکس پروفایل) موارد زیر رعایت شده است.
- خط ۱: ویژگی
method="post"برای form تنظیم شده است. - خط۱: ویژگی
enctype="multipart/form-data"برای form تنظیم شده است. - خط ۱۴: ویژگی
type="file"برای اینپوت مورد نظر تنظیم شده است.
ارسال فایل بدون انجام هر یک از موارد بالا امکان پذیر نیست.
همچنین در این کد، در خط ۱۴ مقدار accept="image/*" برای input تنظیم شده است. این یعنی وقتی کاربر روی دکمه choose file کلیک کرد، فقط فایل های عکس را بتواند انتخاب کند. برخی از رایج ترین تایپ هایی که به عنوان accept میتوان استفاده کرد به شرح زیر است.
| مقدار accept | کاربرد |
|---|---|
image/* | همه فرمتهای عکس (jpg, png, gif, webp, …) |
image/jpeg, image/png | فقط عکسهای JPG و PNG (متداولترین فرمتهای وب) |
application/pdf | فایل PDF |
application/msword | فایل Word قدیمی (doc) |
application/vnd.openxmlformats-officedocument.wordprocessingml.document | فایل Word جدید (docx) |
text/plain | فایل متنی ساده (txt) |
.pdf, .doc, .docx | ترکیب پسوندها (سادهترین روش برای فایلهای آفیس) |
video/mp4 | ویدیوی MP4 |
audio/mpeg | فایل صوتی MP3 |
نکته دیگری که در مورد این فیلد باید به آن توجه کرد، فیلد ایمیل است. اگر سعی کنید چیزی جز ایمیل در آن وارد کنید و روی دکمه Register کلیک کنید با خطایی شبیه زیر مواجه خواهید شد.

این به خاطر این است که مقدار type="email" برای آن تنظیم شده است.
فرم با فیلدهای تکراری
فرض کنید یک فرم لازم داشته باشیم که چندین شماره تلفن را از کاربر بگیرد. بدین منظور یک فایل به نام phone.html بسازید و کد زیر را در آن قرار دهید.
<form>
<div>
<label>phone 1:</label>
<input name="phone">
</div>
<div>
<label>phone 2:</label>
<input name="phone">
</div>
<div>
<label>phone 3:</label>
<input name="phone">
</div>
<div>
<button type="submit">Save</button>
</div>
</form>آن را در مرورگر باز کرده و فرم را پر کنید. سپس روی دکمه Save کلیک کنید. خروجی مشابه زیر خواهد بود.

همانطور که میبینید، چندین phone با مقادیر متفاوت به سمت سرور ارسال شده است. در چنین شرایطی اگر سمت سرور از جنگو استفاده کنیم، برای گرفتن مقادیر به جای get باید از getlist استفاده کنیم. در ادامه یک نمونه کد از سمت بکند (django) را مشاهده میکنید که با آن میتوان چنین مقادیری را دریافت کرد.
def save_phones_view(request):
phones = request.POST.getlist('phone') # ['123', '456', '789']فرم با داده های تو در تو
فرض کنید در قسمتی از طراحی یک سایت فروشگاهی به فرمی نیاز داشته باشیم که اطلاعات مشتری و آدرس تحویل را بگیرد. بدین منظور یک فایل به نام customer.html بسازید و کد زیر را در آن قرار دهید.
<form method="post">
<fieldset>
<legend>Customer Info</legend>
<label for="customer_name">Name:</label>
<input type="text" id="customer_name" name="customer[name]" required>
<label for="customer_email">Email:</label>
<input type="email" id="customer_email" name="customer[email]" required>
</fieldset>
<fieldset>
<legend>Shipping Address</legend>
<label for="address_city">City:</label>
<input type="text" id="address_city" name="address[city]" required>
<label for="address_street">Street:</label>
<input type="text" id="address_street" name="address[street]" required>
</fieldset>
<button type="submit">Submit</button>
</form>اگر این فرم را پر کنید و روی دکمه Submit کلیک کنید و درخواستی که به سمت سرور ارسال شده را در تب Network مرورگر باز کنید، چیزی شبیه زیر خواهید دید.

در اینجا رفتار تکنولوژی های سمت بکند متفاوت است. مثلا در php این اطلاعات در قالب یک آرایه قرار میگیرند.
$_POST['customer']['name'] // 'علی رضایی'
$_POST['address']['city'] // 'تهران'در جنگو اما وضع فرق میکند. جنگو به صورت پیشفرض این براکتها را به ساختار تودرتو تبدیل نمیکند. شما باید یا خودتان پارس کنید، یا از فرمهایی با ساختار مسطح استفاده کنید. این نکته خیلی مهم است: فرمت نامگذاری فیلدها در HTML به فریمورک بکاند شما بستگی دارد. فرانتاند بر اساس انتظار شما name ها را تنظیم میکند.
فرم با دکمههای متعدد – هر دکمه یک رفتار
فرض کنید بخواهیم اطلاعات یک فرم را به دو حالت ذخیره کنیم.
- ذخیره و انتشار
- ذخیره و عدم انتشار (ذخیره پیشنویس)
برای هر کدام از این حالات باید یک دکمه در نظر بگیریم.
برای طراحی چنین فرمی، یک فایل به نام index.html بسازید و کد زیر را در آن وارد کنید.
<form method="post">
<div>
<label for="title">Title:</label>
<input type="text" id="title" name="title" required>
</div>
<div>
<label for="content">Descriptoin:</label>
<textarea id="content" name="content" rows="6" required></textarea>
</div>
<div>
<button type="submit" name="action" value="draft">Save as draft</button>
<button type="submit" name="action" value="publish">Publish</button>
</div>
</form>اکنون اگر فرم را با دکمه save as draft ذخیره کنید مقدار draft و اگر با دکمه Publish ذخیره کنید مقدار publish سمت سرور ارسال خواهد شد.
فرم کامنتگذاری با فیلد مخفی
فرض کنید بخواهیم امکان ثبت نظر برای پست ها را فراهم کنیم. بدین منظور ما به یک فرم نیاز داریم که بتواند مشخص کند کاربری که لاگین کرده و میخواهد نظر بدهد، قصد دارد به چه پستی، چه نظری بدهد.
- از آنجایی که این فرم در صفحه آن پست قرار دارد، پاسخ به سوال «چه پستی» را میتوان به راحتی با دسترسی به id آن پست پاسخ داد.
- پاسخ به سوال «چه نظری» را هم یک فیلد برایش در نظر میگیریم که کاربر بتواند نظرش را در آن بنویسد.
اکنون نوبت نوشتن کد این فرم است. بدین منظور یک فایل به نام index.html بسازید و کد زیر را در آن وارد کنید.
<form method="post">
<input type="hidden" name="post_id" value="42">
<div>
<label for="body">Comment:</label>
<textarea id="body" name="body" rows="4" required></textarea>
</div>
<button type="submit">Send</button>
</form>در کد بالا در خط ۲ یک فیلد مخفی درست کرده ایم که خودش id پست را میگیرد و نیازی به هیچ اقدامی از سوی کاربر ندارد. مقدار این فیلد که با value="42" مشخص شده توسط برنامهنویس فرانت مقدار دهی میشود. به همین دلیل نوع این فیلد را hidden در نظر میگیریم که کاربر نهایی را سردرگم نکند.
سخن پایانی
در این بخش سعی کردم با ذکر چندین مثال کاربردی، استفاده از فرم ها را در عمل به شما آموزش دهم. در فصل بعدی تگ هایی که در این بخش دیدیم را به همراه ویژگی هایشان جمع بندی میکنیم.