در این درس میخوانید
تا اینجا بیشتر با مقدارها و منطق زبان کار کردهایم. اکنون میخواهیم نتیجه روی صفحه دیده شود. مرورگر از سند HTML یک مدل درختی میسازد که DOM نام دارد؛ JavaScript از راه APIهای مرورگر میتواند بخشهایی از آن را بخواند یا تغییر دهد. DOM خود زبان JavaScript نیست و در Node.js خام نیز همان document مرورگر وجود ندارد. هدف درس دهم، نمایش یک فهرست آموزشی بدون ساختن HTML از متن ورودی است.
پروژهٔ دو فایلی را آماده کنید
فایلهای زیر را کنار هم قرار دهید. ابتدا HTML ساختار ظرف فهرست و پیام وضعیت را میسازد. script با defer پس از تحلیل سند اجرا میشود؛ بنابراین عنصرهای مورد نیاز در زمان اجرا موجودند. برای نام شناسهها از رشتهٔ ثابت و یکتا استفاده میکنیم. اگر یک نام را تغییر دادید، selector مربوط به آن در app.js هم باید بهروز شود.
<!doctype html>
<html lang="fa" dir="rtl">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>فهرست درسها</title>
<script src="./app.js" defer></script>
</head>
<body>
<h1>مسیر یادگیری</h1>
<p id="status" aria-live="polite"></p>
<ul id="lesson-list"></ul>
</body>
</html>انتخاب عنصر و برخورد با نبود نتیجه
querySelector نخستین عنصر مطابق selector را میگیرد و اگر پیدا نکند، null برمیگرداند. querySelectorAll یک فهرست NodeList از نتیجهها میدهد. پیش از استفاده، وجود عنصرهای ضروری را بررسی کنید تا خطایی مانند Cannot set properties of null را بهتر تشخیص دهید. بیشتر وقتها مشکل از نام اشتباه شناسه، صفحهٔ نادرست یا زمان اجرای اسکریپت است. برای یک عنصر ضروری، بازگشت بیصدای تابع میتواند ایراد پروژه را پنهان کند؛ پیام خطای مشخص هنگام توسعه مفیدتر است.
const list = document.querySelector("#lesson-list");
const status = document.querySelector("#status");
if (!list || !status) {
throw new Error("Required page elements are missing");
}این مثال به محیط مرورگر، پروژه یا Node.js نیاز دارد؛ طبق راهنمای درس اجرا کنید.ساخت فهرست با Nodeهای واقعی
کد بعدی را در ادامهٔ app.js بگذارید. createElement یک عنصر تازه میسازد، textContent عنوان را بهصورت متن درج میکند و replaceChildren محتوای ظرف را با نسخهٔ تازه عوض میکند. DocumentFragment یک ظرف موقت برای کنار هم گذاشتن Nodeهاست؛ در این مثال خوانایی ساخت و درج فهرست را بهتر میکند. این الگو تضمین نمیکند هر رابطی سریعتر میشود؛ اندازهٔ داده و تعداد بهروزرسانیها نیز مهماند.
const titles = ["متغیرها", "تابعها", "DOM"];
function renderLessons(items) {
const fragment = document.createDocumentFragment();
for (const [index, title] of items.entries()) {
const li = document.createElement("li");
li.textContent = title;
li.dataset.index = String(index);
fragment.append(li);
}
list.replaceChildren(fragment);
status.textContent = items.length > 0
? `${items.length} درس آمادهٔ مطالعه است`
: "هنوز درسی اضافه نشده است";
}
renderLessons(titles);این مثال به محیط مرورگر، پروژه یا Node.js نیاز دارد؛ طبق راهنمای درس اجرا کنید.باید سه عضو فهرست و پیام تعداد را ببینید. دوباره renderLessons را صدا بزنید؛ اعضا نباید دو برابر شوند، چون محتوای قبلی جایگزین میشود. اگر از append بدون پاککردن ظرف در هر بار render استفاده کنید، نتیجهٔ تکراری ایجاد میشود. اکنون renderLessons([]) را اجرا کنید؛ فهرست باید خالی و پیام متناسب نمایش داده شود. این حالت خالی، بخشی از رفتار واقعی رابط است.
classList برای تغییر حالت ظاهری
بهجای ساختن رشتههای طولانی برای className، میتوانید classList.add، remove یا toggle را به کار ببرید. دادهٔ برنامه و ظاهر را جدا نگه دارید: مثلاً وضعیت تکمیل در مدل داده باشد و کلاس CSS فقط نمایش آن را تعیین کند. dataset برای دادهٔ کوچکِ مرتبط با عنصر مفید است، اما محل نگهداری رمز، توکن یا دادهٔ حساس نیست. هر چیزی که در DOM قرار میگیرد، در مرورگر قابل مشاهده و تغییر است.
const firstLesson = document.querySelector("#lesson-list li");
if (firstLesson) {
firstLesson.classList.add("is-complete");
firstLesson.setAttribute("title", "این درس مطالعه شده است");
}این مثال به محیط مرورگر، پروژه یا Node.js نیاز دارد؛ طبق راهنمای درس اجرا کنید.textContent و innerHTML را آگاهانه انتخاب کنید
وقتی داده باید بهصورت متن دیده شود، textContent مناسب است. innerHTML یک رشته را بهعنوان HTML پردازش میکند؛ درج متن کنترلنشده با آن میتواند خطر XSS ایجاد کند. Template Literal نیز داده را پاکسازی نمیکند. اگر روزی نمایش HTML واقعی لازم شد، باید سیاست معتبر پاکسازی و کنترل منبع داشته باشید. برای نام درس، پیام و نتیجهٔ جستوجوی این مجموعه نیازی به تفسیر HTML وجود ندارد.
تمرین و پاسخ پیشنهادی
به هر درس یک پیشوند شماره اضافه کنید و حالت بدون داده را آزمایش کنید. داخل تابع renderLessons، خط تعیین textContent را با نمونهٔ زیر جایگزین کنید. نتیجه باید «1. متغیرها»، «2. تابعها» و «3. DOM» باشد. سپس یک عنوان شامل علامتهای < و > اضافه کنید؛ متن باید همانطور دیده شود و عنصر HTML تازه نسازد. پس از تازهکردن صفحه، وضعیت از دادهٔ فایل دوباره ساخته میشود؛ ذخیرهسازی را بعداً یاد میگیریم.
// جایگزین خط li.textContent در همان حلقه:
li.textContent = `${index + 1}. ${title}`;بازبینی نمونه
- script بعد از آمادهشدن ساختار لازم اجرا میشود.
- عنصر ضروری پیدا نشده را با پیام واضح تشخیص میدهیم.
- رندر دوباره، عضوهای تکراری نمیسازد.
- دادهٔ متنی بهعنوان HTML تفسیر نمیشود.
- برای فهرست خالی، پیام روشن داریم.
منابع انگلیسی
- MDN — Document Object Model
- https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model
- MDN — Document.createElement
- https://developer.mozilla.org/en-US/docs/Web/API/Document/createElement
- MDN — Node.textContent
- https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent
این نمونهٔ فهرست آموزشی برای درسآوند نوشته شده است. درس بعد، کلیک، ارسال فرم و واکنش صفحه به رویدادهای کاربر را اضافه میکند.
پیشنیاز و محیط اجرای مثالها
درس 10 از ۲۰؛ پیشنیاز: درسهای قبلی مجموعه. این درس در مرورگر اجرا میشود. دو فایل index.html و app.js بسازید و فایل JS را با defer به HTML وصل کنید؛ document در Node.js خام وجود ندارد.
پیشرفت روی همین مرورگر ذخیره میشود.