درس ۴۳ از ۴۵ · صفحه‌های وب

حالت، DOM و زمان‌سنج‌ها

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

حالت در متغیرها نگه داشته می‌شود

صفحه‌ی تعاملی چرخه‌ای سه‌قدمی است: چیزی رخ می‌دهد، برنامه آنچه به یاد دارد را عوض می‌کند، و صفحه از روی آنچه برنامه به یاد دارد دوباره کشیده می‌شود. آنچه برنامه به یاد دارد حالت (state) آن است. در سلام حالت صفحه چند متغیر ناپایا در سطح بالاست، بالای بلوک صفحه:. رویدادگیرها آن‌ها را عوض می‌کنند و یک تابع، که معمولاً ترسیم یا نمایش نامش می‌گذارند، آن‌ها را در صفحه می‌نویسد.

web-dom-counter.salam
واردسازی دام

ناپایا شمار := 0

تابع ترسیم:
    نمایشگر := دام.با شناسه("value")
    دام.تنظیم متن(نمایشگر، "" + شمار)
    اگر شمار < 0:
        دام.تنظیم سبک(نمایشگر، "color"، "#dc2626")
    وگرنه:
        دام.تنظیم سبک(نمایشگر، "color"، "#0f172a")
    پایان
پایان

تابع بیفزا:
    شمار += 1
    ترسیم()
پایان

تابع بکاه:
    شمار -= 1
    ترسیم()
پایان

تابع صفرکن:
    شمار = 0
    ترسیم()
پایان

صفحه:
    عنوان = "شمارنده"
    سراسری: گزینشگر = "body" خانواده قلم = "Tahoma, system-ui, sans-serif" فضا = "1.5rem" پایان
    سراسری: گزینشگر = "button" فاصله = "8px 18px" اندازه قلم = "1.1rem" کادر = "1px solid #cbd5e1" گردی کادر = 8 پس زمینه = "#f8fafc" نشانگر = اشاره‌گر پایان
    سراسری: گزینشگر = "button:hover" پس زمینه = "#e0e7ff" پایان
    جعبه:
        بیشینه عرض = "16rem" فضا = "0 auto" فاصله = 20 ترازمتن = وسط کادر = "1px solid #e2e8f0" گردی کادر = 14
        سرتیتر: شناسه = "value" اندازه = 1 محتوا = "0" اندازه قلم = "4rem" فضا = "0 0 12px 0" پایان
        دکمه: شناسه = "minus" کلیک = بکاه محتوا = "-1" جهت = "ltr" پایان
        دکمه: شناسه = "plus" کلیک = بیفزا محتوا = "+1" فضا = "0 8px" جهت = "ltr" پایان
        دکمه: شناسه = "zero" کلیک = صفرکن محتوا = "از نو" پایان
    پایان
پایان

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

کمک‌تابع‌ها از واردسازی دام می‌آیند و فهرست کاملشان در صفحه‌ی بسته‌ی دام است. این درس سمت صفحه را یاد می‌دهد: برای هر کار سراغ کدام کمک‌تابع بروید و اجزا چطور کنار هم می‌نشینند.

برنامه‌ی صفحه به ریشه (main) نیازی ندارد و هیچ‌کس آن را صدا نمی‌زند. اگر یکی بنویسید مترجم فقط آنچه را که ریشه صدا می‌زند به حساب می‌آورد و رویدادگیرهای شما «تابع بی‌استفاده» گزارش می‌شوند (خطای E066). کار را در رویدادگیرها بگذارید، و برای کدی که هنگام باز شدن صفحه باید اجرا شود بخش ذخیره‌سازی را ببینید.

پیدا کردن عنصرها، خواندن و نوشتن

برای عوض کردن یک عنصر اول باید به آن دسترسی داشته باشید. دام.با شناسه("value") (در انگلیسی dom.ById) عنصری را برمی‌گرداند که این شناسه را دارد، و مترجم شناسه را برایتان وارسی می‌کند: اگر هیچ عنصری در همان صفحه آن را نداشته باشد ساختن با خطای E138 متوقف می‌شود، پس اشتباه تایپی پیش از اجرای صفحه پیدا می‌شود.

E138: no element with id 'result2' in this file's layout

این وارسی فقط شناسه‌هایی را می‌بیند که به شکل متن ساده نوشته شده‌اند. شناسه‌ای که هنگام اجرا ساخته می‌شود، مثل دام.با شناسه("votes-" + نام)، وارسی نمی‌شود و اشتباهش به شکل خطا در کنسول مرورگر پیدا می‌شود. وقتی شناسه‌ای در کار نیست، دام.پرسمان(".note") نخستین عنصری را می‌دهد که با یک گزینشگر CSS جور است و دام.پرسمان همه("#books li") همه‌شان را به شکل فهرست. فهرست را با دام.تعداد(فهرست) و دام.عنصر شماره(فهرست، i) بخوانید، با شمارش از ۰.

با دسترسی به عنصر می‌توانید بخوانید و بنویسید. دام.تنظیم متن(عنصر، "...") و دام.دریافت متن(عنصر) با متن کار دارند و دام.تنظیم مقدار و دام.دریافت مقدار با آنچه در یک کادر نوشته شده است. شمارنده‌ی نویسه‌ی زیر با هر رویداد ورودی کادر را می‌خواند، تعداد و سهم از حد مجاز را نشان می‌دهد و تا وقتی یادداشت خالی یا از حد درازتر است دکمه را خاموش نگه می‌دارد.

web-dom-charcount.salam
واردسازی دام

پایا حد := 80

تابع بسنج:
    استفاده := دام.دریافت خاصیت عددی(دام.با شناسه("note")، "textLength") برگردان صحیح۶۴
    دام.تنظیم متن(دام.با شناسه("used")، "" + استفاده + " / " + حد)
    اگر استفاده > حد:
        دام.تنظیم سبک(دام.با شناسه("used")، "color"، "#dc2626")
        دام.تنظیم سبک(دام.با شناسه("bar")، "width"، "100%")
        دام.تنظیم سبک(دام.با شناسه("bar")، "background"، "#dc2626")
    وگرنه:
        دام.تنظیم سبک(دام.با شناسه("used")، "color"، "#475569")
        دام.تنظیم سبک(دام.با شناسه("bar")، "width"، "" + استفاده * 100 / حد + "%")
        دام.تنظیم سبک(دام.با شناسه("bar")، "background"، "#4f46e5")
    پایان
    دام.تنظیم خاصیت منطقی(دام.با شناسه("post")، "disabled"، استفاده == 0 یا استفاده > حد)
پایان

صفحه:
    عنوان = "شمارنده‌ی نویسه"
    سراسری: گزینشگر = "body" خانواده قلم = "Tahoma, system-ui, sans-serif" فضا = "1.5rem" بیشینه عرض = "28rem" پایان
    متن بلند: شناسه = "note" ورودی = بسنج ردیف‌ها = 3 جانگهدار = "یادداشت کوتاهی بنویسید" عرض = "100%" فاصله = 10 اندازه قلم = "1rem" خانواده قلم = inherit اندازه جعبه = "border-box" گردی کادر = 8 کادر = "1px solid #cbd5e1" پایان
    جعبه: ارتفاع = 6 پس زمینه = "#e2e8f0" گردی کادر = 3 فضا = "6px 0"
        جعبه: شناسه = "bar" ارتفاع = "100%" عرض = "0%" پس زمینه = "#4f46e5" گردی کادر = 3 گذار = "width 0.15s" پایان
    پایان
    جعبه: نمایش = فلکس چینش = "space-between" ترازعمودی = وسط
        تکه: شناسه = "used" محتوا = "0 / 80" رنگ = "#475569" جهت = "ltr" پایان
        دکمه: شناسه = "post" غیرفعال = درست محتوا = "ارسال" فاصله = "6px 16px" پایان
    پایان
پایان

تعداد از دام.دریافت خاصیت عددی(کادر، "textLength") می‌آید، خاصیتی که مرورگر برای یک متن بلند نگه می‌دارد، و نه از نوشته.طول(). در مرورگر رشته‌ی سلام بایت‌های UTF-8 را می‌شمارد، پس کلمه‌ی سلام ۸ حساب می‌شد. دریافت خاصیت عددی، دریافت خاصیت رشته ای، دریافت خاصیت منطقی و دریافت خاصیت هر خاصیتی از عنصر را می‌خوانند، و راه رسیدن به هر چیزی‌اند که کمک‌تابع‌های دیگر پوشش نمی‌دهند.

پالایش زنده فهرستی از عنصرها می‌خواهد. رویدادگیر زیر سطرها را یکی‌یکی می‌گذراند، آن‌هایی که عبارت جست‌وجو را دارند نشان می‌دهد و بقیه را پنهان می‌کند، می‌شمارد چند تا ماند و اگر هیچ‌کدام نماند پیام می‌دهد.

web-dom-filter.salam
واردسازی دام

تابع محدود:
    عبارت := دام.دریافت مقدار(دام.با شناسه("q"))
    سطرها := دام.پرسمان همه("#books li")
    ناپایا دیده := 0
    تکرار دام.تعداد(سطرها) در ردیف:
        سطر := دام.عنصر شماره(سطرها، ردیف)
        اگر دام.دریافت متن(سطر).دربردارد(عبارت):
            دام.تنظیم سبک(سطر، "display"، "")
            دیده += 1
        وگرنه:
            دام.تنظیم سبک(سطر، "display"، "none")
        پایان
    پایان
    دام.تنظیم متن(دام.با شناسه("found")، "" + دیده + " از " + دام.تعداد(سطرها))
    اگر دیده == 0:
        دام.تنظیم سبک(دام.با شناسه("none")، "display"، "block")
    وگرنه:
        دام.تنظیم سبک(دام.با شناسه("none")، "display"، "none")
    پایان
پایان

صفحه:
    عنوان = "پالایش"
    ظاهر سفارشی: محتوا = "#books li { padding: 10px 12px; margin: 6px 0; background: #f1f5f9; border-radius: 8px }" پایان
    سراسری: گزینشگر = "body" خانواده قلم = "Tahoma, system-ui, sans-serif" فضا = "1.5rem" بیشینه عرض = "26rem" پایان
    ورودی: شناسه = "q" نوع = جستجو ورودی = محدود جانگهدار = "جست‌وجو در قفسه" عرض = "100%" فاصله = 10 اندازه قلم = "1rem" اندازه جعبه = "border-box" گردی کادر = 8 کادر = "1px solid #cbd5e1" پایان
    پاراگراف: شناسه = "found" محتوا = "6 از 6" رنگ = "#475569" فضا = "6px 0" پایان
    فهرست: شناسه = "books" سبک فهرست = هیچ فاصله = 0
        مورد: محتوا = "شازده کوچولو" پایان
        مورد: محتوا = "غرور و تعصب" پایان
        مورد: محتوا = "هابیت" پایان
        مورد: محتوا = "زنان کوچک" پایان
        مورد: محتوا = "موبی دیک" پایان
        مورد: محتوا = "آرزوهای بزرگ" پایان
    پایان
    پاراگراف: شناسه = "none" محتوا = "هیچ کتابی در قفسه با این عبارت نمی‌خواند." نمایش = هیچ رنگ = "#b45309" پایان
پایان

فهرستی که پرسمان همه می‌دهد عکس لحظه‌ی خودش است. سطرهایی که بعداً اضافه شوند در آن نیستند، پس هر بار دوباره بپرسید، همان‌طور که رویدادگیر می‌کند.

راه دیگر نوشتن دام.تنظیم اچ تی ام ال(عنصر، "...") است. مرورگر رشته را به‌عنوان HTML می‌خواند، پس می‌توان یک تکه‌ی کامل را با یک فراخوانی ساخت، و دام.تنظیم اچ تی ام ال(عنصر، "") عنصر را خالی می‌کند. خطرش هم همین است. نامی که بازدیدکننده تایپ کرده و با تنظیم اچ تی ام ال گذاشته شود به‌عنوان HTML اجرا می‌شود، و نامی مثل <img src=x onerror=...> برنامه می‌شود. متنی که از بیرون آمده همیشه با تنظیم متن گذاشته می‌شود، که هر نویسه را همان‌طور که هست نشان می‌دهد:

تابع پژواک:
    دام.تنظیم متن(دام.با شناسه("shown")، دام.دریافت مقدار(دام.با شناسه("name")))
پایان

صفحه:
    ورودی: شناسه = "name" ورودی = پژواک جانگهدار = "نام شما" پایان
    پاراگراف: شناسه = "shown" پایان
پایان

عنصری که نیست با مقدار خالی یکی نیست. دام.با شناسه و دام.پرسمان برای عنصری که وجود ندارد null جاوااسکریپت را می‌دهند و به‌کار بردنش رویدادگیر را با خطا در کنسول متوقف می‌کند. وقتی ممکن است عنصر نباشد اول بپرسید: اگر دام.وجود دارد("banner"): دام.برداشتن(دام.با شناسه("banner")) پایان.

کلاس، سبک و ویژگی

برای عوض کردن ظاهر یا رفتار یک عنصر سه ابزار هست و ترتیب ترجیح همین ترتیب زیر است. اول کلاس‌ها: دام.افزودن کلاس، دام.حذف کلاس و دام.تعویض کلاس کلاسی را روشن و خاموش می‌کنند و CSS آن یک بار در یک عنصر style نوشته می‌شود. اسکریپت می‌گوید عنصر در چه وضعی است و برگه‌ی سبک می‌گوید آن وضع چه شکلی دارد، و این دو از هم جدا می‌مانند. کلید حالت شب زیر یک کلاس روی body را عوض می‌کند و همه‌ی رنگ‌های صفحه دنبالش می‌آیند.

web-dom-theme.salam
واردسازی دام

ناپایا شبانه := نادرست

تابع بچرخان:
    دام.تعویض کلاس(دام.بدنه()، "night")
    شبانه = وارونه شبانه
    اگر شبانه:
        دام.تنظیم متن(دام.با شناسه("flip")، "رفتن به حالت روز")
    وگرنه:
        دام.تنظیم متن(دام.با شناسه("flip")، "رفتن به حالت شب")
    پایان
پایان

صفحه:
    عنوان = "حالت شب"
    ظاهر سفارشی: محتوا = "body { background: #f8fafc; color: #0f172a; transition: background 0.3s, color 0.3s } body.night { background: #0f172a; color: #e2e8f0 } body.night .card { background: #1e293b; border-color: #334155 }" پایان
    سراسری: گزینشگر = "body" خانواده قلم = "Tahoma, system-ui, sans-serif" فضا = 0 فاصله = "1.5rem" کمینه ارتفاع = "12rem" پایان
    جعبه:
        کلاس = "card"
        بیشینه عرض = "26rem" فاصله = 20 پس زمینه = سفید کادر = "1px solid #e2e8f0" گردی کادر = 14
        سرتیتر: اندازه = 2 محتوا = "عصر بخیر" فضا = "0 0 8px 0" پایان
        پاراگراف: محتوا = "یک کلاس روی بدنه‌ی صفحه همه‌ی رنگ‌ها را عوض می‌کند." پایان
        دکمه: شناسه = "flip" کلیک = بچرخان محتوا = "رفتن به حالت شب" فاصله = "8px 16px" گردی کادر = 8 کادر = "1px solid #6366f1" پس زمینه = "#6366f1" رنگ = سفید نشانگر = اشاره‌گر پایان
    پایان
پایان

دوم سبک‌ها: دام.تنظیم سبک(عنصر، "width"، "40%") یک ویژگی CSS را روی یک عنصر می‌گذارد، با نام CSS آن (background-color، نه backgroundColor)، و مقدار خالی آن را برمی‌دارد. برای مقداری مناسب است که هنگام اجرا حساب می‌کنید، مثل پهنای نوار در شمارنده‌ی نویسه. دام.دریافت سبک(عنصر، "display") مقداری را می‌دهد که مرورگر همین حالا به کار می‌برد، مثلاً none برای عنصر پنهان.

سوم ویژگی‌ها: دام.تنظیم ویژگی(عنصر، "aria-expanded"، "true") و دام.دریافت ویژگی(عنصر، "aria-expanded") ویژگی‌های HTML را به شکل متن می‌خوانند و می‌نویسند. بیشتر برای ویژگی‌های دسترس‌پذیری مهم‌اند، چون صفحه‌خوان aria-expanded را می‌خواند و به کلاس کاری ندارد. این آکاردئون هر دو را هم‌گام نگه می‌دارد: کلاس بخش را نشان یا پنهان می‌کند و ویژگی به ابزارهای کمکی می‌گوید باز است یا نه.

web-dom-accordion.salam
واردسازی دام

تابع بگردان(نام: رشته):
    سرخط := دام.با شناسه("h-" + نام)
    قاب := دام.با شناسه("p-" + نام)
    دام.تعویض کلاس(قاب، "open")
    اگر دام.دریافت ویژگی(سرخط، "aria-expanded") == "true":
        دام.تنظیم ویژگی(سرخط، "aria-expanded"، "false")
    وگرنه:
        دام.تنظیم ویژگی(سرخط، "aria-expanded"، "true")
    پایان
پایان

بخش تاشو(نام، عنوان):
    جعبه:
        کلاس = "fold"
        دکمه: شناسه = "h-" + نام باز شده دسترسی‌پذیری = "false" کلیک = (): بگردان(نام) پایان محتوا = عنوان پایان
        جعبه: شناسه = "p-" + نام کلاس = "pane"
            محتوا: پایان
        پایان
    پایان
پایان

صفحه:
    عنوان = "پرسش‌های پرتکرار"
    ظاهر سفارشی: محتوا = ".pane { display: none; padding: 0 14px 12px } .pane.open { display: block } .fold button { width: 100%; text-align: start; padding: 12px 14px; font-size: 1rem; border: 0; background: #f1f5f9; cursor: pointer; font-family: inherit } .fold button:hover { background: #e0e7ff } .fold button[aria-expanded=true] { font-weight: bold }" پایان
    سراسری: گزینشگر = "body" خانواده قلم = "Tahoma, system-ui, sans-serif" فضا = "1.5rem" بیشینه عرض = "30rem" پایان
    سراسری: گزینشگر = ".fold" کادر = "1px solid #e2e8f0" گردی کادر = 10 سرریز = پنهان فضا = "0 0 8px 0" پایان
    تاشو: نام = "ship" عنوان = "ارسال سفارش چقدر طول می‌کشد؟"
        پاراگراف: محتوا = "دو تا چهار روز کاری، در سراسر کشور." پایان
    پایان
    تاشو: نام = "back" عنوان = "آیا می‌توانم کالا را پس بدهم؟"
        پاراگراف: محتوا = "بله، تا سی روز، به شرط آنکه استفاده نشده باشد." پایان
    پایان
    تاشو: نام = "pay" عنوان = "چه روش‌هایی برای پرداخت دارید؟"
        پاراگراف: محتوا = "کارت بانکی و حواله‌ی بانکی." پایان
    پایان
پایان

آکاردئون یک بخش (component) است و رویدادگیرش نامی را که با آن ساخته شده به یاد دارد. بخش مربوط به استفاده‌ی دوباره به این برمی‌گردد. مرورگر برای همین کار عنصر details را هم دارد، بدون هیچ اسکریپتی (درس ساختار صفحه را ببینید). آکاردئون اینجاست چون حالت، کلاس و ویژگی را کنار هم نشان می‌دهد.

بعضی چیزها ویژگی نیستند و خاصیت عنصرند: checked، disabled، value. ویژگی فقط مقدار آغازین را می‌دهد و خاصیت مقدار زنده است. اینها را با دام.دریافت خاصیت منطقی و دام.تنظیم خاصیت منطقی بخوانید و بنویسید:

تابع موافقت:
    زده := دام.دریافت خاصیت منطقی(دام.با شناسه("agree")، "checked")
    دام.تنظیم خاصیت منطقی(دام.با شناسه("join")، "disabled"، وارونه زده)
پایان

صفحه:
    ورودی: شناسه = "agree" نوع = تیک تغییر = موافقت پایان
    برچسب: برای = "agree" محتوا = "قوانین را می‌پذیرم" پایان
    دکمه: شناسه = "join" غیرفعال = درست محتوا = "عضویت" پایان
پایان

زبانه‌ها هر سه ابزار را با هم به کار می‌برند. یک رویدادگیر همه‌ی زبانه‌ها و همه‌ی برگه‌ها را می‌گذراند، و آنکه شماره‌اش انتخاب شده کلاس on و aria-selected = true می‌گیرد و بقیه این دو را از دست می‌دهند.

web-dom-tabs.salam
واردسازی دام

تابع برگزین(شماره: صحیح):
    زبانه‌ها := دام.پرسمان همه(".tab")
    برگه‌ها := دام.پرسمان همه(".sheet")
    تکرار دام.تعداد(زبانه‌ها) در ردیف:
        زبانه := دام.عنصر شماره(زبانه‌ها، ردیف)
        برگه := دام.عنصر شماره(برگه‌ها، ردیف)
        اگر ردیف == شماره:
            دام.افزودن کلاس(زبانه، "on")
            دام.افزودن کلاس(برگه، "on")
            دام.تنظیم ویژگی(زبانه، "aria-selected"، "true")
        وگرنه:
            دام.حذف کلاس(زبانه، "on")
            دام.حذف کلاس(برگه، "on")
            دام.تنظیم ویژگی(زبانه، "aria-selected"، "false")
        پایان
    پایان
پایان

صفحه:
    عنوان = "نان تخت"
    ظاهر سفارشی: محتوا = ".tab { padding: 10px 18px; border: 0; border-bottom: 3px solid transparent; background: none; font-size: 1rem; font-family: inherit; cursor: pointer } .tab:hover { background: #f1f5f9 } .tab.on { border-bottom-color: #4f46e5; font-weight: bold } .sheet { display: none; padding: 14px 4px } .sheet.on { display: block }" پایان
    سراسری: گزینشگر = "body" خانواده قلم = "Tahoma, system-ui, sans-serif" فضا = "1.5rem" بیشینه عرض = "32rem" پایان
    جعبه: نقش = tablist کادر پایین = "1px solid #e2e8f0"
        دکمه: کلاس = "tab on" aria selected = "true" کلیک = (): برگزین(0) پایان محتوا = "نمای کلی" پایان
        دکمه: کلاس = "tab" aria selected = "false" کلیک = (): برگزین(1) پایان محتوا = "دستور" پایان
        دکمه: کلاس = "tab" aria selected = "false" کلیک = (): برگزین(2) پایان محتوا = "نظرها" پایان
    پایان
    جعبه: کلاس = "sheet on" محتوا = "نانی نازک که روی سنگ داغ، در حدود چهار دقیقه پخته می‌شود." پایان
    جعبه: کلاس = "sheet" محتوا = "آرد، آب، نمک و یک قاشق خمیرمایه. خمیر را یک ساعت استراحت بدهید." پایان
    جعبه: کلاس = "sheet" محتوا = "میانگین چهار و نیم ستاره از ۲۱۲ خواننده." پایان
پایان

وقتی دو چیز باید با هم عوض شوند، آن‌ها را در یک تابع بگذارید و آن را صدا بزنید، همان‌طور که برگزین می‌کند. اگر دکمه‌ی زبانه‌ها و برگه‌ها در دو جای جدا تازه می‌شدند، دیر یا زود یکی‌شان فراموش می‌شد.

ساختن و برداشتن عنصر

صفحه اغلب باید رشد کند: سطر تازه‌ای در یک فهرست، پیام تازه‌ای. دام.بساز("li") عنصری می‌سازد که هنوز در صفحه نیست، دام.تنظیم متن و دام.تنظیم ویژگی آن را پر می‌کنند و دام.افزودن(والد، فرزند) آن را به انتهای والد می‌گذارد. دام.برداشتن(عنصر) دوباره بیرونش می‌برد. فهرست کار زیر همه‌ی اینها را انجام می‌دهد.

web-dom-todo.salam
واردسازی دام

تابع بشمار:
    مانده := دام.تعداد(دام.پرسمان همه("#tasks li"))
    دام.تنظیم متن(دام.با شناسه("left")، "" + مانده + " کار مانده")
پایان

تابع ثبت(رویداد: صحیح۶۴):
    دام.جلوگیری پیش فرض(رویداد)
    خانه := دام.با شناسه("name")
    نوشته := دام.دریافت مقدار(خانه)
    اگر نوشته.طول() > 0:
        سطر := دام.بساز("li")
        عبارت := دام.بساز("span")
        دام.تنظیم متن(عبارت، نوشته)
        انجام := دام.بساز("button")
        دام.تنظیم متن(انجام، "انجام شد")
        دام.هنگام رویداد(انجام، "click"، (): دام.برداشتن(سطر) بشمار() پایان)
        دام.افزودن(سطر، عبارت)
        دام.افزودن(سطر، انجام)
        دام.افزودن(دام.با شناسه("tasks")، سطر)
        دام.تنظیم مقدار(خانه، "")
        بشمار()
    پایان
پایان

صفحه:
    عنوان = "کارهای من"
    ظاهر سفارشی: محتوا = "#tasks li { display: flex; justify-content: space-between; align-items: center; padding: 8px 12px; margin: 6px 0; background: #f1f5f9; border-radius: 8px } #tasks button { border: 0; background: #e0e7ff; border-radius: 6px; padding: 4px 10px; cursor: pointer }" پایان
    سراسری: گزینشگر = "body" خانواده قلم = "Tahoma, system-ui, sans-serif" فضا = "1.5rem" بیشینه عرض = "26rem" پایان
    فرم:
        شناسه = "new" ارسال = ثبت نمایش = فلکس شکاف = 8
        ورودی: شناسه = "name" جانگهدار = "چه کاری باید انجام شود؟" فلکس = 1 فاصله = 8 اندازه قلم = "1rem" پایان
        دکمه: شناسه = "add" نوع = ارسال محتوا = "افزودن" فاصله = "8px 16px" کادر = "0" پس زمینه = "#4f46e5" رنگ = سفید گردی کادر = 8 نشانگر = اشاره‌گر پایان
    پایان
    فهرست: شناسه = "tasks" سبک فهرست = هیچ فاصله = 0 پایان
    پاراگراف: شناسه = "left" محتوا = "0 کار مانده" رنگ = "#475569" پایان
پایان

ثبت را از بالا بخوانید. کادر را می‌خواند، یک li با یک span برای متن و یک دکمه می‌سازد، آن‌ها را به هم می‌چسباند، سطر را به فهرست می‌افزاید، کادر را خالی می‌کند و شمار را تازه می‌کند. شمار متغیر نیست: بشمار سطرهایی را می‌شمارد که روی صفحه‌اند، پس هرگز با آن‌ها ناسازگار نمی‌شود. اینجا خود صفحه حالت است، که برای عددی که خواندنش آسان است اشکالی ندارد.

دکمه‌ی تازه رویدادگیر می‌خواهد، و عنصری که هنگام نوشتن صفحه وجود ندارد نمی‌تواند ویژگی کلیک داشته باشد. دام.هنگام رویداد(عنصر، "click"، رویدادگیر) هنگام اجرا یکی می‌افزاید. رویدادگیر یک لامبدا است و متغیرهای اطرافش را به یاد دارد، پس لامبدایی که برای یک سطر ساخته شده همان سطر را برمی‌دارد. وقتی رویدادگیر باید خود رویداد را ببیند، به جایش دام.هنگام رویداد جزئی و یک لامبدا با پارامتر صحیح۶۴ به کار ببرید، مثل (رخداد: صحیح۶۴): سلام(رخداد) پایان.

شکل نمونه هم اهمیت دارد. کادر و دکمه در یک فرم با ارسال = ثبت هستند، پس کلید Enter هم مثل دکمه کار اضافه می‌کند، بی‌آنکه مدیریت کلیدی نوشته باشید. رویدادگیر دام.جلوگیری پیش فرض(رویداد) را صدا می‌زند، چون وگرنه مرورگر فرم را می‌فرستاد و صفحه را دوباره بار می‌کرد.

متن کاربر را با دام.تنظیم متن در عنصر تازه بگذارید و هرگز با تنظیم اچ تی ام ال، به دلیلی که بالا گفته شد. ساختن عنصرها یکی‌یکی نوشتنش را درازتر می‌کند، و ایمن است.

رویداد چه چیزی می‌گوید

رویدادگیری که با پارامتر صحیح۶۴ نوشته شده خود رویداد را می‌گیرد و چند کمک‌تابع آن را می‌خوانند. دام.هدف رویداد(رویداد) عنصری است که زده شد، دام.رویداد افقی و دام.رویداد عمودی مکان اشاره‌گر، دام.کلید رویداد کلید، دام.جلوگیری پیش فرض کاری را که مرورگر بعد می‌کرد لغو می‌کند و دام.توقف انتشار رویداد را از رسیدن به عنصرهای بالاتر باز می‌دارد. هر کدام درس خودشان را دارند: رویدادهای ماوس، صفحه‌کلید و فرم، کشیدن و رها کردن و لمس و رسانه و بقیه.

هدف رویداد چیزی است که اجازه می‌دهد یک رویدادگیر به چند عنصر خدمت کند. انتخاب‌گر صندلی زیر هشت دکمه‌ی یکسان دارد که با تکرار = 8 ساخته شده‌اند و نمی‌توانند هشت شناسه‌ی متفاوت داشته باشند. رویدادگیر از رویداد می‌پرسد کدام زده شد، کلاسش را برعکس می‌کند و صندلی‌هایی را که آن کلاس را دارند می‌شمارد.

web-dom-seats.salam
واردسازی دام

تابع رزرو(رویداد: صحیح۶۴):
    دام.تعویض کلاس(دام.هدف رویداد(رویداد)، "taken")
    گرفته := دام.تعداد(دام.پرسمان همه(".seat.taken"))
    دام.تنظیم متن(دام.با شناسه("summary")، "" + گرفته + " صندلی از 8 صندلی انتخاب شده است")
پایان

صفحه:
    عنوان = "صندلی‌ها"
    ظاهر سفارشی: محتوا = ".seat { width: 44px; height: 44px; border: 2px solid #94a3b8; border-radius: 8px 8px 14px 14px; background: white; cursor: pointer } .seat:hover { border-color: #4f46e5 } .seat.taken { background: #4f46e5; border-color: #4f46e5 }" پایان
    سراسری: گزینشگر = "body" خانواده قلم = "Tahoma, system-ui, sans-serif" فضا = "1.5rem" پایان
    جعبه: نمایش = شبکه ستون های شبکه = "repeat(4, 44px)" شکاف = 10 چینش = آغاز
        دکمه: تکرار = 8 کلاس = "seat" کلیک = رزرو برچسب دسترسی‌پذیری = "صندلی" پایان
    پایان
    پاراگراف: شناسه = "summary" محتوا = "0 صندلی از 8 صندلی انتخاب شده است" رنگ = "#475569" پایان
پایان

زمان‌سنج‌ها و فریم‌ها

سه کمک‌تابع تابعی را بعداً اجرا می‌کنند. دام.تنظیم مهلت(تابع، میلی‌ثانیه) یک بار پس از آن مدت اجرایش می‌کند. دام.تنظیم بازه(تابع، میلی‌ثانیه) بارها و بارها. هر دو عددی برمی‌گردانند و دام.پاک کردن مهلت(شماره) و دام.پاک کردن بازه(شماره) زمان‌سنجی را که آن شماره را دارد لغو می‌کنند. شماره را در یک متغیر سطح بالا نگه دارید که به شکل ناپایا شماره := 0 برگردان صحیح۶۴ نوشته شده، چون زمان‌سنجی که نتوانید لغوش کنید تا وقتی صفحه باز است کار می‌کند. این کمک‌تابع‌ها تابعی بی‌پارامتر می‌گیرند، پس یک لامبدا بدهید: (): تیک() پایان.

کرنومتر با فشردن «شروع» یک بازه‌ی ۱۰۰ میلی‌ثانیه‌ای راه می‌اندازد، با هر ضربه یک دهم ثانیه می‌افزاید و با فشردن «توقف» بازه را پاک می‌کند. پیام موقت همین کار را با مهلت می‌کند: پیامی نشان می‌دهد، و کلیک دوم اول مهلتی را که در انتظار بود لغو می‌کند تا پیام از آخرین کلیک دو ثانیه‌ی کامل بماند.

web-dom-stopwatch.salam
واردسازی دام

ناپایا دهم := 0
ناپایا ضربان := 0 برگردان صحیح۶۴
ناپایا روشن := نادرست

تابع ترسیم:
    دام.تنظیم متن(دام.با شناسه("time")، "" + دهم / 600 + ":" + دهم / 10 % 60 / 10 + دهم / 10 % 60 % 10 + "." + دهم % 10)
پایان

تابع تیک:
    دهم += 1
    ترسیم()
پایان

تابع آغازوپایان:
    اگر روشن:
        دام.پاک کردن بازه(ضربان)
        روشن = نادرست
        دام.تنظیم متن(دام.با شناسه("go")، "شروع")
    وگرنه:
        ضربان = دام.تنظیم بازه((): تیک() پایان، 100)
        روشن = درست
        دام.تنظیم متن(دام.با شناسه("go")، "توقف")
    پایان
پایان

تابع صفرکن:
    دهم = 0
    ترسیم()
پایان

صفحه:
    عنوان = "کرنومتر"
    سراسری: گزینشگر = "body" خانواده قلم = "Tahoma, system-ui, sans-serif" فضا = "1.5rem" پایان
    سراسری: گزینشگر = "button" فاصله = "8px 20px" اندازه قلم = "1.05rem" کادر = "1px solid #cbd5e1" گردی کادر = 8 پس زمینه = "#f8fafc" نشانگر = اشاره‌گر پایان
    جعبه:
        بیشینه عرض = "16rem" فضا = "0 auto" فاصله = 20 ترازمتن = وسط کادر = "1px solid #e2e8f0" گردی کادر = 14
        پاراگراف: شناسه = "time" محتوا = "0:00.0" اندازه قلم = "3rem" خانواده قلم = "ui-monospace, monospace" فضا = "0 0 12px 0" جهت = "ltr" پایان
        دکمه: شناسه = "go" کلیک = آغازوپایان محتوا = "شروع" پایان
        دکمه: شناسه = "clear" کلیک = صفرکن محتوا = "از نو" فضا = "0 8px 0 0" پایان
    پایان
پایان
web-dom-toast.salam
واردسازی دام

ناپایا منتظر := 0 برگردان صحیح۶۴

تابع بپوشان:
    دام.حذف کلاس(دام.با شناسه("toast")، "show")
پایان

تابع رونوشت:
    دام.پاک کردن مهلت(منتظر)
    دام.افزودن کلاس(دام.با شناسه("toast")، "show")
    منتظر = دام.تنظیم مهلت((): بپوشان() پایان، 2000)
پایان

صفحه:
    عنوان = "پیام موقت"
    ظاهر سفارشی: محتوا = "#toast { opacity: 0; transform: translateY(8px); transition: opacity 0.25s, transform 0.25s; pointer-events: none } #toast.show { opacity: 1; transform: none }" پایان
    سراسری: گزینشگر = "body" خانواده قلم = "Tahoma, system-ui, sans-serif" فضا = "1.5rem" پایان
    دکمه: شناسه = "copy" کلیک = رونوشت محتوا = "کپی پیوند" فاصله = "10px 20px" اندازه قلم = "1rem" گردی کادر = 8 کادر = "1px solid #4f46e5" پس زمینه = "#4f46e5" رنگ = سفید نشانگر = اشاره‌گر خانواده قلم = inherit پایان
    جعبه: شناسه = "toast" نقش = status محتوا = "پیوند کپی شد. این پیام تا دو ثانیه‌ی دیگر محو می‌شود." فضا = "14px 0 0 0" فاصله = "10px 14px" پس زمینه = "#0f172a" رنگ = سفید گردی کادر = 8 بیشینه عرض = "20rem" پایان
پایان

مرورگر قول نمی‌دهد بازه دقیق باشد. می‌تواند دیر اجرا شود، و زبانه‌ای که در پس‌زمینه است خیلی کمتر اجرایش می‌کند. کرنومتر ضربه‌ها را می‌شمارد، که ساده است و برای نشان دادن ایده کافی. برای ساعتی که باید درست باشد، شروع را با دام.اکنون() (میلی‌ثانیه از بازشدن صفحه) به یاد بسپارید و در هر ضربه تفاوت را نشان دهید.

برای حرکت از دام.درخواست فریم(تابع) استفاده کنید. تابع را یک بار، درست پیش از آنکه مرورگر تصویر بعدی را بکشد، اجرا می‌کند و زمان را به میلی‌ثانیه می‌دهد. برای ادامه، تابع خودش فریم بعدی را می‌خواهد. این از بازه روان‌تر است، چون صفحه‌نمایش را دنبال می‌کند. توپ زیر از روی زمان گذشته جا می‌گیرد و نه با افزودن گامی ثابت، پس روی صفحه‌ی کند و صفحه‌ی تند زمان یکسانی می‌برد.

web-dom-slide.salam
واردسازی دام

ناپایا آغاز := 0.0

تابع بلغز(زمان: اعشار۶۴):
    ناپایا چپ := (زمان - آغاز) / 4.0
    اگر چپ < 240.0:
        دام.درخواست فریم((بعدی: اعشار۶۴): بلغز(بعدی) پایان)
    وگرنه:
        چپ = 240.0
    پایان
    دام.تنظیم سبک(دام.با شناسه("ball")، "left"، "" + چپ + "px")
پایان

تابع پرتاب:
    آغاز = دام.اکنون()
    دام.درخواست فریم((زمان: اعشار۶۴): بلغز(زمان) پایان)
پایان

صفحه:
    عنوان = "لغزش"
    سراسری: گزینشگر = "body" خانواده قلم = "Tahoma, system-ui, sans-serif" فضا = "1.5rem" پایان
    جعبه: موقعیت = نسبی عرض = 280 ارتفاع = 44 پس زمینه = "#e0e7ff" گردی کادر = 22 جهت = "ltr"
        جعبه: شناسه = "ball" موقعیت = مطلق بالا = 4 چپ = 0 عرض = 36 ارتفاع = 36 پس زمینه = "#4f46e5" گردی کادر = "50%" پایان
    پایان
    دکمه: شناسه = "go" کلیک = پرتاب محتوا = "بلغزان" فضا = "14px 0 0 0" فاصله = "8px 18px" کادر = "0" پس زمینه = "#4f46e5" رنگ = سفید گردی کادر = 8 نشانگر = اشاره‌گر خانواده قلم = inherit پایان
پایان

اگر حلقه‌ای می‌خواهید که هرگز نایستد، مثل یک بازی، دام.چرخش فریم فریم‌خواستن را خودش انجام می‌دهد. اما اول درس گذار و پویانمایی را ببینید: وقتی CSS می‌تواند حرکت را انجام دهد، بگذارید او انجام دهد.

به یاد سپردن پس از بستن صفحه

متغیرها با بسته شدن صفحه از بین می‌روند. دام.تنظیم متن ذخیره("note"، نوشته) رشته‌ای را در localStorage مرورگر نگه می‌دارد، دام.دریافت متن ذخیره("note") آن را پس می‌دهد (رشته‌ی خالی وقتی چیزی ذخیره نشده)، و تنظیم عدد ذخیره و دریافت عدد ذخیره همین کار را برای عدد صحیح می‌کنند. داده روی دستگاه خواننده و فقط برای همان سایت می‌ماند تا پاک شود. هرگز رمز یا چیز خصوصی آنجا نگذارید، و آنچه را پس می‌خوانید باور نکنید: فقط یک راحتی است و بازدیدکننده می‌تواند آن را عوض کند.

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

web-dom-notes.salam
واردسازی دام

تابع ذخیره:
    نوشته := دام.دریافت مقدار(دام.با شناسه("text"))
    دام.تنظیم متن ذخیره("note"، نوشته)
    اگر دام.دریافت متن ذخیره("note") == نوشته:
        دام.تنظیم متن(دام.با شناسه("state")، "روی این دستگاه ذخیره شد.")
    وگرنه:
        دام.تنظیم متن(دام.با شناسه("state")، "این مرورگر آن را نگه نمی‌دارد.")
    پایان
پایان

تابع بارگذاری:
    ذخیره‌شده := دام.دریافت متن ذخیره("note")
    اگر ذخیره‌شده.طول() > 0:
        دام.تنظیم مقدار(دام.با شناسه("text")، ذخیره‌شده)
        دام.تنظیم متن(دام.با شناسه("state")، "یادداشت ذخیره‌شده‌ی شما بارگذاری شد.")
    وگرنه:
        دام.تنظیم متن(دام.با شناسه("state")، "هنوز چیزی ذخیره نشده است.")
    پایان
پایان

صفحه:
    عنوان = "یادداشت"
    سراسری: گزینشگر = "body" خانواده قلم = "Tahoma, system-ui, sans-serif" فضا = "1.5rem" بیشینه عرض = "28rem" پایان
    متن بلند: شناسه = "text" ورودی = ذخیره ردیف‌ها = 4 جانگهدار = "یادداشت شما" عرض = "100%" فاصله = 10 اندازه قلم = "1rem" خانواده قلم = inherit اندازه جعبه = "border-box" گردی کادر = 8 کادر = "1px solid #cbd5e1" پایان
    جعبه: نمایش = فلکس چینش = "space-between" ترازعمودی = وسط فضا = "6px 0"
        تکه: شناسه = "state" محتوا = "هنوز چیزی ذخیره نشده است." رنگ = "#475569" پایان
        دکمه: شناسه = "again" کلیک = بارگذاری محتوا = "بارگذاری یادداشت" فاصله = "6px 14px" پایان
    پایان
    اسکریپت سفارشی: محتوا = "window.addEventListener('DOMContentLoaded', function () { بارگذاری(); });" پایان
پایان

آخرین خط صفحه یک عنصر script (اسکریپت سفارشی) کوچک است. بلوک صفحه: قلاب شروعی ندارد، پس این خط از مرورگر می‌خواهد پس از خواندن صفحه بارگذاری را یک بار اجرا کند. می‌تواند نام تابع را بنویسد چون تابعی که رویدادگیر است (اینجا کلیک دکمه) جایی هم گذاشته می‌شود که مرورگر پیدایش کند. بی‌این خط، صفحه یادداشت را فقط با زدن دکمه بارگذاری می‌کرد.

هشدار، پرسش و درخواست ورودی

سه کمک‌تابع از پنجره‌های خود مرورگر استفاده می‌کنند: دام.هشدار(پیام) پیامی نشان می‌دهد، دام.تایید(پرسش) درست یا نادرست می‌دهد، و دام.درخواست ورودی(پرسش) متنی را که خواننده نوشته می‌دهد، یا رشته‌ی خالی اگر منصرف شد.

تابع بزدای:
    اگر دام.تایید("همه‌ی یادداشت‌ها پاک شوند؟"):
        دام.تنظیم متن(دام.با شناسه("notes")، "")
    پایان
پایان

صفحه:
    پاراگراف: شناسه = "notes" محتوا = "سه یادداشت" پایان
    دکمه: کلیک = بزدای محتوا = "پاک کردن همه" پایان
پایان

نوشتنشان آسان است و با آن‌ها زندگی کردن سخت. تا وقتی خواننده جواب ندهد صفحه می‌ایستد، نمی‌شود سبکشان داد، مرورگر ممکن است پیشنهاد بستنشان را بدهد، و صفحه‌ای که درون صفحه‌ی دیگری قرار گرفته اغلب اجازه‌ی نشان دادنشان را ندارد، که در این صورت تایید بی‌صدا نادرست جواب می‌دهد. برای آزمودن اولیه‌ی یک ایده به کار ببریدشان، و در صفحه‌ی نهایی از عنصر dialog (ساختار صفحه) یا پیامی درون صفحه، مثل پیام موقت بالا، استفاده کنید.

بخش‌ها، تکرار و رویدادگیرها

بخش (component) تکه‌ای از صفحه است که چند بار می‌توانید به کارش ببرید و رویدادگیرهایش می‌توانند از پارامترهای بخش استفاده کنند. در نظرسنجی زیر لامبدای درون گزینه تابع رای(نام) را با نامی که نسخه‌ی خودش با آن ساخته شده صدا می‌زند، و شمارنده شناسه‌ای دارد که از همین نام ساخته شده، پس هر نسخه رأی‌های خودش را می‌شمارد.

web-dom-poll.salam
واردسازی دام

تابع رای(نام: رشته):
    جمع := دام.با شناسه("votes-" + نام)
    دام.تنظیم متن(جمع، "" + (دام.دریافت متن(جمع).به صحیح() + 1))
پایان

بخش گزینه(نام):
    جعبه:
        کلاس = "choice"
        دکمه: کلیک = (): رای(نام) پایان محتوا = "رأی به " + نام پایان
        تکه: شناسه = "votes-" + نام محتوا = "0" پایان
    پایان
پایان

صفحه:
    عنوان = "نظرسنجی"
    سراسری: گزینشگر = "body" خانواده قلم = "Tahoma, system-ui, sans-serif" فضا = "1.5rem" بیشینه عرض = "26rem" پایان
    سراسری: گزینشگر = ".choice" نمایش = فلکس چینش = "space-between" ترازعمودی = وسط فاصله = "8px 12px" فضا = "0 0 8px 0" پس زمینه = "#f1f5f9" گردی کادر = 10 پایان
    سراسری: گزینشگر = ".choice button" فاصله = "6px 14px" کادر = "1px solid #6366f1" گردی کادر = 8 پس زمینه = سفید رنگ = "#4338ca" نشانگر = اشاره‌گر خانواده قلم = inherit پایان
    سراسری: گزینشگر = ".choice span" اندازه قلم = "1.4rem" وزن قلم = پررنگ رنگ = "#0f172a" پایان
    سرتیتر: اندازه = 2 محتوا = "نوشیدنی مورد علاقه‌ی شما؟" فضا = "0 0 12px 0" پایان
    گزینه: نام = "چای" پایان
    گزینه: نام = "قهوه" پایان
    گزینه: نام = "آبمیوه" پایان
پایان

دو محدودیت را باید دانست. لامبدا در ویژگی صفحه پارامتر رویداد ندارد، پس وقتی رویدادگیر رویداد لازم دارد تابعی با نام بنویسید که پارامتر داشته باشد، همان‌طور که انتخاب‌گر صندلی می‌کند. و صفحه‌ی ساده ثابت است: تکرار = 8 هشت نسخه‌ی یکسان می‌سازد، اما هر (each) و اگر به مقدارهای زمان اجرا نیاز دارند و فقط در صفحه‌ای با پارامتر کار می‌کنند که سرور پرش می‌کند (وگرنه خطای E133). آن نوع صفحه در نخستین درس این بخش آمده است، و شناسه‌هایش در هر نسخه باید فرق کنند، پس آنجا از کلاس یا هدف رویداد استفاده کنید.

کنار هم گذاشتن: یک پرسش و پاسخ

این یکی بزرگ‌تر است و از هر چیزی کمی دارد. حالت سه متغیر است: کدام پرسش دیده می‌شود، امتیاز، و اینکه به پرسش فعلی جواب داده شده یا نه. پرسش‌ها در نشانه‌گذاری نیستند. یک تابع پرسش و پاسخ‌هایش را به شکل یک رشته می‌دهد، بشکاف("|") آن را تکه‌تکه می‌کند و نمایش‌کارت تکه‌ها را در صفحه می‌ریزد. افزودن یک پرسش یعنی افزودن یک سطر.

web-dom-quiz.salam
واردسازی دام

ناپایا پرسیده := 0
ناپایا امتیاز := 0
ناپایا قفل := نادرست

تابع کارت(شماره: صحیح): رشته:
    ترابرد شماره:
        0: برگشت "کدام عنصر بزرگ‌ترین سرتیتر است؟|h1|h6|header|0" پایان
        1: برگشت "کدام ویژگی به تصویر متن جایگزین می‌دهد؟|alt|src|title|0" پایان
        وگرنه: برگشت "کدام رویداد با هر تایپ در یک کادر رخ می‌دهد؟|click|input|load|1" پایان
    پایان
    برگشت ""
پایان

تابع نمایش‌کارت:
    اجزا := کارت(پرسیده).بشکاف("|")
    دام.تنظیم متن(دام.با شناسه("ask")، اجزا.بگیر(0))
    تکرار 3 در k:
        دام.تنظیم متن(دام.با شناسه("c" + k)، اجزا.بگیر(k + 1))
        دام.تنظیم ویژگی(دام.با شناسه("c" + k)، "class"، "choice")
    پایان
    دام.تنظیم متن(دام.با شناسه("verdict")، "یک پاسخ انتخاب کنید.")
    قفل = نادرست
پایان

تابع انتخاب(k: صحیح):
    اگر وارونه قفل:
        قفل = درست
        درست‌ها := کارت(پرسیده).بشکاف("|").بگیر(4).به صحیح()
        اگر k == درست‌ها:
            امتیاز += 1
            دام.افزودن کلاس(دام.با شناسه("c" + k)، "right")
            دام.تنظیم متن(دام.با شناسه("verdict")، "درست است.")
        وگرنه:
            دام.افزودن کلاس(دام.با شناسه("c" + k)، "wrong")
            دام.افزودن کلاس(دام.با شناسه("c" + درست‌ها)، "right")
            دام.تنظیم متن(دام.با شناسه("verdict")، "این بار نه.")
        پایان
    پایان
پایان

تابع بعدی:
    اگر پرسیده == 3:
        پرسیده = 0
        امتیاز = 0
        دام.تنظیم متن(دام.با شناسه("next")، "بعدی")
        نمایش‌کارت()
    وگرنه قفل:
        پرسیده += 1
        اگر پرسیده == 3:
            دام.تنظیم متن(دام.با شناسه("verdict")، "امتیاز شما " + امتیاز + " از 3 است.")
            دام.تنظیم متن(دام.با شناسه("next")، "بازی دوباره")
        وگرنه:
            نمایش‌کارت()
        پایان
    پایان
پایان

صفحه:
    عنوان = "پرسش و پاسخ"
    ظاهر سفارشی: محتوا = ".choice { display: block; width: 100%; margin: 6px 0; padding: 10px 14px; text-align: start; font-size: 1rem; font-family: inherit; border: 1px solid #cbd5e1; border-radius: 8px; background: white; cursor: pointer } .choice:hover { background: #eef2ff } .choice.right { background: #dcfce7; border-color: #16a34a } .choice.wrong { background: #fee2e2; border-color: #dc2626 }" پایان
    سراسری: گزینشگر = "body" خانواده قلم = "Tahoma, system-ui, sans-serif" فضا = "1.5rem" بیشینه عرض = "28rem" پایان
    سرتیتر: شناسه = "ask" اندازه = 3 محتوا = "کدام عنصر بزرگ‌ترین سرتیتر است؟" پایان
    دکمه: شناسه = "c0" کلاس = "choice" کلیک = (): انتخاب(0) پایان محتوا = "h1" جهت = "ltr" پایان
    دکمه: شناسه = "c1" کلاس = "choice" کلیک = (): انتخاب(1) پایان محتوا = "h6" جهت = "ltr" پایان
    دکمه: شناسه = "c2" کلاس = "choice" کلیک = (): انتخاب(2) پایان محتوا = "header" جهت = "ltr" پایان
    پاراگراف: شناسه = "verdict" محتوا = "یک پاسخ انتخاب کنید." رنگ = "#475569" پایان
    دکمه: شناسه = "next" کلیک = بعدی محتوا = "بعدی" فاصله = "8px 18px" کادر = "0" پس زمینه = "#4f46e5" رنگ = سفید گردی کادر = 8 نشانگر = اشاره‌گر پایان
پایان

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

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

وقتی کار نمی‌کند

  • کنسول مرورگر را باز کنید (F12 و بعد Console). خطای یک رویدادگیر با شماره‌ی سطرش آنجا می‌آید و روی صفحه چیزی نمی‌آید. بیشتر مشکل‌های زیر در آن دیده می‌شوند.
  • آنچه می‌خواهید بدانید چاپ کنید. سرچاپ "کلیک‌ها:"، شمار در یک رویدادگیر مثل console.log در کنسول می‌نویسد. ساده‌ترین راه برای دیدن این است که رویدادگیر اصلاً اجرا می‌شود یا نه و حالت چیست.
  • با کلیک چیزی نمی‌شود. ببینید نام تابع نامی که مرورگر از پیش دارد (reset، open، close، focus؛ یادداشت درس ماوس را ببینید) نباشد، و ببینید در پرونده ریشه نیست. فراخوانی با پرانتز، کلیک = بیشتر()، شکست بی‌صدا نیست: مترجم با خطای E137 رد می‌کند، چون ویژگی خود تابع را می‌خواهد.
  • «Cannot read properties of null». شناسه یا گزینشگری با هیچ چیز جور نشد. آن را با صفحه مقایسه کنید و یادتان باشد شناسه‌هایی که هنگام اجرا ساخته می‌شوند را مترجم وارسی نمی‌کند.
  • صفحه عوض می‌شود و بعد برمی‌گردد. فرمی فرستاده شد. در رویدادگیر ارسال آن دام.جلوگیری پیش فرض(رویداد) را صدا بزنید.
  • فهرستی کهنه شده. نتیجه‌ی پرسمان همه عکس لحظه است. پس از افزودن یا برداشتن عنصرها دوباره بپرسید.
  • متن فارسی اشتباه شمرده می‌شود. رشته.طول() در مرورگر بایت می‌شمارد. برای آنچه خواننده می‌بیند از textLength مثل بالا استفاده کنید.
ناپایا کلیک‌ها := 0

تابع بیشتر:
    کلیک‌ها += 1
    سرچاپ "کلیک‌ها:"، کلیک‌ها
پایان

صفحه:
    دکمه: کلیک = بیشتر محتوا = "کلیک کنید و کنسول را ببینید" پایان
پایان

هر کاری که صفحه در مرورگر می‌کند را خواننده می‌تواند ببیند و عوض کند. هر چه اهمیت دارد، مثل قیمت، دسترسی و حد مجاز، را روی سرور دوباره بررسی کنید. نخستین درس این بخش نشان می‌دهد برنامه‌ی سلام چطور صفحه را می‌فرستد، و صفحه‌ی بسته‌ی دام هر کمک‌تابع را با امضایش فهرست کرده است.

خودتان امتحان کنید

  • فهرست کار را وادار کنید کارهایش را نگه دارد: هنگام عوض شدن فهرست متن هر کار را با تنظیم متن ذخیره ذخیره کنید و هنگام باز شدن صفحه پس بخوانید، مثل نمونه‌ی یادداشت.
  • یک پرسش چهارم به پرسش و پاسخ اضافه کنید و زیر امتیاز نهایی سطری بگذارید که بگوید چند پاسخ پشت سر هم درست بوده است.
  • کرنومتر را به شمارش معکوس تبدیل کنید: از ۳۰ ثانیه شروع کنید، در صفر بایستید و با دام.تنظیم مهلت یا همان بازه وقتی زمان تمام شد پیامی نشان دهید.
  • آکاردئون را طوری کنید که با باز شدن یک بخش بقیه بسته شوند و همیشه فقط یکی باز باشد. با پرسمان همه همه را ببینید.