تاروپود — خودآموز HTML و CSS

فصل ۵ از ۱۰

پیشرفت ترم
۰٪

ترم ۵ · تعامل و جزئیات

اعتبارسنجی فرم

فصل ۵پیش‌نمایش رایگان
۹ دقیقه مطالعه فصل ۵

در این فصل چه می‌سازی#

آخر این فصل یک فرمِ ثبت‌نامِ کوچک داری که پیش از فرستادن، خودش به کاربر می‌گوید چه چیزی درست است و چه چیزی نه: خانهٔ ایمیل وقتی درست پر شود سبز می‌شود و وقتی غلط باشد قرمز، و تیکِ «قوانین را می‌پذیرم» تا زده نشود فرم فرستاده نمی‌شود. نکتهٔ مهم این است که هیچ‌کدامِ این‌ها به JavaScript نیاز ندارد — خودِ مرورگر و چند خط CSS این کار را می‌کنند.

یک فرمِ ساده در سه حالت کنارِ هم: خانهٔ ایمیلِ خالی با مرزِ خنثی، همان خانه با ورودیِ نادرست و مرزِ قرمز، و همان خانه با ورودیِ درست و مرزِ سبز

آخر این فصل می‌توانی:

  • با attribute های required، type="email"، min/max و pattern بگذاری مرورگر خودش ورودی را بررسی کند
  • با :required، :valid و :invalid ظاهرِ خانه‌ها را به وضعیتشان گره بزنی
  • توضیح بدهی چرا یک خانهٔ خالیِ required از همان لحظهٔ اول :invalid است
  • با :checked ظاهر را به تیک‌خوردنِ یک checkbox گره بزنی

قبل از شروع#

  • از فصل ۴ (همین ترم): استایلِ <input> و <button>، selectorِ صفتی مثل [type="email"]، و accent-color — فرم را از آنجا داریم و حالا اعتبارش را نشان می‌دهیم.
  • از فصل ۲ (همین ترم): :not() و combinatorِ +.

مثل همیشه فقط یک مرورگر لازم داری، یا فقط همین آزمایشگاه.

۱. مرورگر پیش از تو بررسی می‌کند#

قبل از هر CSS ای، یک چیزِ مهم را بدان: مرورگر از قبل یک اعتبارسنجیِ داخلی دارد. کافی است در HTML به آن بگویی هر خانه چه انتظاری دارد. این را با attribute می‌گویی:

  • required — این خانه نباید خالی بماند.
  • type="email" — این خانه باید یک ایمیلِ درست باشد (یعنی یک @ داشته باشد).
  • min و max — کمینه و بیشینهٔ یک عدد (type="number").
  • pattern — یک الگو که ورودی باید با آن جور دربیاید.

فرمِ ما دو خانه دارد که هر دو required اند: یک ایمیل و یک checkbox. این HTML را در آزمایشگاه بنویس:

<form>
  <label for="email">ایمیل</label>
  <input type="email" id="email" name="email" placeholder="[email protected]" required>

  <label class="terms">
    <input type="checkbox" name="agree" required>
    <span>قوانین را می‌پذیرم</span>
  </label>

  <button type="submit">ثبت‌نام</button>
</form>

حالا بدونِ اینکه چیزی پر کنی، دکمهٔ «ثبت‌نام» را بزن.

چک کن: فرم فرستاده نمی‌شود؛ مرورگر یک حبابِ کوچک بالای خانهٔ ایمیل نشان می‌دهد که می‌گوید این خانه لازم است. حالا یک متنِ بی‌ربط مثل abc در ایمیل بنویس و باز دکمه را بزن — این‌بار می‌گوید ایمیل معتبر نیست. همهٔ این‌ها را خودِ مرورگر انجام داد، بی‌یک خط CSS یا JavaScript.

💡 نکته: برای عددها همین کار را با min و max می‌کنی. مثلاً <input type="number" min="1" max="5"> نمی‌گذارد کاربر عددی بیرونِ بازهٔ ۱ تا ۵ بفرستد. pattern هم یک الگوی دقیق‌تر می‌دهد. کارِ همهٔ این‌ها یکی است: قانونِ خانه را در HTML اعلام می‌کنی، بررسی‌اش با مرورگر است.

قبل از ادامه، جواب بده: اگر required را از خانهٔ ایمیل برداری، زدنِ دکمه روی خانهٔ خالی باز هم جلوی فرستادن را می‌گیرد؟


جواب: نه. بدونِ required مرورگر خانهٔ خالی را مشکل نمی‌بیند، پس فرم فرستاده می‌شود. قانون را تو با attribute می‌گذاری؛ اگر نگذاری، بررسی‌ای هم نیست.

۲. :required: خانه‌های لازم را نشان بده#

حالا که مرورگر می‌داند کدام خانه لازم است، CSS هم می‌تواند همان را بداند. یک pseudo-class تازه داریم: :required به هر خانه‌ای می‌خورد که attributeِ required دارد. بیا خانه‌های لازم را با یک پس‌زمینهٔ گرم نشان بدهیم تا کاربر ببیند کدام‌ها را حتماً باید پر کند. اول استایلِ پایهٔ خانهٔ ایمیل، بعد قاعدهٔ :required:

input[type="email"] {
  padding: 8px;
  border: 2px solid #9a8c78;
  border-radius: 6px;
}

input:required {
  background-color: #fbeecb;
}

چک کن: خانهٔ ایمیل یک پس‌زمینهٔ کرمِ ملایم می‌گیرد — نشانه‌ای که می‌گوید «این خانه لازم است». هنوز هیچ رنگِ سبز یا قرمزی نیست؛ فقط داریم می‌گوییم کدام خانه اجباری است.

۳. :valid و :invalid — و دامِ قرمزِ لحظهٔ اول#

دو pseudo-classِ اصلیِ این فصل این‌هایند: :valid به خانه‌ای می‌خورد که ورودی‌اش قانون را رعایت کرده، و :invalid به خانه‌ای که رعایت نکرده. با این دو می‌توانی مرزِ خانه را سبز یا قرمز کنی. طبیعی است که اول این را بنویسی:

input[type="email"]:valid {
  border-color: #2e7d32;
}

input[type="email"]:invalid {
  border-color: #c62828;
}

نگاه کن به صفحه. یک چیزِ عجیب می‌بینی: خانهٔ ایمیل همین حالا، پیش از اینکه حتی یک حرف تایپ کنی، مرزِ قرمز دارد. انگار فرم از کاربر عصبانی است قبل از اینکه کاری کرده باشد. این بدترین نوعِ بازخورد است.

چرا این‌طور شد؟ چون خانهٔ ایمیل هم required است و هم خالی. یک خانهٔ requiredِ خالی، قانونش را رعایت نکرده — پس از همان اولین لحظه :invalid است، بی‌آنکه کاربر خطایی کرده باشد. این دقیقاً همان تلهٔ معروفِ اعتبارسنجی است.

🔧 اگر کار نکرد: خانه از لحظهٔ باز شدنِ صفحه قرمز است؟ مشکل این است که :invalid به خانهٔ خالیِ لازم هم می‌خورد. راهِ درست این است که رنگ را فقط وقتی نشان بدهی که کاربر چیزی تایپ کرده باشد. برای این یک pseudo-classِ کمکی هست: :placeholder-shown به خانه‌ای می‌خورد که خالی است و placeholderش دیده می‌شود. پس :not(:placeholder-shown) یعنی «فقط وقتی چیزی نوشته شده». این را به هر دو قاعده اضافه کن:

input[type="email"]:not(:placeholder-shown):valid {
  border-color: #2e7d32;
}

input[type="email"]:not(:placeholder-shown):invalid {
  border-color: #c62828;
}

چک کن: حالا خانهٔ خالی مرزِ خنثی دارد، نه قرمز. یک abc بنویس — قرمز می‌شود. آن را پاک کن و یک ایمیلِ درست مثل [email protected] بنویس — سبز می‌شود. رنگ دیگر تنها وقتی می‌آید که کاربر واقعاً چیزی گفته باشد. (این خانه placeholder دارد؛ برای همین :placeholder-shown روی آن معنا پیدا می‌کند.)

قبل از ادامه، جواب بده: چرا یک خانهٔ requiredِ خالی از همان اول :invalid است، ولی یک خانهٔ بدونِ requiredِ خالی این‌طور نیست؟


جواب: چون :invalid یعنی «قانونِ این خانه رعایت نشده». قانونِ خانهٔ required این است که «خالی نباش»، و خانهٔ خالی این قانون را می‌شکند — پس بی‌درنگ :invalid است. خانه‌ای که required نیست هیچ قانونی ندارد که بشکند، پس خالی هم که باشد :valid می‌ماند.

۴. :checked: ظاهر را به تیک گره بزن#

آخرین pseudo-classِ این فصل :checked است: به یک checkbox یا radio می‌خورد که تیک خورده باشد. با آن می‌توانی وقتی کاربر تیک زد، چیزی روی صفحه عوض شود. بیا متنِ «قوانین را می‌پذیرم» را وقتی تیک خورد سبز و پررنگ کنیم. چون متن در یک <span>ِ بعد از checkbox است، از combinatorِ + (از فصل ۲) استفاده می‌کنیم:

.terms input:checked + span {
  color: #2e7d32;
  font-weight: bold;
}

چک کن: روی checkbox تیک بزن — متنِ کنارش سبز و پررنگ می‌شود. تیک را بردار، به حالتِ عادی برمی‌گردد. input:checked + span یعنی «آن <span> ای که بلافاصله بعد از یک checkboxِ تیک‌خورده آمده».

⚠️ مواظب باش: :checked فقط به checkbox و radio می‌خورد، نه به خانه‌های متنی. برای خانهٔ متنی از :valid/:invalid استفاده کن، و برای تیک از :checked. این‌ها را با هم قاطی نکن.

۵. کد کامل#

این هم دو فایلِ کاملِ فرم. اول index.html:

<!-- index.html -->
<!DOCTYPE html>
<html lang="fa" dir="rtl">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>اعتبارسنجی فرم</title>
    <link rel="stylesheet" href="styles.css">
  </head>
  <body>
    <form>
      <label for="email">ایمیل</label>
      <input type="email" id="email" name="email" placeholder="[email protected]" required>

      <label class="terms">
        <input type="checkbox" name="agree" required>
        <span>قوانین را می‌پذیرم</span>
      </label>

      <button type="submit">ثبت‌نام</button>
    </form>
  </body>
</html>

و styles.css — دقت کن که قاعده‌های :valid/:invalid هر دو :not(:placeholder-shown) دارند، همان راهِ درستی که تلهٔ قرمزِ لحظهٔ اول را می‌بندد:

/* styles.css */
* {
  box-sizing: border-box;
}

body {
  background-color: #f6edda;
  font-family: system-ui, sans-serif;
  padding: 24px;
}

form {
  max-width: 320px;
  display: grid;
  gap: 12px;
}

label {
  font-weight: bold;
}

input[type="email"] {
  padding: 8px;
  border: 2px solid #9a8c78;
  border-radius: 6px;
}

/* har field-e required yek pas-zamine-ye garm migirad */
input:required {
  background-color: #fbeecb;
}

/* rang-e marz faghat ba'd az inke karbar chizi tayp kard */
input[type="email"]:not(:placeholder-shown):valid {
  border-color: #2e7d32;
}

input[type="email"]:not(:placeholder-shown):invalid {
  border-color: #c62828;
}

.terms {
  font-weight: normal;
}

/* matn-e label vaqti checkbox tik khord sabz mishavad */
.terms input:checked + span {
  color: #2e7d32;
  font-weight: bold;
}

همان فرم با راهنمای رنگی: خانهٔ خالیِ لازم با پس‌زمینهٔ گرم و مرزِ خنثی، خانهٔ پرشدهٔ درست با مرزِ سبز، و متنِ کنارِ تیکِ خوردهٔ سبز

چک کن: فرم را کامل کن — یک ایمیلِ درست بنویس و تیک را بزن. هر دو خانه بازخوردِ سبز می‌دهند و حالا دکمه فرم را می‌فرستد. یک بار هم خالی رهایش کن و دکمه را بزن تا حبابِ مرورگر را دوباره ببینی.

🤖 با AI: از دستیار AI ات بپرس «چرا خانهٔ required من از همان اول قرمز است؟». معمولاً جوابِ درستی می‌دهد، ولی گاهی راهی پیشنهاد می‌کند که با JavaScript یک class اضافه می‌کند — که برای این کار زیادی است. جوابش را با چیزی که در همین فصل دیدی بسنج: راهِ ساده‌ترِ بدونِ JavaScript همان :not(:placeholder-shown) است. AI هم اشتباه می‌کند — گاهی راه‌حلی بزرگ‌تر از مسئله می‌دهد.

واژه‌های تازهٔ این فصل#

کلمه تلفظ به حروف فارسی یعنی چه
required ریکوایِرد attribute ای که می‌گوید این خانه نباید خالی بماند
:required ریکوایِرد pseudo-class ای که به خانه‌های لازم می‌خورد
:valid ولید خانه‌ای که ورودی‌اش قانون را رعایت کرده
:invalid این‌ولید خانه‌ای که ورودی‌اش قانون را رعایت نکرده
:checked چِکد checkbox یا radio که تیک خورده باشد
:placeholder-shown پلیس‌هولدر-شون خانه‌ای که خالی است و placeholderش دیده می‌شود

تمرین‌ها

اول خودت فکر کن یا امتحان کن — بعد اینجا را باز کن.

در فصل بعد#

الان می‌توانی فرم را زنده و باخبر کنی، همه بدونِ JavaScript. فصل بعد سراغِ حرکت می‌رویم: با transform یاد می‌گیری یک عنصر را بزرگ کنی، بچرخانی یا جابه‌جا کنی — مثلاً یک کارت که با :hover کمی بزرگ می‌شود. و یک نکتهٔ ظریف را می‌بینی: transform فقط ظاهر را جابه‌جا می‌کند، نه جای واقعیِ عنصر در چیدمان.

به آخر این فصل رسیدی!

اگر ساختی و جواب داد، این دکمه مال توست.