MakeFirstStep· Frontend + зеҳни сунъӣ

Дарси 15 · Async ва API

Кор бо API-и воқеӣ

Дӯкони интернетӣ ҳазорон молро дар HTML нигоҳ намедорад — вай онҳоро аз сервер мепурсад: «шаш молро нишон деҳ, дувоздаҳи аввалро гузарон, танҳо аз категорияи «телефонҳо»». Имрӯз шумо меомӯзед, ки чунин саволҳоро тавассути параметрҳои дархост диҳед ва ҷавобро ба рӯйхати зинда бо ҷустуҷӯ, филтрҳо ва саҳифаҳо табдил диҳед — каталогҳо, лентаҳо ва ҷустуҷӯ дар сайтҳои воқеӣ маҳз ҳамин тавр сохта шудаанд.

  • Суроғаи дархостро тавассути URLSearchParams ҷамъ кардан, бе андеша дар бораи фосилаҳо ва кириллитса
  • Ҳуҷҷатгузории API-ро хондан ва дар ҷавоб майдонҳои лозимаро ёфтан
  • Аз ҷавоби сервер рӯйхат кашидан, бе такрори кортҳо
  • Ҷустуҷӯро бе дархостҳои зиёдатӣ анҷом додан: debounce ва ҳимоя аз «мусобиқаи» дархостҳо
  • Филтр ва мураттаб кардан — дар сервер ва дар клиент
  • Саҳифаҳоро аз рӯи limit / skip ҳисоб кардан ва тугмаҳои «Ба қафо» / «Ба пеш»-ро идора кардан
Бо ин саҳифа чӣ тавр кор кардан лозим

Коди дар заминаи торик бо тугмаи ▶ Иҷро кардан зинда аст: тағйир диҳед ва иҷро кунед. Мисолҳо бо fetch ба API-ҳои оммавии воқеӣ мераванд, барои ҳамин интернет лозим аст. Блокҳо бо ҷадвалҳои index.html / style.css / script.js — ин саҳифаи хурд аст: дар тарафи чап код, дар зери он пешнамоиш ва консол. Вазифаҳо худашон аз рӯи он ки шумо ба консол чоп мекунед, санҷида мешаванд. Агар ба сервер «бад» бошад (ҳолати нодир), хатои шабакаро мебинед — оддӣ боз як бор иҷро кунед. Ишораҳо зери қулфак 🔒: аввал худатон, баъд ишора.

Дар дарс се API-и оммавӣ бе калид ҳаст: DummyJSON (мол), Rick and Morty API (персонажҳо) ва андаке Open-Meteo (обу ҳаво) дар вазифаи хонагӣ.

01 Параметрҳои дархост: URL ва URLSearchParams

Ҳар дархост ба API — ин суроға аст. Аммо суроға на танҳо «ба куҷо рафтан»-ро, балки «чиро дақиқан овардан»-ро ҳам медонад. Барои ин баъд аз аломати ? параметрҳои дархост (query string) дар намуди ҷуфтҳои калид=қимат, ки бо & ҷудо шудаанд, илова мекунанд.

https://dummyjson.com/products/search?q=phone&limit=5&skip=10
Роҳ ба саволи «чӣ?» ҷавоб медиҳад, параметрҳо — «чандто, кадомҳо, бо кадом тартиб?»
Қиёс аз ҳаёт

Фармоиш дар чойхона. «Курутоб» — ин роҳ аст: шумо чиро фармоиш медиҳед. «Бе piyoz, ҳиссаи калонтар, ду коса чой» — ин параметрҳо аст: возеҳкунӣ ба ҳамон хӯрок. Хӯрок як аст, фармоишҳо гуногунанд. Бо API ҳам чунин аст: роҳи /products як аст, параметрҳои limit, skip, q бошанд тағйир медиҳанд, ки ба шумо дақиқан чӣ меоранд.

Чаро сатрҳоро бо даст часпондан мумкин нест

Навомӯзон суроғаро ҳамин тавр ҷамъ мекунанд: "…?q=" + text + "&limit=" + limit. То даме ки дар text як калима бо лотинӣ бошад, ҳама чиз кор мекунад. Аммо ҳамин ки корбар фосила, &, # ё ҳарфи русӣ ворид кунад — суроға вайрон мешавад. URL қоидаҳои сахт дорад: чунин аломатҳоро бояд рамзгузорӣ кард (фосила → + ё %20, & → %26, кириллитса → %D0…).

Худамон рамзгузорӣ кардан — мушкил аст ва хато кардан осон. Барои ин дар браузер (ва дар Node.js) синфи тайёри URLSearchParams ҳаст.

URLSearchParams ҷамъ ва рамзгузорӣ мекунад · Ctrl + Enter
const params = new URLSearchParams();
params.set("q", "плов & чай");   // фосилаҳо, &, кириллитса — ташвиши мо нест
params.set("limit", 5);          // рақам худаш ба сатр табдил меёбад
params.set("skip", 10);

console.log(params.toString());
// q=%D0%BF%D0%BB%D0%BE%D0%B2+%26+%D1%87%D0%B0%D0%B9&limit=5&skip=10

const url = "https://dummyjson.com/products/search?" + params;
console.log(url);   // объект худкор ба сатр табдил меёбад

URLSearchParams-ро фавран аз объект ё аз сатри тайёр сохтан мумкин аст — барои таҷзияи суроғаи бегона қулай аст:

Аз объект ва аз сатр · Ctrl + Enter
const fromObject = new URLSearchParams({ q: "phone", limit: 5 });
console.log(fromObject.toString());

const fromString = new URLSearchParams("q=plov&limit=10&page=3");
console.log(fromString.get("q"));        // plov
console.log(fromString.get("limit"));    // "10" — сатр аст, на рақам!
console.log(fromString.get("sort"));     // null — чунин параметр нест
console.log(fromString.has("page"));     // true
set(калид, қимат)
Параметрро муқаррар кардан. Агар чунин аллакай буд — бознависӣ кардан.
append(калид, қимат)
Боз як ҷуфт илова кардан, ҳатто агар калид аллакай бошад (чунин мешавад: ?tag=a&tag=b).
get(калид) / getAll(калид)
Қиматро хондан. Ҳамеша сатр ё null.
has(калид) / delete(калид)
Мавҷуд будан санҷидан / параметрро нест кардан.
toString()
Сатри тайёри параметрҳо бе аломати ?.

Суроғаи пурра: URL

Барои таҷзияи суроғаи пурра синфи URL ҳаст: он searchParams дорад — ҳамон URLSearchParams, ки аллакай ба суроға пайваст шудааст.

Таҳрир кардан мумкин аст · Ctrl + Enter
const url = new URL("https://dummyjson.com/products/search?q=phone&limit=5");

console.log(url.hostname);                  // dummyjson.com
console.log(url.pathname);                  // /products/search
console.log(url.searchParams.get("q"));     // phone

url.searchParams.set("skip", 10);           // суроғаро тавассути параметрҳо тағйир додем
console.log(url.href);
Хатоҳои маъмул

1. params.get("limit") + 1 "101" медиҳад, на 11: қиматҳо сатранд. Number(...) лозим аст. 2. Часпондани сатрҳо дар фосилаҳо ва & вайрон мешавад. 3. Санҷиши if (value) ҳангоми ҷамъ кардани суроға skip=0-ро мепартояд — нул «дурӯғин» аст, аммо барои сервер ин қимати муҳим аст. === undefined-ро санҷед, на «ростнамоӣ»-ро.

Часпондани сатрҳо суроғаро вайрон мекунад · Ctrl + Enter
const text = "rock & roll";
const bad = "https://dummyjson.com/products/search?q=" + text + "&limit=5";

console.log(bad);
// сервер мебинад: q="rock ", параметри " roll" бе қимат ва limit=5 — ҷустуҷӯ чизи дигарро мекунад
console.log(new URL(bad).searchParams.get("q"));

Муфассалтар хонед: learn.javascript.ru — Объекти URL · MDN — URLSearchParams

Амал: мустаҳкам мекунем

Суроғаҳоро ҷамъ ва таҷзия мекунем. Ҳамаи вазифаҳо бо чопи консол санҷида мешаванд.

ВАЗИФАИ 1.1

Се параметр барои ҷустуҷӯ

осон

URLSearchParams созед ва тавассути set се параметр муқаррар кунед: q = samsa, limit = 5, skip = 10. Сатри ниҳоии параметрҳоро чоп кунед.

Таҳрир кардан мумкин аст · Ctrl + Enter
const params = new URLSearchParams();

// q, limit ва skip-ро муқаррар кунед

// params.toString()-ро чоп кунед
Натиҷаи интизоршаванда
q=samsa&limit=5&skip=10
Ишора 1

params.set("q", "samsa"); — барои ҳар параметр як сатр, бо ҳамон тартибе, ки дар ҷавоб аст.

Ишора 2

Рақамҳоро чунон ки ҳастанд додан мумкин: params.set("limit", 5) — синф худаш онҳоро ба сатр табдил медиҳад.

ВАЗИФАИ 1.2

Чопро пешгӯӣ кун: сатрҳо ва рақамҳо

осон

Аввал, бе иҷро кардан, пешгӯиро барои панҷ сатри аввал нависед. Иҷро кунед ва муқоиса намоед. Баъд дар поён анҷом диҳед: тавассути append қимати дуюми limit = 20-ро илова кунед ва getAll("limit")-ро бо вергул чоп кунед (.join(",")).

Таҳрир кардан мумкин аст · Ctrl + Enter
const params = new URLSearchParams("q=plov&limit=10&page=3");

console.log(params.get("limit") + 1);
console.log(Number(params.get("limit")) + 1);
console.log(params.get("sort"));
console.log(params.has("page"));
console.log(params.get("page") * 2);

// қисми шумо: append + getAll
Натиҷаи интизоршаванда
101
11
null
true
6
10,20
Ишора 1

Плюс бо сатр — часпонидан аст: "10" + 1 ин "101" аст. Аммо зарб худаш сатрро ба рақам табдил медиҳад.

Ишора 2

params.append("limit", 20); ва console.log(params.getAll("limit").join(","));

ВАЗИФАИ 1.3

Функсияи buildUrl

миёна

buildUrl(base, params)-ро нависед: вай суроға ва объекти параметрҳоро мегирад ва суроғаи тайёрро бармегардонад. Параметрҳо бо қимати undefined, null ё сатри холӣ гузаронда мешаванд; 0 — гузаронда намешавад. Агар параметрҳо намонанд, аломати ? илова намешавад.

Таҳрир кардан мумкин аст · Ctrl + Enter
function buildUrl(base, params) {
  // коди шумо
}

console.log(buildUrl("https://dummyjson.com/products/search", { q: "phone", limit: 5, skip: 0 }));
console.log(buildUrl("https://dummyjson.com/products", { limit: 10, skip: undefined, q: "" }));
console.log(buildUrl("https://dummyjson.com/products", {}));
Натиҷаи интизоршаванда
https://dummyjson.com/products/search?q=phone&limit=5&skip=0
https://dummyjson.com/products?limit=10
https://dummyjson.com/products
Ишора 1

Object.entries(params)-ро бо давраи for…of тай кунед, қиматҳои баддро бо continue гузаронед, хубҳоро ба URLSearchParams гузоред.

Ишора 2

Шарти гузаронидан: value === undefined || value === null || value === "". Дар охир: const text = query.toString(); return text ? `${base}?${text}` : base;

ВАЗИФАИ 1.4

Хаторо ёб: ҷустуҷӯи «rock & roll»

осон

Корбар rock & roll-ро ҷустуҷӯ кард, аммо сервер чизи аҷибе гирифт. Ҷамъкунии суроғаро тавассути URLSearchParams бознависед (параметрҳои q ва limit=5). Суроғаро чоп кунед ва дар сатри дуюм — он чиро, ки new URL(url).searchParams.get("q") бармегардонад: ин ҳамон чизест, ки сервер мебинад.

Таҳрир кардан мумкин аст · Ctrl + Enter
const query = "rock & roll";
const url = "https://dummyjson.com/products/search?q=" + query + "&limit=5";

console.log(url);
console.log(new URL(url).searchParams.get("q"));
Натиҷаи интизоршаванда
https://dummyjson.com/products/search?q=rock+%26+roll&limit=5
rock & roll
Ишора 1

new URLSearchParams({ q: query, limit: 5 })-ро созед.

Ишора 2

const url = "https://dummyjson.com/products/search?" + params; — объект худаш ба сатр табдил меёбад.

02 Ҳуҷҷатгузорӣ ва ҷавоби API-ро мехонем

Ҳар API қоидаҳои худро дорад: параметрҳо чӣ ном доранд, ҷавоб чӣ тавр сохта шудааст, агар ҳеҷ чиз ёфт нашавад чӣ мешавад. Инро танҳо аз ҳуҷҷатгузорӣ ва аз худи ҷавоб фаҳмидан мумкин аст. Одати №1-и барномасоз: аввал бубинед, ки воқеан чӣ меояд ва танҳо баъд код нависед.

Аз дарси 14 шумо печонандаи болои fetch-ро дар хотир доред. Вай дар ҳар мисол лозим мешавад, барои ҳамин версияи кӯтоҳи вай инҷост — дар дарс вай дар аввали ҳар блок меистад:

Ҷавоби DummyJSON чӣ гуна менамояд · Ctrl + Enter
async function getJson(url) {
  const response = await fetch(url);
  if (!response.ok) throw new Error("HTTP " + response.status);
  return response.json();
}

async function main() {
  const data = await getJson("https://dummyjson.com/products?limit=2&select=title,price");
  console.log(Object.keys(data));
  console.log(data.products);
  console.log("всего товаров на сервере больше, чем пришло:", data.total > data.products.length);
}
main();

Мебинед: ҷавоб — на оддӣ массиви мол, балки объект-лифофа аст. Худи мол дар products ҷойгир аст, дар паҳлӯи он — майдонҳои хидматии total (ҳамагӣ чандто), skip ва limit (кадом қисмро баргардонданд). Онҳо барои пагинатсия лозим мешаванд.

DummyJSONRick and Morty APIJSONPlaceholder
Рӯйхат дар куҷостdata.productsdata.resultsхуди ҷавоб — массив аст
Ҷустуҷӯ/products/search?q=?name=не, танҳо филтрҳо аз рӯи майдонҳо: ?userId=1
Саҳифаҳоlimit + skippage (20 тоӣ)_page + _limit
Ҳамагӣ чандтоdata.totaldata.info.count, info.pagesсарлавҳаи x-total-count
Чизе ёфт нашуд200 ва products-и холӣ404 ва { error }200 ва массиви холӣ

Сатри охирин — аз ҳама маккор: «чизе ёфт нашуд» дар як API ин рӯйхати холӣ аст, дар дигар — хатои 404. Агар инро надонед, ҷустуҷӯ аз рӯи Рик ва Морти дар ҳар хатои имлоӣ «меафтад».

404 — на ҳамеша вайроншавӣ аст · Ctrl + Enter
async function main() {
  const found = await fetch("https://rickandmortyapi.com/api/character?name=rick");
  console.log("rick →", found.status);

  const none = await fetch("https://rickandmortyapi.com/api/character?name=zzzzqq");
  console.log("zzzzqq →", none.status, none.ok);
  console.log(await none.json());
}
main();
Қиёс аз ҳаёт

Ҳуҷҷатгузории API — ин менюи чойхона аст. Шумо тахмин намекунед, ки дар ошхона чӣ ҳаст: менюро мекушоед ва хӯроку (эндпойнтҳо), ҳиссаҳо ва иловаҳоро (параметрҳо) мебинед. Ҷавоби сервер бошад — худи хӯрок дар табақ: пеш аз хӯрдан бояд бингаред, ки дар он чӣ ҳаст.

Ҳуҷҷатгузориро дар 2 дақиқа чӣ тавр хондан

  1. Эндпойнти лозимаро ёбед. «Ба ман ҷустуҷӯи мол лозим» → GET /products/search.
  2. Параметрҳоро нависед: кадомаш ҳатмӣ, кадомаш не, кадом қиматҳо иҷозат дода мешаванд (limit, skip, select, sortBy, order).
  3. Намунаи ҷавобро кушоед ва ёбед, ки рӯйхат дар куҷо ва майдонҳои хидматӣ дар куҷо ҳастанд.
  4. Бо даст санҷед: суроғаро ба браузер ё ба console.log гузоред ва боварӣ ҳосил кунед, ки ҷавоб чунон аст, ки дар ҳуҷҷатгузорӣ.
  5. Дар бораи «холӣ» ва «хато» бидонед: агар ҳеҷ чиз набошад, кадом статус ва кадом бадан меояд.

Як усули дигар: параметри select дар DummyJSON дар ҷавоб танҳо майдонҳои лозимаро мегузорад. Дархост сабуктар ва тезтар мешавад — барои чӣ тавсиф ва шарҳҳоро зеркашӣ кардан, агар ном ва нарх лозим бошад?

Амал: мустаҳкам мекунем

Се вазифа, ки дар онҳо шумо ҷавоби API-ро мехонед ва хусусиятҳои онро ба назар мегиред.

ВАЗИФАИ 2.1

Дар дохили лифофа чӣ ҳаст

осон

Се молро дархост кунед (limit=3, select=title,price). Калидҳои ҷавобро бо вергул чоп кунед (Object.keys(data).join(", ")), дар сатри дуюм — дар data.products чанд мол ҳаст.

Таҳрир кардан мумкин аст · Ctrl + Enter
async function main() {
  const url = "https://dummyjson.com/products?limit=3&select=title,price";
  // fetch → response.json() → ду console.log
}
main();
Натиҷаи интизоршаванда
products, total, skip, limit
3
Ишора 1

Ду await пай дар пай: const response = await fetch(url); const data = await response.json();

Ишора 2

Рӯйхат — data.products, массив .length дорад.

ВАЗИФАИ 2.2

Хаторо ёб: «data.map is not a function»

осон

Функсия бояд Загружено: 4-ро чоп кунад, аммо бо хато меафтад. Иҷро кунед, хаторо бихонед, ислоҳ намоед.

Ин код хато медиҳад · Ctrl + Enter
async function loadTitles() {
  const response = await fetch("https://dummyjson.com/products?limit=4&select=title");
  const data = await response.json();
  const titles = data.map((p) => p.title);
  console.log("Загружено:", titles.length);
}
loadTitles();
Натиҷаи интизоршаванда
Загружено: 4
Ишора 1

data — на массив, балки объект-лифофа аст. Кадом майдони он массиви молро дорад?

Ишора 2

data.map(...)-ро бо data.products.map(...) иваз кунед.

ВАЗИФАИ 2.3

Ҷустуҷӯи персонажҳо бе афтодан

миёна

searchCharacters(name)-ро анҷом диҳед: вай https://rickandmortyapi.com/api/character?name=…-ро дархост мекунад (параметрро тавассути URLSearchParams ҷамъ кунед) ва массиви data.results-ро бармегардонад. Агар сервер бо 404 ҷавоб дод — ин «чизе ёфт нашуд» аст, массиви холии []-ро баргардонед. Ҳар нокомии дигарро (!response.ok) хато ҳисоб кунед ва Error бипартоед.

Таҳрир кардан мумкин аст · Ctrl + Enter
async function searchCharacters(name) {
  // коди шумо
}

async function main() {
  const found = await searchCharacters("rick");
  console.log(found.length > 0);
  const none = await searchCharacters("zzzzqq");
  console.log(none.length);
}
main();
Натиҷаи интизоршаванда
true
0
Ишора 1

Аввал const response = await fetch(...), баъд санҷишҳои статус — ва танҳо баъд аз онҳо response.json().

Ишора 2

if (response.status === 404) return []; баъд if (!response.ok) throw new Error("HTTP " + response.status);

03 Рендери рӯйхат аз API

Маълумот омад — акнун онро дар саҳифа нишон медиҳем. Схема ҳамеша як аст: fetch → массив → барои ҳар унсур гиреҳ сохтан → ба саҳифа гузоштан. Ин ҳамон чизест, ки шумо дар дарсҳои 8 ва 11 мекардед, фақат массив акнун на аз тағйирёбанда, балки аз интернет меояд.

  1. Маълумотро дархост кардем: await fetch(url), await response.json().
  2. Рӯйхатро гирифтем аз лифофа: data.products.
  3. Контейнерро тоза кардем: list.replaceChildren().
  4. Барои ҳар мол <li> / корт сохтем, textContent-ро пур кардем ва ба контейнер илова намудем.
Рӯйхати мол аз API · Ctrl + Enter
<h3>Первые 5 товаров</h3>
<ul id="list"></ul>
Пешнамоиш
Барои дидани натиҷа «Иҷро кардан»-ро пахш кунед.
Қиёс аз ҳаёт

Рендер — ин витринаи бозор аст. Ҳар субҳ фурӯшанда молро болои дирӯза илова намекунад, балки витринаро аз нав мегузорад: кӯҳнаро бардошт — навро гузошт. Функсияи render бояд ҳамин тавр кор кунад: аввал тоза кунад, баъд созад. Он гоҳ вайро ҳар қадар бор даъват кардан мумкин аст (ҳангоми ҷустуҷӯ, ҳангоми иваз шудани саҳифа) ва ҳеҷ чиз дучанд намешавад.

Қоида: render рӯйхатро пурра аз нав месозад

Хатои маъмултарини навомӯзон — тоза карданро фаромӯш кардан. Даъвати аввал кор мекунад, дар дуюм (саҳифаи нав, ҷустуҷӯи нав) кортҳо ба кӯҳнаҳо илова мешаванд. Барои ҳамин ҳар чизе, ки рӯйхатро мекашад, бо replaceChildren() (ё innerHTML = "") оғоз мешавад.

Маълумоти сервер — ба он бовар накунед

Агар ҷавобро тавассути innerHTML гузорем, ҳар «мол» бо номи <b>…</b> ба тамға табдил меёбад, ҳамлагар бошад скриптро ҳам андохта метавонад. Барои матн аз API textContent-ро истифода баред: вай сатрро чунон ки ҳаст, бе таҷзияи тегҳо мегузорад.

textContent дар муқобили innerHTML · Ctrl + Enter
const title = "<b>СКИДКА!</b> Самбуса";   // гӯё аз сервер омадааст

const safe = document.createElement("p");
safe.textContent = title;
const risky = document.createElement("p");
risky.innerHTML = title;

console.log(safe.innerHTML);     // тегҳо ба матни бехатар табдил дода шуданд
console.log(risky.children.length);  // 1 — дар дохил <b>-и ҳақиқӣ пайдо шуд

Кортҳо бо расм

Каталоги ҳақиқӣ — ин на рӯйхати сатрҳо, балки кортҳо аст. Принсип ҳамон, гиреҳҳо фақат зиёдтаранд. Расмро аз майдони thumbnail мегирем ва ҳатман alt-ро муқаррар мекунем — барои дастрасӣ ва дар сурате, ки расм бор нашавад.

Кортҳо · Ctrl + Enter
<div id="grid" class="grid"></div>
Пешнамоиш
Барои дидани натиҷа «Иҷро кардан»-ро пахш кунед.
Рӯйхати холӣ — ҳам ҳолат аст

Агар сервер сифр мол баргардонад, корбар бояд «Ничего не найдено»-ро бинад, на ҷои холиро. Дар бораи ҳолатҳои боркунӣ ва хатоҳо дар дарси оянда муфассал сӯҳбат мекунем, ҳоло бошад ҳадди ақалро мегузорем: render натиҷаи холиро нишон дода метавонад.

Муфассалтар хонед: learn.javascript.ru — Fetch · MDN — Element.replaceChildren

Амал: мустаҳкам мекунем

Чор вазифа дар мавзӯи рендер. Натиҷа — дар пешнамоиш ва дар консоли зери майдони озмоиш.

ВАЗИФАИ 3.1

Панҷ мол ба рӯйхат

осон

loadProducts-ро анҷом диҳед: барои ҳар мол <li> бо матни название — цена $ (хатча бо фосилаҳо дар гирду атроф) созед ва ба list илова кунед. Консол санҷишро аллакай чоп мекунад: дар рӯйхат чанд унсур ҳаст ва оё дар аввалӣ хатча ҳаст.

Рӯйхати мол · Ctrl + Enter
<ul id="list"></ul>
Пешнамоиш
Барои дидани натиҷа «Иҷро кардан»-ро пахш кунед.
Натиҷаи интизоршаванда
5
true
Ишора 1

for (const product of data.products) { ... } — дар дохили давра як li месозем.

Ишора 2

const li = document.createElement("li"); li.textContent = `${product.title} — ${product.price} $`; list.append(li);

ВАЗИФАИ 3.2

Кортҳо бо расмҳо

миёна

Шаш молро бо кортҳо бароред: <article class="card"> бо <img> (src = thumbnail, alt = ном), сарлавҳаи <h3> ва нархи <p>. Ҳама тавассути createElement ва textContent.

Кортҳои мол · Ctrl + Enter
<div id="grid" class="grid"></div>
Пешнамоиш
Барои дидани натиҷа «Иҷро кардан»-ро пахш кунед.
Натиҷаи интизоршаванда
6
6
Ишора 1

Чор гиреҳ созед: article, img, h3, p. Ба синфи card card.className = "card" мувофиқ аст.

Ишора 2

Кортро бо як сатр ҷамъ кунед: card.append(img, title, price); ва ба grid гузоред.

ВАЗИФАИ 3.3

Хаторо ёб: рӯйхати дучандшуда

осон

Рӯйхатро ду бор навсозӣ мекунанд (гӯё корбар «навсозӣ»-ро пахш кард). Бояд 3 унсур монад, аммо 6 то аст. loadProducts-ро ислоҳ кунед, санҷишро ламс накарда.

Навсозии дучанда · Ctrl + Enter
<ul id="list"></ul>
Пешнамоиш
Барои дидани натиҷа «Иҷро кардан»-ро пахш кунед.
Натиҷаи интизоршаванда
3
Ишора 1

Ҳар даъват li-ҳои навро ба онҳое, ки аллакай ҳастанд, илова мекунад. Пеш аз давра чӣ намерасад?

Ишора 2

list.replaceChildren();-ро баъд аз гирифтани маълумот, пеш аз давра илова кунед.

ВАЗИФАИ 3.4

renderList бо ҳолати холӣ

душвор

renderList(items)-ро нависед (items — массиви сатрҳо). Вай ҳамеша аввал list-ро тоза мекунад. Барои массиви холӣ як <li class="empty">Ничего не найдено</li> илова мекунад, барои холинабуда — барои ҳар сатр як <li>. Шабака лозим нест.

renderList · Ctrl + Enter
<ul id="list"></ul>
Пешнамоиш
Барои дидани натиҷа «Иҷро кардан»-ро пахш кунед.
Натиҷаи интизоршаванда
2
1 Ничего не найдено
Чой
Ишора 1

Сатри аввал — list.replaceChildren();. Баъд шохаи if (items.length === 0) { ...; return; }.

Ишора 2

Дар шохаи холӣ li созед, className = "empty" ва матнро муқаррар кунед, ба рӯйхат илова намоед ва return нависед. Поёнтар, барои холинабуда, давраи оддии for (const item of items).

05 Филтркунӣ ва мураттабкунӣ

Филтр кардан дар ду ҷой мумкин аст — ва аз интихоб суръат ва қулайӣ вобаста аст:

Филтр дар сервер

Шумо аз API танҳо лозимаро металабед: /products/category/smartphones. Маълумоти камтар меояд, дар каталогҳои калон (ҳазорон сабт) кор мекунад, бо пагинатсия мувофиқ аст. Аммо ҳар филтри нав — дархости нав.

Филтр дар клиент

Маълумот аллакай бор шудааст, шумо оддӣ болои массив filter мекунед. Фавран, бе шабака. Аммо танҳо дар сурате кор мекунад, ки маҷмӯи пурра дар даст бошад — барои каталогҳои азим ва боркунии саҳифавӣ мувофиқ нест.

Қиёс аз ҳаёт

Сервер — ин анбор дар «Корвон»: шумо занг мезанед ва мегӯед «танҳо қолинҳои сурхро фиристед» — лозимаро меоранд. Клиент — ин пештахтаи шумо: қолинҳо аллакай оварда шудаанд, шумо оддӣ сурхҳоро ба чап, боқиро ба рост мемонед. Анбор роҳро сарфа мекунад, пештахта — вақти интизориро.

Филтр аз рӯи категория — дар тарафи сервер

Категорияҳо ва моли категория · Ctrl + Enter
async function getJson(url) {
  const response = await fetch(url);
  return response.json();
}

async function main() {
  const categories = await getJson("https://dummyjson.com/products/category-list");
  console.log("категорий больше десяти:", categories.length > 10);
  console.log(categories.slice(0, 3));

  const data = await getJson("https://dummyjson.com/products/category/smartphones?limit=3&select=title,category");
  data.products.forEach((p) => console.log(p.category, "—", p.title));
}
main();

Мураттабкунӣ дар сервер

DummyJSON худаш мураттаб карда метавонад: параметрҳои sortBy (аз рӯи кадом майдон) ва order (asc — афзоянда, desc — коҳанда).

Арзонтарин мол · Ctrl + Enter
async function main() {
  const params = new URLSearchParams({ sortBy: "price", order: "asc", limit: 5, select: "title,price" });
  const response = await fetch("https://dummyjson.com/products?" + params);
  const data = await response.json();
  data.products.forEach((p) => console.log(p.price, p.title));
}
main();
Маҳдудиятҳои API — ҳуҷҷатгузориро бихонед

Дар DummyJSON эндпойнти /products/search бо категория омехта намешавад: шумо ё аз рӯи калимаҳо ҷустуҷӯ мекунед, ё моли категорияро мегиред. Ихтироъ кардани ?q=phone&category=smartphones бефоида аст — сервер категорияро нодида мегирад. API-ҳои воқеӣ аз чунин маҳдудиятҳо пуранд. Дар барномаи мо қоида оддӣ мешавад: агар ҷустуҷӯ ворид шуда бошад — ҷустуҷӯ кор мекунад; агар категория интихоб шуда бошад — категория кор мекунад; яктояшро интихоб карда, дигарашро барқарор мекунем.

Филтр дар клиент: filter + includes

Вақте шартҳоро омехта кардан лозим аст ва маълумот кам аст, массивро дар ҷояш филтр мекунем. Як шарт — як «элак»:

Якчанд шарт дар як filter · Ctrl + Enter
const menu = [
  { title: "Самбуса", category: "выпечка", price: 6 },
  { title: "Курутоб", category: "горячее", price: 35 },
  { title: "Шакароб", category: "салаты", price: 15 },
  { title: "Оши палав", category: "горячее", price: 40 },
];

function applyFilters(items, { q = "", category = "", maxPrice = Infinity }) {
  const text = q.trim().toLowerCase();
  return items.filter((item) => {
    if (text && !item.title.toLowerCase().includes(text)) return false;  // ҷустуҷӯ бе назардошти ҳарфи калон/хурд
    if (category && item.category !== category) return false;
    return item.price <= maxPrice;
  });
}

console.log(applyFilters(menu, { category: "горячее" }).map((i) => i.title));
console.log(applyFilters(menu, { q: "ША", maxPrice: 20 }).map((i) => i.title));

Ба усул диққат диҳед: филтрҳо бо қиматҳои пешфарз «хомӯш» мешаванд (q = "", category = "", maxPrice = Infinity) — шарти холӣ ҳамаро мегузаронад.

Рӯйхати афтанда аз маълумот

Категорияҳоро бо даст навиштан лозим нест: онҳоро аз маълумот ҷамъ кардан мумкин аст. new Set(...) танҳо қиматҳои беназирро мегузорад — ҳамон чизе ки барои <select> лозим аст:

Таҳрир кардан мумкин аст · Ctrl + Enter
const menu = ["выпечка", "горячее", "горячее", "салаты", "выпечка"];
const unique = [...new Set(menu)];
console.log(unique);   // ["выпечка", "горячее", "салаты"]

Муфассалтар хонед: learn.javascript.ru — Усулҳои массивҳо · MDN — Array.prototype.filter

Амал: мустаҳкам мекунем

Филтрҳо ва мураттабкунӣ — дар сервер ва дар клиент.

ВАЗИФАИ 5.1

Моли як категория

осон

Се сатр чоп кунед: оё "smartphones" дар category-list ҳаст; оё ҳамаи моли аз ҷавоби /products/category/smartphones?limit=3&select=category категорияи smartphones доранд (every); чанд мол омад.

Таҳрир кардан мумкин аст · Ctrl + Enter
async function getJson(url) {
  const response = await fetch(url);
  return response.json();
}

async function main() {
  // се console.log
}
main();
Натиҷаи интизоршаванда
true
true
3
Ишора 1

const categories = await getJson(".../products/category-list"); — ин массиви оддии сатрҳо аст, includes дорад.

Ишора 2

data.products.every((p) => p.category === "smartphones")

ВАЗИФАИ 5.2

Аз ҳама гарон: мураттабкунӣ ва санҷиш

миёна

5 молро дархост кунед, ки аз рӯи нарх коҳанда мураттаб шудаанд (sortBy=price, order=desc, select=price, limit=5 — тавассути URLSearchParams ҷамъ кунед). Санҷед, ки нархҳо воқеан коҳиш меёбанд ва натиҷа ва миқдорро чоп кунед.

Таҳрир кардан мумкин аст · Ctrl + Enter
async function main() {
  // дархост → массиви нархҳо → санҷиш тавассути every
  // console.log("по убыванию:", ...);
  // console.log("количество:", ...);
}
main();
Натиҷаи интизоршаванда
по убыванию: true
количество: 5
Ишора 1

Нархҳо: data.products.map((p) => p.price).

Ишора 2

Ҳар нарх аз қаблӣ зиёд нест: prices.every((price, i) => i === 0 || prices[i - 1] >= price)

ВАЗИФАИ 5.3

Меню: филтр аз рӯи се шарт

миёна

applyFilters(items, { q, category, maxPrice })-ро нависед. Ҷустуҷӯ — аз рӯи ном, бе назардошти ҳарфи калон/хурд; категория — мувофиқати дақиқ; maxPrice — «на гаронтар». Ҳар шартро надодан мумкин аст. Функсияи show аллакай навишта шудааст.

Таҳрир кардан мумкин аст · Ctrl + Enter
const menu = [
  { title: "Самбуса с мясом", category: "выпечка", price: 6 },
  { title: "Самбуса с тыквой", category: "выпечка", price: 5 },
  { title: "Курутоб", category: "горячее", price: 35 },
  { title: "Шакароб", category: "салаты", price: 15 },
  { title: "Оши палав", category: "горячее", price: 40 },
  { title: "Манту", category: "горячее", price: 30 },
];

function applyFilters(items, { q = "", category = "", maxPrice = Infinity }) {
  // коди шумо
}

function show(items) {
  console.log(items.map((item) => item.title).join(", ") || "ничего не найдено");
}

show(applyFilters(menu, { q: "самбуса" }));
show(applyFilters(menu, { category: "горячее", maxPrice: 35 }));
show(applyFilters(menu, { q: "ШАК" }));
show(applyFilters(menu, { category: "супы" }));
show(applyFilters(menu, { q: "а", category: "горячее", maxPrice: 30 }));
Натиҷаи интизоршаванда
Самбуса с мясом, Самбуса с тыквой
Курутоб, Манту
Шакароб
ничего не найдено
Манту
Ишора 1

Матни ҷустуҷӯшаванда ва номро ба ҳарфи хурд оред: toLowerCase(). Санҷиши дохил будан — includes.

Ишора 2

Дар дохили items.filter(...) ба навбат элак кунед: if (text && !… ) return false;, if (category && … !== category) return false; ва дар охир return item.price <= maxPrice;

ВАЗИФАИ 5.4

Select бо категорияҳо аз маълумот

душвор

Майдони озмоишро пурра кунед: 1) select-ро пур кунед: опсияи аввал «Все» бо қимати "", баъд барои ҳар категорияи беназир як опсия (new Set ва addOption-и тайёр кӯмак мекунанд); 2) ҳангоми ҳодисаи change танҳо моли категорияи интихобшударо нишон диҳед (ё ҳамаро, агар «Все» интихоб шуда бошад), renderList-и тайёрро истифода бурда. Дар поён код худаш категорияҳоро «интихоб мекунад» ва санҷишро чоп менамояд.

Филтр аз рӯи категория · Ctrl + Enter
<select id="category"></select>
<ul id="list"></ul>
Пешнамоиш
Барои дидани натиҷа «Иҷро кардан»-ро пахш кунед.
Натиҷаи интизоршаванда
опций: 4
горячее: 3
все: 6
Ишора 1

addOption("", "Все");, баъд давраи for (const category of new Set(menu.map((item) => item.category))).

Ишора 2

Дар коркардкунанда: const value = select.value; renderList(value ? menu.filter((item) => item.category === value) : menu); Як даъвати renderList(menu)-ро пеш аз обуна фаромӯш накунед.

06 Пагинатсия: саҳифаҳо аз рӯи limit ва skip

Дар каталог 194 мол ҳаст — ҳамаро якбора бор кардан нодонист: дароз, вазнин ва касе он қадар намегардонад. Барои ҳамин сервер маълумотро ҳисса-ҳисса медиҳад, шумо бошед ҳиссаи лозимаро металабед. Ин аст пагинатсия (боркунии саҳифавӣ).

Қиёс аз ҳаёт

Палавро дар тӯй на бо тамоми дег, балки бо ҳисса дар лаганҳо медиҳанд: дар ҳар кадом — даҳ ҳисса. Иловаи дигар мехоҳед — лагани навбатӣ металабед. Параметри limit — дар як лаган чанд ҳисса, skip — чанд ҳиссаро шумо аллакай хӯрдед ва мегузаронед.

Ду усули маъмул

УсулПараметрҳоКӣ истифода мебарад
limit + skip (offset)«limit дона деҳ, skip-и аввалро гузаронда»DummyJSON, PokeAPI (limit + offset)
page«саҳифаи № page-ро деҳ», андозаи саҳифа аз ҷониби сервер муайян шудаастRick and Morty (20 дар саҳифа), CoinGecko (page + per_page)

Корбар бо рақамҳои саҳифа фикр мекунад: «саҳифаи 3». Ба сервери DummyJSON skip лозим аст. Бо формула табдил медиҳем:

skip=(page − 1)×limit
Саҳифаи 1 — 0 сабтро мегузаронад, саҳифаи 2 — limit сабт, саҳифаи 3 — 2 × limit
Саҳифаskip ҳангоми limit = 10Кадом сабтҳо
101–10
21011–20
32021–30

Ва ҳамагӣ чанд саҳифа аст? Шумораи умумии сабтҳоро (total аз ҷавоб) ба андозаи саҳифа тақсим мекунем ва ба боло мудаввар мекунем — саҳифаи охирини нопурра ҳам саҳифа аст:

Формулаҳои пагинатсия · Ctrl + Enter
const pagesCount = (total, limit) => Math.ceil(total / limit);
const pageToSkip = (page, limit) => (page - 1) * limit;

console.log(pagesCount(100, 10));   // 10
console.log(pagesCount(101, 10));   // 11 — як сабт ба саҳифаи нав «рехт»
console.log(pageToSkip(3, 10));     // 20
Саҳифаи зинда аз API · Ctrl + Enter
async function loadPage(page, limit) {
  const params = new URLSearchParams({ limit, skip: (page - 1) * limit, select: "title" });
  const response = await fetch("https://dummyjson.com/products?" + params);
  return response.json();
}

async function main() {
  const data = await loadPage(2, 5);
  console.log(`skip=${data.skip}, limit=${data.limit}`);
  console.log("страниц всего больше одной:", Math.ceil(data.total / data.limit) > 1);
  data.products.forEach((p) => console.log(p.id, p.title));
}
main();

Тугмаҳои «Ба қафо» ва «Ба пеш»

Ҳолати пагинатсия — ҳамагӣ ду рақам аст: саҳифаи ҷорӣ ва шумораи умумии саҳифаҳо. Ҳама чизи дигар аз онҳо кашида мешавад:

  • «Ба қафо» дар саҳифаи аввал хомӯш мешавад: prev.disabled = page === 1.
  • «Ба пеш» дар охирин хомӯш мешавад: next.disabled = page === pages.
  • Ёддошти «Саҳифаи 3 аз 5» аз ҳамин рақамҳо ҷамъ мешавад.
  • Баъд аз ҷустуҷӯи нав ё иваз шудани категория саҳифа ба аввал барқарор мешавад — вагарна дар саҳифаи 7-уми натиҷаҳо мондан мумкин аст, ки ҳамагӣ се то аст.
Хатоҳои маъмул

1. skip = page * limit бе «−1» — як саҳифаи пурра гузаронда мешавад, ҳиссаи аввали мол ҳеҷ гоҳ намоён нест. 2. Саҳифаҳоро тавассути Math.floor ё Math.round ҳисоб мекунанд — саҳифаи охирин гум мешавад. 3. page = 1-ро ҳангоми ҷустуҷӯи нав барқарор карданро фаромӯш мекунанд. 4. Ҷустуҷӯи холӣ дар DummyJSON total: 0 бармегардонад — ба нул тақсим кардан тарсовар нест, аммо «саҳифаи 1 аз 0» аҷиб менамояд: ҳадди ақал як саҳифаро нишон диҳед (Math.max(1, …)).

Rick and Morty: саҳифаҳо бе skip

Дар он ҷо ҳама чиз осонтар: ?page=2. Ва чанд саҳифа будани онро худи сервер дар info.pages мегӯяд ва ҳатто info.next-ро медиҳад — суроғаи саҳифаи оянда ё null, агар саҳифа охирин бошад. Ин барои тугмаи «Боз нишон деҳ» қулай аст.

Боз нишон деҳ: саҳифаҳоро ҷамъ мекунем · Ctrl + Enter
let page = 0;
let items = [];

async function loadMore(name) {
  page++;
  const params = new URLSearchParams({ name, page });
  const response = await fetch("https://rickandmortyapi.com/api/character?" + params);
  const data = await response.json();
  items = items.concat(data.results);       // иваз намекунем, балки илова мекунем
  return data.info.next !== null;           // оё саҳифаҳои дигар ҳаст
}

async function main() {
  let hasMore = await loadMore("rick");
  console.log("после 1-й загрузки:", items.length, hasMore);
  hasMore = await loadMore("rick");
  console.log("после 2-й загрузки:", items.length, hasMore);
}
main();

Ду интерфейс ҳаст: саҳифаҳо (тугмаҳо, бо рақамҳо варақ мезанем; render рӯйхатро иваз мекунад) ва «Боз нишон деҳ» / лентаи беохир (кортҳо илова мешаванд; render илова мекунад, на тоза). Интихоб — кори дизайн аст, дархостҳо бошанд якхела.

Муфассалтар хонед: DummyJSON — Products · Rick and Morty API — Character · MDN — Math.ceil

Амал: мустаҳкам мекунем

Формулаҳо, саҳифаҳои зинда ва тугмаҳо.

ВАЗИФАИ 6.1

Чанд саҳифа

осон

pagesCount(total, limit)-ро нависед — шумораи саҳифаҳо бо назардошти охирини нопурра.

Таҳрир кардан мумкин аст · Ctrl + Enter
function pagesCount(total, limit) {
  // коди шумо
}

console.log(pagesCount(100, 10));
console.log(pagesCount(101, 10));
console.log(pagesCount(0, 10));
console.log(pagesCount(5, 10));
Натиҷаи интизоршаванда
10
11
0
1
Ишора 1

Тақсимкунӣ барои саҳифаи нопурра каср медиҳад: 101 / 10 = 10.1. Ба боло мудаввар кардан лозим аст.

Ишора 2

return Math.ceil(total / limit);

ВАЗИФАИ 6.2

Суроғаи саҳифа

осон

pageToSkip(page, limit) ва productsUrl(page, limit)-ро нависед: суроғаи намуди https://dummyjson.com/products?limit=10&skip=20 (аввал limit, баъд skip).

Таҳрир кардан мумкин аст · Ctrl + Enter
function pageToSkip(page, limit) {
  // коди шумо
}

function productsUrl(page, limit) {
  // коди шумо
}

console.log(productsUrl(1, 10));
console.log(productsUrl(3, 10));
console.log(productsUrl(2, 5));
Натиҷаи интизоршаванда
https://dummyjson.com/products?limit=10&skip=0
https://dummyjson.com/products?limit=10&skip=20
https://dummyjson.com/products?limit=5&skip=5
Ишора 1

Саҳифаи аввал 0 сабтро мегузаронад: (page - 1) * limit.

Ишора 2

Сатри қолабӣ: `https://dummyjson.com/products?limit=${limit}&skip=${pageToSkip(page, limit)}`

ВАЗИФАИ 6.3

Ду саҳифа бе такрор

миёна

loadPage(page, limit)-ро нависед (дархост ба /products бо limit, skip ва select=title, тамоми объекти ҷавобро бармегардонад). Саҳифаҳои 1 ва 2-ро 5-тоӣ бор кунед ва чоп намоед: skip ва limit-и саҳифаи дуюм, оё дар он моли саҳифаи аввал нест (аз рӯи id) ва оё саҳифаҳо аз як зиёданд.

Таҳрир кардан мумкин аст · Ctrl + Enter
async function loadPage(page, limit) {
  // коди шумо
}

async function main() {
  const first = await loadPage(1, 5);
  const second = await loadPage(2, 5);
  console.log(`skip=${second.skip}, limit=${second.limit}`);
  // бе такрор: Set-и id-ҳои саҳифаи аввалро истифода баред
  // саҳифаҳо аз як зиёд: Math.ceil(second.total / second.limit) > 1
}
main();
Натиҷаи интизоршаванда
skip=5, limit=5
без повторов: true
страниц больше одной: true
Ишора 1

const ids = new Set(first.products.map((p) => p.id));

Ишора 2

second.products.every((p) => !ids.has(p.id)) — ҳеҷ id-и саҳифаи дуюм дар ids нест.

ВАЗИФАИ 6.4

Тугмаҳои пагинатсия

миёна

Дар майдони озмоиш 47 сабт ҳаст, дар ҳар саҳифа 10 — 5 саҳифа. render-ро нависед (ёддошти «Страница N из M», хомӯш кардани тугмаҳо) ва тугмаҳоро ба click обуна кунед (page-- / page++ ва render()). Фаромӯш накунед, ки render-ро як маротиба дар оғоз даъват кунед. Коди зер ба тугмаҳо «клик мекунад», аз ҷумла ба хомӯшшудаҳо.

Пагинатсия · Ctrl + Enter
<button id="prev">← Назад</button>
<span id="label"></span>
<button id="next">Вперёд →</button>
Пешнамоиш
Барои дидани натиҷа «Иҷро кардан»-ро пахш кунед.
Натиҷаи интизоршаванда
Страница 1 из 5 | назад: выкл | вперёд: вкл
Страница 3 из 5 | назад: вкл | вперёд: вкл
Страница 5 из 5 | назад: вкл | вперёд: выкл
Страница 4 из 5 | назад: вкл | вперёд: вкл
Ишора 1

label.textContent = `Страница ${page} из ${pages}`; ва prev.disabled = page === 1;

Ишора 2

Клик ба тугмаи аллакай хомӯшшуда коре намекунад, барои ҳамин аз саҳифаи панҷум дуртар «варақ задан» намешавад: фақат аз паси он назар кунед, ки render() баъд аз ҳар тағйири page даъват шавад.

ВАЗИФАИ 6.5

«Боз нишон деҳ» дар Рик ва Морти

душвор

loadMore(name)-ро нависед: вай ҳисобкунаки page-ро меафзояд, https://rickandmortyapi.com/api/character?name=…&page=…-ро дархост мекунад, data.results-ро ба массиви items илова мекунад ва true бармегардонад, агар саҳифаи оянда бошад (data.info.next !== null). Онро барои «rick» ду бор пай дар пай даъват кунед ва баъд аз ҳар даъват чоп намоед: чандто ҷамъ шуд ва оё боз ҳаст.

Таҳрир кардан мумкин аст · Ctrl + Enter
let page = 0;
let items = [];

async function loadMore(name) {
  // коди шумо
}

async function main() {
  // ду даъвати loadMore, баъд аз ҳар кадом console.log(items.length, hasMore)
}
main();
Натиҷаи интизоршаванда
20 true
40 true
Ишора 1

Саҳифаи андозааш 20 — аз ҷониби сервер муайян шудааст. Ҷамъшавӣ: items = items.concat(data.results); (на items = data.results).

Ишора 2

Дар main: let hasMore = await loadMore("rick"); console.log(items.length, hasMore); — ва боз як бор ҳамин.

07 Блоки ЗС: ҳуҷҷатгузории API-ро бо ЗС якҷоя мехонем

Ҳуҷҷатгузориҳо дароз, бо забони англисӣ ва чунон навишта шудаанд, ки параметри лозимаро ёфтан душвор аст. ЗС аъло кумак мекунад, ки зуд эндпойнт ва параметрҳоро ёбед — агар худи матни ҳуҷҷатгузориро ба он диҳед. Аммо вай нуқсон дорад: вақте ҳуҷҷатгузорӣ дар даст нест, вай параметрҳоеро ихтироъ мекунад, ки ба ҳақиқат монанданд, аммо дар API-и шумо вуҷуд надоранд.

Қоидаҳои курс, ки дар ин ҷо махсусан муҳиманд

1. Аввал 15 дақиқа худатон кӯшиш мекунед: ҳуҷҷатгузориро мекушоед, эндпойнтро меҷӯед, суроғаро дастӣ менависед. 2. Баъд назди ЗС меравед. 3. Ба лоиҳа коде гузоред, ки онро сатр ба сатр шарҳ дода наметавонед. 4. Ҳар чизе, ки ЗС дар бораи параметрҳои API гуфт, бо ҷавоби воқеии сервер муқоиса кунед — бо дархост дар консол.

Чӣ тавр пурсидан: бад ва хуб

Формулаи промпти курс: контекст → вазифа → маҳдудиятҳо → формати ҷавоб → санҷиш. Барои ҳуҷҷатгузорӣ муҳимтаринаш — матни онро гузоштан (ё порчаи лозимаро), на хоҳиш кардани «ба ёд овардан».

дар dummyjson молро аз рӯи категория ва ном якбора чӣ тавр ҷустуҷӯ кунам
Ман JavaScript меомӯзам, каталоги молро бо JS-и пок бе китобхонаҳо менависам. Инак порчаи ҳуҷҷатгузории DummyJSON: [матнро аз саҳифаи https://dummyjson.com/docs/products гузоред]. Ба ман лозим: саҳифаи 6 молро нишон додан, аз рӯи ном ҷустуҷӯ кардан ва аз рӯи категория филтр кардан. Бигӯ: кадом эндпойнт барои ҷустуҷӯ, кадом барои категория, онҳо кадом параметрҳо доранд (limit, skip, select, sortBy, order) ва оё онҳоро омехта кардан мумкин аст. Бо ҷадвали «вазифа → эндпойнт → параметрҳо» ва як мисоли дархост бо fetch ва URLSearchParams ҷавоб деҳ. Агар дар ҳуҷҷатгузорӣ чизе набошад, ҳамин тавр бигӯ, чизеро ихтироъ накун. Дар охир бинвис, ки ман дар консол чиро дастӣ санҷам.

Промпти хубро нусха кунед, матни воқеии ҳуҷҷатгузориро гузоред — ва ҷавоб гиред. Ва акнун доми асосиро месанҷем: агар ба параметри ихтироъшуда бовар кунем чӣ мешавад. Фарз кунем, ЗС бо боварӣ ?category=smartphones-ро ба ҷустуҷӯ маслиҳат дод:

Маслиҳати ЗС-ро бо дархост месанҷем · Ctrl + Enter
async function main() {
  const params = new URLSearchParams({ q: "phone", category: "smartphones", limit: 10, select: "title,category" });
  const response = await fetch("https://dummyjson.com/products/search?" + params);
  const data = await response.json();

  const works = data.products.every((p) => p.category === "smartphones");
  console.log("параметр category сработал:", works);
  console.log(data.products.map((p) => p.category));
}
main();
// false — сервер параметри ихтироъшударо хомӯшона нодида гирифт. Хато нест, аммо филтр ҳам нест!
Дар ҷавоби ЗС дар бораи API чиро санҷидан лозим

1) Чунин параметр дар ҳуҷҷатгузорӣ ҳаст? 2) Рӯйхат дар ҷое ҳаст, ки ЗС гуфт (data.products ё оддӣ массив)? 3) Ҳангоми «чизе ёфт нашуд» чӣ бармегардад — рӯйхати холӣ ё 404? 4) Калиди API лозим аст (дар курси мо — не)? 5) Оё мисол бо ҷавоби воқеӣ дар консол мувофиқат мекунад?

Порчаи ҳуҷҷатгузорӣ чунин менамояд. Хондани онро бе тарҷума омӯзед — аз рӯи номҳои параметрҳо ва мисолҳо:

Порчаи ҳуҷҷатгузорӣ (мухтасар)
GET https://dummyjson.com/products/category/{category}

Query-параметры:
  limit    сколько записей вернуть
  skip     сколько записей пропустить
  select   поля через запятую (title,price)
  sortBy   поле для сортировки
  order    asc | desc

Ответ: { products: [...], total, skip, limit }

Амал: ЗС-ро месанҷем ва ҳуҷҷатгузориро мехонем

ВАЗИФАИ 7.1

ЗС ҷамъкунии суроғаро навишт — се хаторо ёбед

миёна

ЗС функсияро барои суроғаи рӯйхати мол навишт. Вай хуб менамояд, аммо дар он се хато ҳаст: skip-и нодуруст, ҷустуҷӯ ба эндпойнти нодуруст меравад ва қимати q рамзгузорӣ намешавад. Ислоҳ кунед (параметрҳо дар суроға — бо тартиби limit, skip, q).

Таҳрир кардан мумкин аст · Ctrl + Enter
// ЗС навишт — бовар намекунем, месанҷем
function buildProductsUrl(page, limit, q) {
  const skip = page * limit;
  let url = "https://dummyjson.com/products?limit=" + limit + "&skip=" + skip;
  if (q) url += "&q=" + q;
  return url;
}

console.log(buildProductsUrl(1, 10, ""));
console.log(buildProductsUrl(3, 10, "iphone case"));
Натиҷаи интизоршаванда
https://dummyjson.com/products?limit=10&skip=0
https://dummyjson.com/products/search?limit=10&skip=20&q=iphone+case
Ишора 1

Саҳифаи 1 бояд 0 сабтро гузаронад: (page - 1) * limit. Барои q-и холинабуда роҳи /products/search лозим аст.

Ишора 2

new URLSearchParams({ limit, skip })-ро ҷамъ кунед, ҳангоми q-и холинабуда params.set("q", q)-ро илова кунед ва суроғаро бо /search баргардонед; вагарна — бе он.

ВАЗИФАИ 7.2

Суроға аз рӯи ҳуҷҷатгузорӣ

миёна

Аз рӯи порчаи боло: се ноутбуки аз ҳама арзон — категорияи laptops, limit=3, мураттабкунӣ аз рӯи нарх афзоянда, майдонҳои title,price (параметрҳо бо ин тартиб: limit, sortBy, order, select). Суроғаро чоп кунед, баъд миқдори моли гирифташуда ва инро, ки нархҳо воқеан афзоянда мераванд. (Вергул дар суроға ба %2C табдил меёбад — ин муқаррарӣ аст, сервер мефаҳмад.)

Таҳрир кардан мумкин аст · Ctrl + Enter
async function main() {
  // 1) суроғаро тавассути URLSearchParams ҷамъ кунед ва чоп намоед
  // 2) дархост кунед, миқдор ва санҷиши мураттабкуниро чоп кунед
}
main();
Натиҷаи интизоршаванда
https://dummyjson.com/products/category/laptops?limit=3&sortBy=price&order=asc&select=title%2Cprice
3
true
Ишора 1

new URLSearchParams({ limit: 3, sortBy: "price", order: "asc", select: "title,price" }) — тартиби калидҳо дар объект тартибро дар суроға муайян мекунад.

Ишора 2

Санҷиши мураттабкунӣ: prices.every((price, i) => i === 0 || prices[i - 1] <= price).

08 Амал дар дарс: каталог бо ҷустуҷӯ, категорияҳо ва саҳифаҳо

Барномаро аз ҳамаи порчаҳои дарс ҷамъ мекунем. Ин ҳамон «каталоги корӣ» аст: ҷустуҷӯ, филтр аз рӯи категория, боркунии саҳифавӣ ва маълумот аз сервери воқеӣ. Қадам ба қадам месозем — баъд аз ҳар қадам иҷро кунед ва бинед.

Қадами 1. Ҳолат ва суроғаи дархост

Тамоми мантиқи барнома дар як объекти state (дарси 11) меистад: чиро меҷӯем, кадом категория, кадом саҳифа. Функсияи buildUrl бошад ҳолатро ба суроға табдил медиҳад. Иҷро кунед — ҳоло бе шабака:

Қадами 1. state → суроға · Ctrl + Enter
const API = "https://dummyjson.com/products";
const LIMIT = 6;
const state = { q: "", category: "", page: 1 };

function buildUrl() {
  const params = new URLSearchParams({
    limit: LIMIT,
    skip: (state.page - 1) * LIMIT,
    select: "title,price,thumbnail",
  });
  if (state.q) {                                  // ҷустуҷӯ аз категория муҳимтар аст
    params.set("q", state.q);
    return `${API}/search?${params}`;
  }
  if (state.category) return `${API}/category/${state.category}?${params}`;
  return `${API}?${params}`;
}

console.log(buildUrl());

state.page = 3;
console.log(buildUrl());

state.category = "laptops";
state.page = 1;
console.log(buildUrl());

state.q = "mac";
console.log(buildUrl());

Қадами 2. Боркунӣ ва рендер

fetch ва render-ро пайваст мекунем: дархост аз рӯи buildUrl(), тоза кардани рӯйхат, кортҳо ва барои натиҷаи холӣ — паём. Ҳолати «Боркунӣ…»-ро ҳангоми интизорӣ нишон медиҳем.

Қадами 2. Рӯйхат аз API · Ctrl + Enter
<p id="status"></p>
<ul id="list" class="grid"></ul>
Пешнамоиш
Барои дидани натиҷа «Иҷро кардан»-ро пахш кунед.

Қадами 3. Саҳифаҳо

Ба state pages-ро (чанд саҳифа) илова мекунем — онро аз data.total баъд аз ҳар ҷавоб ҳисоб мекунем. Функсияи go(page) сарҳадҳоро месанҷад, саҳифаро иваз мекунад ва бор мекунад. updatePager ёддоштро мекашад ва тугмаҳои зиёдатиро хомӯш мекунад.

Қадами 3. Ба қадами 2 чӣ илова мекунем
const state = { q: "", category: "", page: 1, pages: 1 };

function updatePager() {
  info.textContent = `Страница ${state.page} из ${state.pages}`;
  prev.disabled = state.page <= 1;
  next.disabled = state.page >= state.pages;
}

async function load() {
  statusEl.textContent = "Загрузка…";
  const response = await fetch(buildUrl());
  const data = await response.json();
  state.pages = Math.max(1, Math.ceil(data.total / LIMIT));   // ҳадди ақал як саҳифа
  render(data.products);
  updatePager();
}

async function go(page) {
  if (page < 1 || page > state.pages) return;   // аз сарҳад намегузарем
  state.page = page;
  await load();
}

prev.addEventListener("click", () => go(state.page - 1));
next.addEventListener("click", () => go(state.page + 1));

Қадами 4. Ҷустуҷӯ ва категорияҳо

Ҷустуҷӯ — ин debounce + функсияи search аст, ки state.q-ро муқаррар мекунад, категория ва саҳифаро барқарор мекунад ва бор мекунад. Категория — ба ҳамин монанд. Ба load ҳимоя аз мусобиқа (lastRequest) ва коркарди ҳадди ақали хатоҳоро илова мекунем. Инак барномаи тайёр пурра — «phone»-ро ҷустуҷӯ кунед, категорияро интихоб кунед, саҳифаҳоро варақ занед:

Каталог: барномаи тайёр · Ctrl + Enter
<div class="app">
  <div class="bar">
    <input id="q" type="search" placeholder="Поиск товара…">
    <select id="category"><option value="">Все категории</option></select>
  </div>
  <p id="status" class="status"></p>
  <ul id="list" class="grid"></ul>
  <div class="pager">
    <button id="prev">← Назад</button>
    <span id="info"></span>
    <button id="next">Вперёд →</button>
  </div>
</div>
Пешнамоиш
Барои дидани натиҷа «Иҷро кардан»-ро пахш кунед.
Дар ин ҷо чиро қайд кардан муҳим аст

Як сарчашмаи ҳақиқат: state. Тугмаҳо, ҷустуҷӯ ва select рӯйхатро худашон намекашанд, онҳо ҳолатро тағйир медиҳанд ва load()-ро садо мекунанд. Як «рассом»: render ҳамеша рӯйхатро пурра аз нав месозад. Як «мизи суроға»: buildUrl. Филтри нав илова кардан — маънои илова кардани майдон ба state, як сатр ба buildUrl ва коркардкунандаи ҳодисаро дорад. Боқиро ламс намекунем.

Амал: барномаро анҷом медиҳем

Дар ду вазифа — ҳамон барнома, аммо аз он як порча гирифта шудааст. Онро анҷом диҳед: майдони озмоиш худаш сенарияро мегузаронад ва санҷишро чоп мекунад (интернет лозим аст, санҷиш чанд сония мегирад).

ВАЗИФАИ 8.1

Пагинатсия: go ва updatePager

миёна

Дар барнома функсияҳои updatePager (ёддошти «Страница N из M» ва disabled барои тугмаҳо) ва go(page) (санҷиши сарҳадҳо, иваз кардани саҳифа, боркунӣ) холӣ мебошанд. Онҳоро анҷом диҳед.

Каталог: пагинатсияро анҷом диҳед · Ctrl + Enter
<div class="app">
  <div class="bar">
    <input id="q" type="search" placeholder="Поиск товара…">
    <select id="category"><option value="">Все категории</option></select>
  </div>
  <p id="status" class="status"></p>
  <ul id="list" class="grid"></ul>
  <div class="pager">
    <button id="prev">← Назад</button>
    <span id="info"></span>
    <button id="next">Вперёд →</button>
  </div>
</div>
Пешнамоиш
Барои дидани натиҷа «Иҷро кардан»-ро пахш кунед.
Натиҷаи интизоршаванда
карточек: 6
страниц больше одной: true
на первой странице «Назад» выкл
страница 2, карточек: 6
go(0) оставила страницу 2
назад вкл, вперёд вкл
Ишора 1

updatePager: info.textContent = `Страница ${state.page} из ${state.pages}`; баъд prev.disabled = state.page <= 1; ва next.disabled = state.page >= state.pages;

Ишора 2

go: агар page < 1 || page > state.pages бошад — оддӣ return; вагарна state.page = page; await load();

ВАЗИФАИ 8.2

Ҷустуҷӯ: search бо барқарор кардани саҳифа

душвор

Функсияи search(text) ҳоло холӣ аст. Вай бояд: фосилаҳои канорро бардорад ва ба state.q нависад, категорияро барқарор кунад (state.category ва қимати catSelect), саҳифаро ба аввал баргардонад ва маълумотро бор кунад. Он гоҳ баъд аз ҷустуҷӯи «zzzzqq» барнома «Ничего не найдено»-ро нишон медиҳад, ҷустуҷӯи холӣ бошад рӯйхати оддиро бармегардонад.

Каталог: ҷустуҷӯро анҷом диҳед · Ctrl + Enter
<div class="app">
  <div class="bar">
    <input id="q" type="search" placeholder="Поиск товара…">
    <select id="category"><option value="">Все категории</option></select>
  </div>
  <p id="status" class="status"></p>
  <ul id="list" class="grid"></ul>
  <div class="pager">
    <button id="prev">← Назад</button>
    <span id="info"></span>
    <button id="next">Вперёд →</button>
  </div>
</div>
Пешнамоиш
Барои дидани натиҷа «Иҷро кардан»-ро пахш кунед.
Натиҷаи интизоршаванда
страница: 3
поиск: карточек больше нуля: true
страница после поиска: 1
пусто: true, сообщение: Ничего не найдено
сброс поиска, карточек: 6
Ишора 1

Чор сатр пеш аз боркунӣ: state.q = text.trim();, state.category = "";, catSelect.value = "";, state.page = 1;

Ишора 2

Сатри охиринро фаромӯш накунед — await load();. Бе он ҳолат тағйир меёбад, рӯйхат бошад не.

09 Вазифаи хонагӣ

Ба барномаи худии бо API ҷустуҷӯ ва пагинатсия илова кунед. Асос — барномаи дарси 14 (ду API), каталоги амали имрӯза ё ҳар кадом аз мавзӯъҳои поён. Ҳамаи API-ҳо — оммавӣ ва бе калид: санҷида шудааст, аз браузер ҷавоб медиҳанд.

Мавзӯъро интихоб кунед

МавзӯъAPI ва ҳуҷҷатгузорӣҶустуҷӯПагинатсия / филтр
A. Филмҳо ва сериалҳоTVmaze — бе калид (TMDB ва OMDb калид металабанд, дар курс онҳоро истифода намебарем)https://api.tvmaze.com/search/shows?q=…: массиви { score, show }; холӣ — []Каталоги https://api.tvmaze.com/shows?page=0 (тақрибан 250 то, саҳифаҳо аз 0; берун аз ҳудуд — 404). Филтр аз рӯи жанр — дар клиент (show.genres). Постери show.image метавонад null бошад!
B. Обу ҳавоOpen-Meteo Geocoding ва ForecastҶустуҷӯи шаҳр: geocoding-api.open-meteo.com/v1/search?name=…&count=10&language=ru. Агар чизе ёфт нашавад, дар ҷавоб майдони results нест — data.results ?? []-ро истифода баредШаҳри интихобшуда → api.open-meteo.com/v1/forecast?latitude=…&longitude=…&daily=temperature_2m_max,temperature_2m_min&timezone=auto. Саҳифаҳо — «рӯзҳои бештар нишон деҳ» ё варақ задани шаҳрҳои ёфтшуда (count то 100)
C. КриптоарзҳоCoinGecko (endpoint-и оммавӣ)https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&per_page=10&page=1; ҷустуҷӯ — дар клиент аз рӯи name ва symbolСаҳифаҳо: per_page + page. Филтр: меафзояд / меафтад (price_change_percentage_24h). Маҳдудияти дархостҳо хурд аст: ҳангоми 429 паёми фаҳмо нишон диҳед ва бо дархостҳо спам накунед
D. Рик ва МортиRick and Morty API…/api/character?name=…; чизе нест — 404page, info.pages; филтрҳои серверии status (alive / dead / unknown) ва gender. Ҳама дар сервер — варианти аз ҳама осон

API-и оммавии худиро бе калид ҳам гирифтан мумкин аст — аммо пешакӣ бо муаллим мувофиқа кунед ва санҷед: оё аз браузер ҷавоб медиҳад (CORS) ва оё ҷустуҷӯ ва саҳифаҳо дорад.

Дар барнома чӣ бояд бошад

  1. Маълумот аз API. Функсияи getJson (аз дарси 14): санҷиши response.ok, try / catch, паёми фаҳмои хато.
  2. Суроға — тавассути URLSearchParams. Ягон часпонидани параметрҳо бо сатрҳо не. Функсияи buildUrl ё монанди он.
  3. Рендери рӯйхат. Як функсияи render, ки аввал тоза мекунад; матн тавассути textContent гузошта мешавад; ҳолати «Ничего не найдено» ҳаст.
  4. Ҷустуҷӯ бо debounce 300–500 мс ва ҳимоя аз мусобиқа (lastRequest ё AbortController).
  5. Филтр ё мураттабкунӣ — ҳадди ақал яке, дар сервер ё дар клиент (бошуурона интихоб кунед ва дар README нависед, чаро).
  6. Пагинатсия: саҳифаҳо (тугмаҳои «Ба қафо» / «Ба пеш» бо disabled дар канорҳо ва ёддошти «Саҳифаи N аз M») ё «Боз нишон деҳ». Ҳангоми ҷустуҷӯи нав саҳифа барқарор мешавад.
  7. Ҳолатҳои ҳадди ақал: «Боркунӣ…», «Ничего не найдено», «Хато» (муфассал — дар дарси оянда).
  8. README: кадом API гирифта шуд, истинод ба ҳуҷҷатгузорӣ, параметрҳои ҷустуҷӯ ва саҳифаҳо чӣ тавр сохта шудаанд, кадом дархостро дастӣ санҷидед.
  9. ЗС: ҳуҷҷатгузориро бо ЗС аз рӯи промпти дарс хонед ва ба README илова кунед: промпт, ЗС чӣ пешниҳод кард, чӣ нодуруст ё ғайридақиқ баромад ва шумо инро бо дархост чӣ тавр санҷидед.

Чӣ супоридан лозим

  • Истинод ба репозиторийи GitHub (ҷузвдон бо index.html, style.css, script.js ва README).
  • Ду-се ҷумла дар README: чӣ анҷом ёфт ва чӣ аз ҳама душвор баромад.
  • Скриншоти барнома (бо натиҷаҳои ҷустуҷӯ) — ихтиёрӣ, ҳамчун замима ба README.

Чӣ тавр баҳо медиҳем (0–10)

ЧӣХолҳо
Маълумот аз API бор мешавад, суроғаҳо тавассути URLSearchParams, хатоҳои шабака коркард шудаанд2
Рендер: тозакунӣ, textContent, «Ничего не найдено»2
Ҷустуҷӯ бо debounce ва ҳимоя аз мусобиқа, барқароркунии саҳифа ҳангоми ҷустуҷӯи нав2
Пагинатсия дар ҳамаи саҳифаҳо кор мекунад, тугмаҳо дар канорҳо маҳкам мешаванд2
Филтр ё мураттабкунӣ + README ва таҳлили ҷавоби ЗС2
Пеш аз супоридан

Сенарияро дастӣ гузаронед: чизеро ёбед, ба саҳифаи 2 варақ занед, ҷустуҷӯи нав ворид кунед (ба саҳифаи 1 баргаштед?), чизи бемаъное ворид кунед («Ничего не найдено» ҳаст?), интернетро хомӯш кунед (паёми хато ҳаст?). Ҳар сатреро, ки ЗС маслиҳат дод, шумо бояд дар ҳимоя шарҳ дода тавонед.

✓ Лавҳаи ёддошти дарс

  • Параметрҳои дархост баъд аз ? меоянд: калид=қимат тавассути &. Ҷамъ мекунем танҳо тавассути new URLSearchParams({…}) — вай фосилаҳо, & ва кириллитсаро рамзгузорӣ мекунад. params.get() ҳамеша сатр ё null медиҳад.
  • API-ро мехонем: ҳуҷҷатгузорӣ + ҷавоби воқеӣ дар консол. Рӯйхат дар куҷост (data.products / data.results / худи массив), total дар куҷост, ҳангоми «холӣ» чӣ мешавад (200 ва [] ё 404).
  • Рендер: render(items) ҳамеша аввал тоза мекунад (replaceChildren()), баъд гиреҳҳо месозад; маълумоти сервер — тавассути textContent; натиҷаи холӣ — паёми ҷудогона.
  • Ҷустуҷӯ: input + debounce (300–500 мс) + trim() + барқарор кардан ба саҳифаи аввал. Аз «мусобиқа» — рақами дархости lastRequest ё AbortController.
  • Филтр ва мураттабкунӣ: дар сервер (/category/…, sortBy, order) — барои маълумоти калон; дар клиент (filter, includes) — барои аллакай боршуда. На ҳамаи параметрҳои API бо ҳам мувофиқанд — ҳуҷҷатгузориро бихонед.
  • Пагинатсия: skip = (page − 1) × limit; саҳифаҳо Math.ceil(total / limit); «Ба қафо» дар саҳифаи 1 хомӯш аст, «Ба пеш» — дар охирин; дар Rick and Morty — page ва info.pages / info.next.
  • Сохтори барнома: state → buildUrl() → load() → render() + updatePager(). Ҳодисаҳо танҳо ҳолатро тағйир медиҳанд ва load()-ро даъват мекунанд.
  • Бо ЗС: ҳуҷҷатгузории API-ро ба ӯ бо матн диҳед; параметрҳои ихтироъшударо бо дархост дар консол мегирем; дар README менависем, ки ЗС чӣ пешниҳод кард ва мо чиро ислоҳ кардем.