Дарси 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 (кадом қисмро баргардонданд). Онҳо барои пагинатсия лозим мешаванд.
DummyJSON Rick and Morty API JSONPlaceholder Рӯйхат дар куҷост 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 дақиқа чӣ тавр хондан
Эндпойнти лозимаро ёбед. «Ба ман ҷустуҷӯи мол лозим» → GET /products/search.
Параметрҳоро нависед: кадомаш ҳатмӣ, кадомаш не, кадом қиматҳо иҷозат дода мешаванд (limit, skip, select, sortBy, order).
Намунаи ҷавобро кушоед ва ёбед, ки рӯйхат дар куҷо ва майдонҳои хидматӣ дар куҷо ҳастанд.
Бо даст санҷед: суроғаро ба браузер ё ба console.log гузоред ва боварӣ ҳосил кунед, ки ҷавоб чунон аст, ки дар ҳуҷҷатгузорӣ.
Дар бораи «холӣ» ва «хато» бидонед: агар ҳеҷ чиз набошад, кадом статус ва кадом бадан меояд.
Як усули дигар: параметри 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 мекардед, фақат массив акнун на аз тағйирёбанда, балки аз интернет меояд.
Маълумотро дархост кардем : await fetch(url), await response.json().
Рӯйхатро гирифтем аз лифофа: data.products.
Контейнерро тоза кардем : list.replaceChildren().
Барои ҳар мол <li> / корт сохтем, textContent-ро пур кардем ва ба контейнер илова намудем.
Рӯйхати мол аз API · Ctrl + Enter ↺ Аз нав ▶ Иҷро кардан
index.html script.js
< h3 > Первые 5 товаров</ h3 >
< ul id ="list" > </ ul >
const list = document.querySelector ("#list" );
async function loadProducts () {
const response = await fetch ("https://dummyjson.com/products?limit=5&select=title,price" );
const data = await response.json ();
list.replaceChildren (); // 3. тоза кардем
for (const product of data.products) { // 4. барои ҳар мол як гиреҳ
const li = document.createElement ("li" );
li.textContent = `${product.title} — ${product.price} $` ;
list.append (li);
}
}
loadProducts ();
Пешнамоиш Пӯшонидан
Барои дидани натиҷа «Иҷро кардан»-ро пахш кунед.
Қиёс аз ҳаёт
Рендер — ин витринаи бозор аст. Ҳар субҳ фурӯшанда молро болои дирӯза илова намекунад, балки витринаро аз нав мегузорад : кӯҳнаро бардошт — навро гузошт. Функсияи 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 ↺ Аз нав ▶ Иҷро кардан
index.html style.css script.js
< div id ="grid" class ="grid" > </ div >
.grid { display : grid; grid-template-columns : repeat(auto-fill, minmax(130px , 1fr )); gap : 10px ; }
.card { border : 1px solid #ccc ; border-radius : 10px ; padding : 8px ; text-align : center; font : 14px system-ui; }
.card img { width : 100 %; height : 90px ; object-fit : contain; }
.card h3 { font-size : 14px ; margin : 6px 0 2px ; }
.card p { margin : 0 ; color : #0a7c5a ; font-weight : 700 ; }
const grid = document.querySelector ("#grid" );
async function loadCards () {
const response = await fetch ("https://dummyjson.com/products?limit=6&select=title,price,thumbnail" );
const data = await response.json ();
grid.replaceChildren ();
for (const product of data.products) {
const card = document.createElement ("article" );
card.className = "card" ;
const img = document.createElement ("img" );
img.src = product.thumbnail;
img.alt = product.title;
const title = document.createElement ("h3" );
title.textContent = product.title;
const price = document.createElement ("p" );
price.textContent = `${product.price} $` ;
card.append (img, title, price);
grid.append (card);
}
}
loadCards ();
Пешнамоиш Пӯшонидан
Барои дидани натиҷа «Иҷро кардан»-ро пахш кунед.
Рӯйхати холӣ — ҳам ҳолат аст
Агар сервер сифр мол баргардонад, корбар бояд «Ничего не найдено»-ро бинад, на ҷои холиро. Дар бораи ҳолатҳои боркунӣ ва хатоҳо дар дарси оянда муфассал сӯҳбат мекунем, ҳоло бошад ҳадди ақалро мегузорем: render натиҷаи холиро нишон дода метавонад.
Муфассалтар хонед: learn.javascript.ru — Fetch · MDN — Element.replaceChildren
Амал: мустаҳкам мекунем
Чор вазифа дар мавзӯи рендер. Натиҷа — дар пешнамоиш ва дар консоли зери майдони озмоиш.
ВАЗИФАИ 3.1 Панҷ мол ба рӯйхат осон
loadProducts-ро анҷом диҳед: барои ҳар мол <li> бо матни название — цена $ (хатча бо фосилаҳо дар гирду атроф) созед ва ба list илова кунед. Консол санҷишро аллакай чоп мекунад: дар рӯйхат чанд унсур ҳаст ва оё дар аввалӣ хатча ҳаст.
Рӯйхати мол · Ctrl + Enter ↺ Аз нав ▶ Иҷро кардан
index.html script.js
const list = document.querySelector ("#list" );
async function loadProducts () {
const response = await fetch ("https://dummyjson.com/products?limit=5&select=title,price" );
const data = await response.json ();
// барои ҳар мол: li созед, матнро нависед, ба list илова кунед
}
// --- санҷиш, тағйир надиҳед ---
loadProducts ().then (() => {
console.log (list.children.length);
console.log (list.firstElementChild.textContent.includes (" — " ));
});
Пешнамоиш Пӯшонидан
Барои дидани натиҷа «Иҷро кардан»-ро пахш кунед.
Натиҷаи интизоршаванда 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 ↺ Аз нав ▶ Иҷро кардан
index.html style.css script.js
< div id ="grid" class ="grid" > </ div >
.grid { display : grid; grid-template-columns : repeat(auto-fill, minmax(120px , 1fr )); gap : 8px ; }
.card { border : 1px solid #ccc ; border-radius : 10px ; padding : 8px ; text-align : center; font : 13px system-ui; }
.card img { width : 100 %; height : 70px ; object-fit : contain; }
.card h3 { font-size : 13px ; margin : 4px 0 2px ; }
.card p { margin : 0 ; font-weight : 700 ; }
const grid = document.querySelector ("#grid" );
async function loadCards () {
const response = await fetch ("https://dummyjson.com/products?limit=6&select=title,price,thumbnail" );
const data = await response.json ();
// коди шумо: корт = article.card + img + h3 + p
}
// --- санҷиш, тағйир надиҳед ---
loadCards ().then (() => {
console.log (grid.querySelectorAll (".card" ).length);
console.log ([... grid.querySelectorAll ("img" )].filter ((img) => img.alt !== "" ).length);
});
Пешнамоиш Пӯшонидан
Барои дидани натиҷа «Иҷро кардан»-ро пахш кунед.
Натиҷаи интизоршаванда 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 ↺ Аз нав ▶ Иҷро кардан
index.html script.js
const list = document.querySelector ("#list" );
async function loadProducts () {
const response = await fetch ("https://dummyjson.com/products?limit=3&select=title" );
const data = await response.json ();
data.products.forEach ((product) => {
const li = document.createElement ("li" );
li.textContent = product.title;
list.append (li);
});
}
// --- санҷиш, тағйир надиҳед ---
loadProducts ()
.then (() => loadProducts ())
.then (() => console.log (list.children.length));
Пешнамоиш Пӯшонидан
Барои дидани натиҷа «Иҷро кардан»-ро пахш кунед.
Ишора 1 Ҳар даъват li-ҳои навро ба онҳое, ки аллакай ҳастанд, илова мекунад. Пеш аз давра чӣ намерасад?
Ишора 2 list.replaceChildren();-ро баъд аз гирифтани маълумот, пеш аз давра илова кунед.
○ Ҳалшуда қайд кардан 💬 Дар бораи ин вазифа пурсидан
ВАЗИФАИ 3.4 renderList бо ҳолати холӣ душвор
renderList(items)-ро нависед (items — массиви сатрҳо). Вай ҳамеша аввал list-ро тоза мекунад. Барои массиви холӣ як <li class="empty">Ничего не найдено</li> илова мекунад, барои холинабуда — барои ҳар сатр як <li>. Шабака лозим нест.
renderList · Ctrl + Enter ↺ Аз нав ▶ Иҷро кардан
index.html script.js
const list = document.querySelector ("#list" );
function renderList (items) {
// коди шумо
}
// --- санҷиш, тағйир надиҳед ---
renderList (["Самбуса" , "Курутоб" ]);
console.log (list.children.length);
renderList ([]);
console.log (`${list.children.length} ${list.textContent}` );
renderList (["Чой" ]);
console.log (list.textContent);
Пешнамоиш Пӯшонидан
Барои дидани натиҷа «Иҷро кардан»-ро пахш кунед.
Натиҷаи интизоршаванда 2
1 Ничего не найдено
Чой
Ишора 1 Сатри аввал — list.replaceChildren();. Баъд шохаи if (items.length === 0) { ...; return; }.
Ишора 2 Дар шохаи холӣ li созед, className = "empty" ва матнро муқаррар кунед, ба рӯйхат илова намоед ва return нависед. Поёнтар, барои холинабуда, давраи оддии for (const item of items).
○ Ҳалшуда қайд кардан 💬 Дар бораи ин вазифа пурсидан
04 Ҷустуҷӯ: input, debounce ва «мусобиқаи» дархостҳо
Ҷустуҷӯ дар API — ин ҳамон дархост бо параметри q аст. Мушкилӣ на дар дархост, балки дар он аст, ки кай фиристодани он . Агар ҳодисаи input-ро «рӯирост» гӯш кунем, барои калимаи «phone» панҷ дархост мефиристем — барои «p», «ph», «pho», «phon», «phone». Ин сарбории зиёдатӣ ба сервер ва интерфейси милликунанда аст.
Ҷустуҷӯи мол: параметри q · Ctrl + Enter ↺ Аз нав ▶ Иҷро кардан
async function search (q) {
const params = new URLSearchParams ({ q, limit: 5 , select: "title" });
const response = await fetch ("https://dummyjson.com/products/search?" + params);
const data = await response.json ();
return data.products;
}
async function main () {
const found = await search ("phone" );
console.log ("нашли:" , found.length > 0 );
found.forEach ((p) => console.log ("•" , p.title));
const none = await search ("zzzzqq" );
console.log ("по чепухе:" , none.length); // 0 — оддӣ рӯйхати холӣ
}
main ();
Debounce: интизор мешавем, то одам гапашро тамом кунад
Debounce («зидди ларзиш») амалро ба таъхир мегузорад ва ҳар даъвати нав интизориро аз сар оғоз мекунад. Амал танҳо дар сурате кор мекунад, ки корбар барои вақти додашуда хомӯш шуд — одатан 300–500 мс. Ин ҳамон setTimeout ва clearTimeout аз дарси 13 аст.
Қиёс аз ҳаёт
Пешхизмат дар чойхона баъд аз ҳар калимаи меҳмон ба ошхона намедавад. Вай интизор мешавад, то меҳмон гапашро тамом кунад: «курутоб… ва самбуса… ва чой… тамом». Танҳо баъд аз танаффус назди ошпаз меравад. Debounce — ин пешхизмати сабур аст: як танаффус дар сӯҳбат — як рафтан ба ошхона.
debounce дар шаш сатр · Ctrl + Enter ↺ Аз нав ▶ Иҷро кардан
function debounce (fn, ms) {
let timer; // таймер байни даъватҳо зинда мемонад
return (... args) => {
clearTimeout (timer); // даъвати нав қаблиро бекор мекунад
timer = setTimeout (() => fn (... args), ms);
};
}
const find = debounce ((text) => console.log ("ищем:" , text), 300 );
find ("п" );
find ("пл" );
find ("пло" );
find ("плов" ); // танҳо даъвати охирин кор мекунад — баъд аз 300 мс
Функсия функсияи нав бармегардонад, ки дар дохили он таймер «пинҳон» аст (closure — шумо онро дар дарси 5 дидаед). Обуна дар барнома чунин менамояд:
Обуна ба input тавассути debounce
input.addEventListener ("input" , debounce (() => {
state.q = input.value.trim ();
state.page = 1 ; // ҷустуҷӯи нав — ҳамеша аз саҳифаи аввал!
load ();
}, 400 ));
«Мусобиқа»-и дархостҳо: вақте ҷавоби кӯҳна баъд аз нав меояд
Ҳатто бо debounce чунин мешавад: шумо дархостро барои «ph» фиристодед, фикратон гашт ва «phone»-ро фиристодед. Сервер ба дархости дуюм тезтар, ба якум — дертар ҷавоб дод. Агар ҷавобҳоро ҳангоми омадан нишон диҳем, кӯҳна навро бознависӣ мекунад ва дар майдон «phone», дар рӯйхат — натиҷаҳо барои «ph» мешавад. Ин мусобиқаи дархостҳо (race condition) номида мешавад.
Қиёс аз ҳаёт
Шумо палав фармоиш додед, баъд аз як дақиқа фикратон гашт ва самбуса фармоиш додед. Агар ошпазон хӯрокҳоро аз рӯи омодагӣ биёранд, самбуса аввал меояд, баъд палави нолозимро «мерасонанд» — ва шумо чизи нодурустро мехӯред. Дуруст: ба ҳар фармоиш рақам додан ва танҳо хӯроки бо рақами охирин -ро қабул кардан.
Хато: ҷавоби кӯҳна нави навро бознависӣ мекунад · Ctrl + Enter ↺ Аз нав ▶ Иҷро кардан
const delays = { ph: 300 , phone: 100 }; // ба «ph» сервер дертар ҷавоб медиҳад
const fakeFetch = (q) => new Promise ((resolve) =>
setTimeout (() => resolve ("результаты для " + q), delays[q]));
async function search (q) {
const result = await fakeFetch (q);
console.log ("показано:" , result);
}
search ("ph" );
setTimeout (() => search ("phone" ), 50 );
// дар экран «ph»-и кӯҳна мемонад — корбар на он чиро, ки ҷустуҷӯ кард, мебинад
Дору оддӣ аст: ҳар дархост рақами худро дорад, танҳо ҷавобро ба дархости аз ҳама наве нишон медиҳем:
Ислоҳ: рақами дархост · Ctrl + Enter ↺ Аз нав ▶ Иҷро кардан
const delays = { ph: 300 , phone: 100 };
const fakeFetch = (q) => new Promise ((resolve) =>
setTimeout (() => resolve ("результаты для " + q), delays[q]));
let lastRequest = 0 ; // рақами дархости охирин
async function search (q) {
const id = ++lastRequest; // рақами худро мегирем
const result = await fakeFetch (q);
if (id !== lastRequest) return ; // дар ин миён дархости нав рафт — партофта мешавад
console.log ("показано:" , result);
}
search ("ph" );
setTimeout (() => search ("phone" ), 50 );
«Калонсолона» роҳ ҳам ҳаст — дархости кӯҳнаро тавассути AbortController бекор кардан: браузер интизориро қатъ мекунад, fetch бошад хатоеро бо номи AbortError мепартояд, ки онро бояд оромона фурӯ бурд.
Бекор кардани дархост: AbortController · Ctrl + Enter ↺ Аз нав ▶ Иҷро кардан
const controller = new AbortController ();
fetch ("https://dummyjson.com/products?limit=3" , { signal: controller.signal })
.then (() => console.log ("ответ пришёл" ))
.catch ((error) => console.log ("отменён:" , error.name));
controller.abort (); // «ҷавобро интизор нестам»
Кадомро интихоб кардан
Барои оғоз — ҳисобкунаки lastRequest: вай дар ҳама ҷо кор мекунад ва аз назари аввал фаҳмо аст. AbortController трафикро сарфа мекунад, аммо коркарди эҳтиётонаи AbortError-ро металабад. Дар барномаи мо ҳисобкунак мешавад.
Муфассалтар хонед: learn.javascript.ru — setTimeout ва setInterval · Fetch: қатъи дархост
Амал: мустаҳкам мекунем
Ҷустуҷӯ, debounce ва ҳимоя аз мусобиқа.
ВАЗИФАИ 4.1 Функсияи search осон
search(q)-ро нависед: вай https://dummyjson.com/products/search-ро бо параметрҳои q, limit=5, select=title (тавассути URLSearchParams) дархост мекунад ва массиви products-ро бармегардонад. Санҷиш аллакай навишта шудааст.
Таҳрир кардан мумкин аст · Ctrl + Enter ↺ Аз нав ▶ Иҷро кардан
async function search (q) {
// коди шумо
}
async function main () {
console.log ((await search ("phone" )).length > 0 );
console.log ((await search ("zzzzqq" )).length);
}
main ();
Натиҷаи интизоршаванда true
0
Ишора 1 Параметрҳо: new URLSearchParams({ q, limit: 5, select: "title" }). Суроға: "https://dummyjson.com/products/search?" + params.
Ишора 2 Минбаъд мисли маъмул: await fetch, await response.json(), return data.products.
○ Ҳалшуда қайд кардан 💬 Дар бораи ин вазифа пурсидан
ВАЗИФАИ 4.2 Пешгӯӣ кун: ҷустуҷӯ чанд бор кор мекунад миёна
debounce(fn, ms)-ро анҷом диҳед. Баъд пеш аз иҷро нависед, ки кадом сатрҳо пайдо мешаванд: се даъвати тез дар лаҳзаи 0 мс, боз як дар миллисонияи 100-ум ва охирин — дар 600-ум. Танаффуси debounce — 200 мс.
Таҳрир кардан мумкин аст · Ctrl + Enter ↺ Аз нав ▶ Иҷро кардан
function debounce (fn, ms) {
// коди шумо
}
const log = debounce ((text) => console.log ("ищем:" , text), 200 );
log ("п" );
log ("пл" );
log ("пло" );
setTimeout (() => log ("плов" ), 100 );
setTimeout (() => log ("плов!" ), 600 );
Натиҷаи интизоршаванда ищем: плов
ищем: плов!
Ишора 1 Тағйирёбандаи timer дар берун аз функсияи баргардонидашаванда лозим аст, то вай таймери қаблиро «ба ёд дошта бошад».
Ишора 2 return (...args) => { clearTimeout(timer); timer = setTimeout(() => fn(...args), ms); };
○ Ҳалшуда қайд кардан 💬 Дар бораи ин вазифа пурсидан
ВАЗИФАИ 4.3 Майдони ҷустуҷӯ бо debounce миёна
Дар майдони озмоиш debounce ва ҷустуҷӯи «қалбакӣ»-и fakeSearch аллакай ҳаст. Ба ҳодисаи input обуна шавед: тавассути debounce бо танаффуси 300 мс fakeSearch(input.value.trim())-ро даъват кунед. Дар поён код тақлиди навиштани калимаи «phone»-ро бо танаффуси 50 мс байни ҳарфҳо мекунад.
Ҷустуҷӯ бо debounce · Ctrl + Enter ↺ Аз нав ▶ Иҷро кардан
index.html script.js
< input id ="q" type ="search" placeholder ="Поиск" >
< ul id ="list" > </ ul >
const input = document.querySelector ("#q" );
let requests = 0 ;
function fakeSearch (text) {
requests++;
console.log (`запрос: ${text}` );
}
function debounce (fn, ms) {
let timer;
return (... args) => {
clearTimeout (timer);
timer = setTimeout (() => fn (... args), ms);
};
}
// коди шумо: обуна ба input тавассути debounce
// --- тақлиди навиштан, тағйир надиҳед ---
[... "phone" ].forEach ((char, i) => {
setTimeout (() => {
input.value += char;
input.dispatchEvent (new Event ("input" ));
}, i * 50 );
});
setTimeout (() => console.log (`всего запросов: ${requests}` ), 1000 );
Пешнамоиш Пӯшонидан
Барои дидани натиҷа «Иҷро кардан»-ро пахш кунед.
Натиҷаи интизоршаванда запрос: phone
всего запросов: 1
Ишора 1 Коркардкунандаи ҳодиса — ин натиҷаи даъвати debounce(...) аст: input.addEventListener("input", debounce(…, 300));
Ишора 2 Дар дохил функсияи тирдор диҳед: () => fakeSearch(input.value.trim()).
○ Ҳалшуда қайд кардан 💬 Дар бораи ин вазифа пурсидан
ВАЗИФАИ 4.4 Хаторо ёб: мусобиқаи дархостҳо душвор
Ду ҷустуҷӯ оғоз мешавад: «ph» (сервер дар 300 мс ҷавоб медиҳад) ва баъд аз 50 мс «phone» (дар 100 мс). Ҳоло ҳарду натиҷа нишон дода мешавад, ва кӯҳнашуда охир меояд. search-ро чунон ислоҳ кунед, ки танҳо натиҷаи дархости аз ҳама нав нишон дода шавад.
Таҳрир кардан мумкин аст · Ctrl + Enter ↺ Аз нав ▶ Иҷро кардан
const delays = { ph: 300 , phone: 100 };
const fakeFetch = (q) => new Promise ((resolve) =>
setTimeout (() => resolve ("результаты для " + q), delays[q]));
function show (text) { console.log ("показано:" , text); }
async function search (q) {
const result = await fakeFetch (q);
show (result);
}
search ("ph" );
setTimeout (() => search ("phone" ), 50 );
Натиҷаи интизоршаванда показано: результаты для phone
Ишора 1 Дар берун аз функсия ҳисобкунаки let lastRequest = 0;-ро созед ва дар аввали search рақам гиред: const id = ++lastRequest;
Ишора 2 Баъд аз await муқоиса кунед: if (id !== lastRequest) return; — агар рақам охирин набошад, ҷавоб кӯҳна шудааст.
○ Ҳалшуда қайд кардан 💬 Дар бораи ин вазифа пурсидан
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 ↺ Аз нав ▶ Иҷро кардан
index.html script.js
< select id ="category" > </ select >
< ul id ="list" > </ ul >
const menu = [
{ title: "Самбуса с мясом" , category: "выпечка" },
{ title: "Самбуса с тыквой" , category: "выпечка" },
{ title: "Курутоб" , category: "горячее" },
{ title: "Оши палав" , category: "горячее" },
{ title: "Манту" , category: "горячее" },
{ title: "Шакароб" , category: "салаты" },
];
const select = document.querySelector ("#category" );
const list = document.querySelector ("#list" );
function addOption (value, text) {
const option = document.createElement ("option" );
option.value = value;
option.textContent = text;
select.append (option);
}
function renderList (items) {
list.replaceChildren ();
for (const item of items) {
const li = document.createElement ("li" );
li.textContent = item.title;
list.append (li);
}
}
// 1) select-ро пур кунед: «Все» + категорияҳои беназир
// 2) тамоми менюро нишон диҳед, баъд ба change обуна шавед
// --- санҷиш, тағйир надиҳед ---
console.log (`опций: ${select.options.length}` );
select.value = "горячее" ;
select.dispatchEvent (new Event ("change" ));
console.log (`горячее: ${list.children.length}` );
select.value = "" ;
select.dispatchEvent (new Event ("change" ));
console.log (`все: ${list.children.length}` );
Пешнамоиш Пӯшонидан
Барои дидани натиҷа «Иҷро кардан»-ро пахш кунед.
Натиҷаи интизоршаванда опций: 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)-ро пеш аз обуна фаромӯш накунед.
○ Ҳалшуда қайд кардан 💬 Дар бораи ин вазифа пурсидан
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 ↺ Аз нав ▶ Иҷро кардан
index.html style.css script.js
< p id ="status" > </ p >
< ul id ="list" class ="grid" > </ ul >
body { font : 14px system-ui; margin : 8px ; }
.grid { list-style : none; padding : 0 ; margin : 0 ; display : grid; grid-template-columns : repeat(auto-fill, minmax(110px , 1fr )); gap : 8px ; }
.card { border : 1px solid #ccc ; border-radius : 8px ; padding : 6px ; text-align : center; }
.card img { width : 100 %; height : 60px ; object-fit : contain; }
.card h3 { font-size : 12px ; margin : 4px 0 0 ; }
.card p { margin : 2px 0 0 ; font-weight : 700 ; }
const API = "https://dummyjson.com/products" ;
const LIMIT = 6 ;
const state = { q: "" , category: "" , page: 1 };
const list = document.querySelector ("#list" );
const statusEl = document.querySelector ("#status" );
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}` ;
}
function render (products) {
list.replaceChildren ();
if (products.length === 0 ) {
statusEl.textContent = "Ничего не найдено" ;
return ;
}
statusEl.textContent = "" ;
for (const product of products) {
const li = document.createElement ("li" );
li.className = "card" ;
const img = document.createElement ("img" );
img.src = product.thumbnail;
img.alt = product.title;
const name = document.createElement ("h3" );
name.textContent = product.title;
const price = document.createElement ("p" );
price.textContent = `${product.price} $` ;
li.append (img, name, price);
list.append (li);
}
}
async function load () {
statusEl.textContent = "Загрузка…" ;
const response = await fetch (buildUrl ());
const data = await response.json ();
render (data.products);
}
load ();
Пешнамоиш Пӯшонидан
Барои дидани натиҷа «Иҷро кардан»-ро пахш кунед.
Қадами 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 ↺ Аз нав ▶ Иҷро кардан
index.html style.css script.js
< 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 >
body { font : 14px system-ui; margin : 8px ; }
.bar { display : flex; gap : 8px ; margin-bottom : 8px ; }
.bar input { flex : 1 ; padding : 6px 8px ; }
.status { color : #666 ; min-height : 1.2em ; margin : 4px 0 ; }
.grid { list-style : none; padding : 0 ; margin : 0 ; display : grid; grid-template-columns : repeat(auto-fill, minmax(110px , 1fr )); gap : 8px ; }
.card { border : 1px solid #ccc ; border-radius : 8px ; padding : 6px ; text-align : center; }
.card img { width : 100 %; height : 60px ; object-fit : contain; }
.card h3 { font-size : 12px ; margin : 4px 0 0 ; }
.card p { margin : 2px 0 0 ; font-weight : 700 ; }
.pager { display : flex; gap : 12px ; align-items : center; justify-content : center; margin-top : 10px ; }
const API = "https://dummyjson.com/products" ;
const LIMIT = 6 ;
const state = { q: "" , category: "" , page: 1 , pages: 1 };
const list = document.querySelector ("#list" );
const statusEl = document.querySelector ("#status" );
const info = document.querySelector ("#info" );
const prev = document.querySelector ("#prev" );
const next = document.querySelector ("#next" );
const qInput = document.querySelector ("#q" );
const catSelect = document.querySelector ("#category" );
function debounce (fn, ms) {
let timer;
return (... args) => {
clearTimeout (timer);
timer = setTimeout (() => fn (... args), ms);
};
}
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}` ;
}
function render (products) {
list.replaceChildren ();
if (products.length === 0 ) {
statusEl.textContent = "Ничего не найдено" ;
return ;
}
statusEl.textContent = "" ;
for (const product of products) {
const li = document.createElement ("li" );
li.className = "card" ;
const img = document.createElement ("img" );
img.src = product.thumbnail;
img.alt = product.title;
const name = document.createElement ("h3" );
name.textContent = product.title;
const price = document.createElement ("p" );
price.textContent = `${product.price} $` ;
li.append (img, name, price);
list.append (li);
}
}
function updatePager () {
info.textContent = `Страница ${state.page} из ${state.pages}` ;
prev.disabled = state.page <= 1 ;
next.disabled = state.page >= state.pages;
}
let lastRequest = 0 ;
async function load () {
const id = ++lastRequest;
statusEl.textContent = "Загрузка…" ;
try {
const response = await fetch (buildUrl ());
if (!response.ok) throw new Error ("HTTP " + response.status);
const data = await response.json ();
if (id !== lastRequest) return ;
state.pages = Math.max (1 , Math.ceil (data.total / LIMIT));
render (data.products);
updatePager ();
} catch (error) {
if (id !== lastRequest) return ;
list.replaceChildren ();
statusEl.textContent = "Не удалось загрузить: " + error.message;
}
}
async function go (page) {
if (page < 1 || page > state.pages) return ;
state.page = page;
await load ();
}
async function search (text) {
state.q = text.trim ();
state.category = "" ;
catSelect.value = "" ;
state.page = 1 ;
await load ();
}
async function pickCategory (value) {
state.category = value;
state.q = "" ;
qInput.value = "" ;
state.page = 1 ;
await load ();
}
async function loadCategories () {
const response = await fetch (`${API}/category-list` );
const names = await response.json ();
for (const name of names) {
const option = document.createElement ("option" );
option.value = name;
option.textContent = name;
catSelect.append (option);
}
}
prev.addEventListener ("click" , () => go (state.page - 1 ));
next.addEventListener ("click" , () => go (state.page + 1 ));
catSelect.addEventListener ("change" , () => pickCategory (catSelect.value));
qInput.addEventListener ("input" , debounce (() => search (qInput.value), 400 ));
loadCategories ();
load ();
Пешнамоиш Пӯшонидан
Барои дидани натиҷа «Иҷро кардан»-ро пахш кунед.
Дар ин ҷо чиро қайд кардан муҳим аст
Як сарчашмаи ҳақиқат: state. Тугмаҳо, ҷустуҷӯ ва select рӯйхатро худашон намекашанд, онҳо ҳолатро тағйир медиҳанд ва load()-ро садо мекунанд. Як «рассом»: render ҳамеша рӯйхатро пурра аз нав месозад. Як «мизи суроға»: buildUrl. Филтри нав илова кардан — маънои илова кардани майдон ба state, як сатр ба buildUrl ва коркардкунандаи ҳодисаро дорад. Боқиро ламс намекунем.
Амал: барномаро анҷом медиҳем
Дар ду вазифа — ҳамон барнома, аммо аз он як порча гирифта шудааст. Онро анҷом диҳед: майдони озмоиш худаш сенарияро мегузаронад ва санҷишро чоп мекунад (интернет лозим аст, санҷиш чанд сония мегирад).
ВАЗИФАИ 8.1 Пагинатсия: go ва updatePager миёна
Дар барнома функсияҳои updatePager (ёддошти «Страница N из M» ва disabled барои тугмаҳо) ва go(page) (санҷиши сарҳадҳо, иваз кардани саҳифа, боркунӣ) холӣ мебошанд. Онҳоро анҷом диҳед.
Каталог: пагинатсияро анҷом диҳед · Ctrl + Enter ↺ Аз нав ▶ Иҷро кардан
index.html style.css script.js
< 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 >
body { font : 14px system-ui; margin : 8px ; }
.bar { display : flex; gap : 8px ; margin-bottom : 8px ; }
.bar input { flex : 1 ; padding : 6px 8px ; }
.status { color : #666 ; min-height : 1.2em ; margin : 4px 0 ; }
.grid { list-style : none; padding : 0 ; margin : 0 ; display : grid; grid-template-columns : repeat(auto-fill, minmax(110px , 1fr )); gap : 8px ; }
.card { border : 1px solid #ccc ; border-radius : 8px ; padding : 6px ; text-align : center; }
.card img { width : 100 %; height : 60px ; object-fit : contain; }
.card h3 { font-size : 12px ; margin : 4px 0 0 ; }
.card p { margin : 2px 0 0 ; font-weight : 700 ; }
.pager { display : flex; gap : 12px ; align-items : center; justify-content : center; margin-top : 10px ; }
const API = "https://dummyjson.com/products" ;
const LIMIT = 6 ;
const state = { q: "" , category: "" , page: 1 , pages: 1 };
const list = document.querySelector ("#list" );
const statusEl = document.querySelector ("#status" );
const info = document.querySelector ("#info" );
const prev = document.querySelector ("#prev" );
const next = document.querySelector ("#next" );
const qInput = document.querySelector ("#q" );
const catSelect = document.querySelector ("#category" );
function debounce (fn, ms) {
let timer;
return (... args) => {
clearTimeout (timer);
timer = setTimeout (() => fn (... args), ms);
};
}
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}` ;
}
function render (products) {
list.replaceChildren ();
if (products.length === 0 ) {
statusEl.textContent = "Ничего не найдено" ;
return ;
}
statusEl.textContent = "" ;
for (const product of products) {
const li = document.createElement ("li" );
li.className = "card" ;
const img = document.createElement ("img" );
img.src = product.thumbnail;
img.alt = product.title;
const name = document.createElement ("h3" );
name.textContent = product.title;
const price = document.createElement ("p" );
price.textContent = `${product.price} $` ;
li.append (img, name, price);
list.append (li);
}
}
function updatePager () {
// TODO: ёддошти «Страница N из M» ва disabled барои prev / next
}
let lastRequest = 0 ;
async function load () {
const id = ++lastRequest;
statusEl.textContent = "Загрузка…" ;
try {
const response = await fetch (buildUrl ());
if (!response.ok) throw new Error ("HTTP " + response.status);
const data = await response.json ();
if (id !== lastRequest) return ;
state.pages = Math.max (1 , Math.ceil (data.total / LIMIT));
render (data.products);
updatePager ();
} catch (error) {
if (id !== lastRequest) return ;
list.replaceChildren ();
statusEl.textContent = "Не удалось загрузить: " + error.message;
}
}
async function go (page) {
// TODO: сарҳадҳоро санҷед, саҳифаро иваз кунед, бор кунед
}
async function search (text) {
state.q = text.trim ();
state.category = "" ;
catSelect.value = "" ;
state.page = 1 ;
await load ();
}
async function pickCategory (value) {
state.category = value;
state.q = "" ;
qInput.value = "" ;
state.page = 1 ;
await load ();
}
async function loadCategories () {
const response = await fetch (`${API}/category-list` );
const names = await response.json ();
for (const name of names) {
const option = document.createElement ("option" );
option.value = name;
option.textContent = name;
catSelect.append (option);
}
}
prev.addEventListener ("click" , () => go (state.page - 1 ));
next.addEventListener ("click" , () => go (state.page + 1 ));
catSelect.addEventListener ("change" , () => pickCategory (catSelect.value));
qInput.addEventListener ("input" , debounce (() => search (qInput.value), 400 ));
// --- санҷиш, тағйир надиҳед ---
async function check () {
await loadCategories ();
await load ();
console.log (`карточек: ${list.children.length}` );
console.log (`страниц больше одной: ${state.pages > 1}` );
console.log (`на первой странице «Назад» ${prev.disabled ? "выкл" : "вкл"}` );
await go (2 );
console.log (`страница ${state.page}, карточек: ${list.children.length}` );
await go (0 );
console.log (`go(0) оставила страницу ${state.page}` );
console.log (`назад ${prev.disabled ? "выкл" : "вкл"}, вперёд ${next.disabled ? "выкл" : "вкл"}` );
}
check ();
Пешнамоиш Пӯшонидан
Барои дидани натиҷа «Иҷро кардан»-ро пахш кунед.
Натиҷаи интизоршаванда карточек: 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 ↺ Аз нав ▶ Иҷро кардан
index.html style.css script.js
< 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 >
body { font : 14px system-ui; margin : 8px ; }
.bar { display : flex; gap : 8px ; margin-bottom : 8px ; }
.bar input { flex : 1 ; padding : 6px 8px ; }
.status { color : #666 ; min-height : 1.2em ; margin : 4px 0 ; }
.grid { list-style : none; padding : 0 ; margin : 0 ; display : grid; grid-template-columns : repeat(auto-fill, minmax(110px , 1fr )); gap : 8px ; }
.card { border : 1px solid #ccc ; border-radius : 8px ; padding : 6px ; text-align : center; }
.card img { width : 100 %; height : 60px ; object-fit : contain; }
.card h3 { font-size : 12px ; margin : 4px 0 0 ; }
.card p { margin : 2px 0 0 ; font-weight : 700 ; }
.pager { display : flex; gap : 12px ; align-items : center; justify-content : center; margin-top : 10px ; }
const API = "https://dummyjson.com/products" ;
const LIMIT = 6 ;
const state = { q: "" , category: "" , page: 1 , pages: 1 };
const list = document.querySelector ("#list" );
const statusEl = document.querySelector ("#status" );
const info = document.querySelector ("#info" );
const prev = document.querySelector ("#prev" );
const next = document.querySelector ("#next" );
const qInput = document.querySelector ("#q" );
const catSelect = document.querySelector ("#category" );
function debounce (fn, ms) {
let timer;
return (... args) => {
clearTimeout (timer);
timer = setTimeout (() => fn (... args), ms);
};
}
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}` ;
}
function render (products) {
list.replaceChildren ();
if (products.length === 0 ) {
statusEl.textContent = "Ничего не найдено" ;
return ;
}
statusEl.textContent = "" ;
for (const product of products) {
const li = document.createElement ("li" );
li.className = "card" ;
const img = document.createElement ("img" );
img.src = product.thumbnail;
img.alt = product.title;
const name = document.createElement ("h3" );
name.textContent = product.title;
const price = document.createElement ("p" );
price.textContent = `${product.price} $` ;
li.append (img, name, price);
list.append (li);
}
}
function updatePager () {
info.textContent = `Страница ${state.page} из ${state.pages}` ;
prev.disabled = state.page <= 1 ;
next.disabled = state.page >= state.pages;
}
let lastRequest = 0 ;
async function load () {
const id = ++lastRequest;
statusEl.textContent = "Загрузка…" ;
try {
const response = await fetch (buildUrl ());
if (!response.ok) throw new Error ("HTTP " + response.status);
const data = await response.json ();
if (id !== lastRequest) return ;
state.pages = Math.max (1 , Math.ceil (data.total / LIMIT));
render (data.products);
updatePager ();
} catch (error) {
if (id !== lastRequest) return ;
list.replaceChildren ();
statusEl.textContent = "Не удалось загрузить: " + error.message;
}
}
async function go (page) {
if (page < 1 || page > state.pages) return ;
state.page = page;
await load ();
}
async function search (text) {
// TODO: матнро нависед, категория ва саҳифаро барқарор кунед, бор кунед
}
async function pickCategory (value) {
state.category = value;
state.q = "" ;
qInput.value = "" ;
state.page = 1 ;
await load ();
}
async function loadCategories () {
const response = await fetch (`${API}/category-list` );
const names = await response.json ();
for (const name of names) {
const option = document.createElement ("option" );
option.value = name;
option.textContent = name;
catSelect.append (option);
}
}
prev.addEventListener ("click" , () => go (state.page - 1 ));
next.addEventListener ("click" , () => go (state.page + 1 ));
catSelect.addEventListener ("change" , () => pickCategory (catSelect.value));
qInput.addEventListener ("input" , debounce (() => search (qInput.value), 400 ));
// --- санҷиш, тағйир надиҳед ---
async function check () {
await load ();
await go (3 );
console.log (`страница: ${state.page}` );
await search ("phone" );
console.log (`поиск: карточек больше нуля: ${list.children.length > 0}` );
console.log (`страница после поиска: ${state.page}` );
await search ("zzzzqq" );
console.log (`пусто: ${list.children.length === 0}, сообщение: ${statusEl.textContent}` );
await search ("" );
console.log (`сброс поиска, карточек: ${list.children.length}` );
}
check ();
Пешнамоиш Пӯшонидан
Барои дидани натиҷа «Иҷро кардан»-ро пахш кунед.
Натиҷаи интизоршаванда страница: 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=…; чизе нест — 404 page, info.pages; филтрҳои серверии status (alive / dead / unknown) ва gender. Ҳама дар сервер — варианти аз ҳама осон
API-и оммавии худиро бе калид ҳам гирифтан мумкин аст — аммо пешакӣ бо муаллим мувофиқа кунед ва санҷед: оё аз браузер ҷавоб медиҳад (CORS) ва оё ҷустуҷӯ ва саҳифаҳо дорад.
Дар барнома чӣ бояд бошад
Маълумот аз API. Функсияи getJson (аз дарси 14): санҷиши response.ok, try / catch, паёми фаҳмои хато.
Суроға — тавассути URLSearchParams. Ягон часпонидани параметрҳо бо сатрҳо не. Функсияи buildUrl ё монанди он.
Рендери рӯйхат. Як функсияи render, ки аввал тоза мекунад; матн тавассути textContent гузошта мешавад; ҳолати «Ничего не найдено» ҳаст.
Ҷустуҷӯ бо debounce 300–500 мс ва ҳимоя аз мусобиқа (lastRequest ё AbortController).
Филтр ё мураттабкунӣ — ҳадди ақал яке, дар сервер ё дар клиент (бошуурона интихоб кунед ва дар README нависед, чаро).
Пагинатсия: саҳифаҳо (тугмаҳои «Ба қафо» / «Ба пеш» бо disabled дар канорҳо ва ёддошти «Саҳифаи N аз M») ё «Боз нишон деҳ». Ҳангоми ҷустуҷӯи нав саҳифа барқарор мешавад.
Ҳолатҳои ҳадди ақал: «Боркунӣ…», «Ничего не найдено», «Хато» (муфассал — дар дарси оянда).
README: кадом API гирифта шуд, истинод ба ҳуҷҷатгузорӣ, параметрҳои ҷустуҷӯ ва саҳифаҳо чӣ тавр сохта шудаанд, кадом дархостро дастӣ санҷидед.
ЗС: ҳуҷҷатгузориро бо ЗС аз рӯи промпти дарс хонед ва ба 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 менависем, ки ЗС чӣ пешниҳод кард ва мо чиро ислоҳ кардем.