IlmHamroh
JavaScript Full-stack/13-qism. Brauzer API'lari, performans va xavfsizlik6/32-dars19 daqiqa
Mundarija (39)

JavaScript Clipboard va Web Share API: nusxalash tugmasi va "Ulashish" oynasi

Qisqacha: await navigator.clipboard.writeText("matn") — matnni buferga yozadi; u tugma bosilganda, sahifa fokusda bo'lganda va faqat HTTPS (yoki localhost) da ishlaydi. Buferni o'qish (readText) — ruxsat so'raydi. navigator.share({ title, url }) — telefonning tizim "Ulashish" oynasini ochadi; qo'llanmasa canShare false beradi — unda havolani nusxalashga o'tasiz. Ikkalasi ham Promise qaytaradi.

Bu darsda

  • navigator.clipboard.writeText bilan to'g'ri ishlaydigan «Nusxalash» tugmasini yozasiz: holat xabari, xato va qulaylik bilan.
  • Buferni o'qish (readText, read) nega ruxsat so'rashini va ClipboardItem bilan HTML va rasm nusxalashni bilasiz.
  • copy va paste hodisalarini ishlatasiz va "nusxa almashtirish" hujumini taniysiz.
  • navigator.share va canShare bilan "Ulashish" tugmasini zaxira yo'li bilan yozasiz.
  • Xavfsiz kontekst (HTTPS) va foydalanuvchi harakati talabini tushuntira olasiz.

Oldin bilishingiz kerak: async / await, fetch asoslari, addEventListener asoslari, Dinamik UI'da qulaylik (a11y).

1. Nega bu kerak?

«Bahor» sayti mehmonlarga chegirma kodi beradi: BAHOR10. Telefonda kodni barmoq bilan belgilash qiyin — harf tushib qoladi, bo'sh joy qo'shilib ketadi. Kod yonida bitta «Nusxalash» tugmasi bo'lsa — bir bosish yetadi.

Ikkinchi holat: mehmon menyudagi oshni do'stiga yubormoqchi. Havolani nusxalaydi, Telegram'ni ochadi, qo'yadi — to'rt qadam. Telefonlarda esa tayyor tizim oynasi bor: "Ulashish" — Telegram, WhatsApp, SMS bir joyda. Sayt shu oynani o'zi ochib bera oladi.

Ikkala ish uchun brauzerda tayyor API bor:

  • Clipboard API — almashuv buferi (clipboard) bilan ishlash. Bu — nusxalangan narsa vaqtincha turadigan joy: Ctrl + C u yerga yozadi, Ctrl + V o'qiydi. Xuddi cho'ntak kabi: bir joydan olib, boshqa joyga qo'yasiz. Darsda qisqa qilib "bufer" deymiz. Node'dagi Buffer (baytlar bo'lagi) bilan aralashtirmang — u boshqa narsa.
  • Web Share API — operatsion tizimning "Ulashish" oynasini ochish.

Ikkalasi ham kichik, lekin tuzoqlari bor: xavfsizlik talablari, ruxsatlar, telefon va kompyuter farqi. Bugun shularni ko'ramiz.

2. Buferga yozish: writeText

2.1 Bir qator

js
await navigator.clipboard.writeText("BAHOR10");

navigator — brauzer va qurilma haqidagi obyekt, uni navigator va qurilma API'lari darsida batafsil ko'ramiz. navigator.clipboard — bufer bilan ishlaydigan obyekt. writeText matnni buferga yozadi va Promise qaytaradi: yozib bo'lganda bajariladi, yozolmasa rad etiladi.

Nega Promise? Bufer — operatsion tizimga tegishli. Brauzer undan "ruxsat" olishi kerak bo'lishi mumkin. Shuning uchun natija darhol emas — async / await darsidagidek kutamiz.

2.2 To'g'ri «Nusxalash» tugmasi

html
<style>
  body { font: 1rem/1.5 system-ui, sans-serif; margin: 1rem; }
  .promo { display: flex; gap: 0.5rem; align-items: center; }
  code { font-size: 1.25rem; padding: 0.2rem 0.5rem;
    border: 1px dashed #1f6f4a; border-radius: 0.4rem; }
  button { font: inherit; padding: 0.4rem 0.8rem; }
  #status { min-height: 1.5em; color: #1f6f4a; }
</style>
<p>Chegirma kodi (10%):</p>
<div class="promo">
  <code id="code">BAHOR10</code>
  <button id="copy" type="button">Nusxalash</button>
</div>
<p id="status" role="status"></p>
<script>
  const button = document.getElementById("copy");
  const status = document.getElementById("status");
  let timerId;

  button.addEventListener("click", async () => {
    const text = document.getElementById("code").textContent;
    try {
      await navigator.clipboard.writeText(text);
      status.textContent = "Nusxalandi ✓";
      console.log(`Buferda: ${text}`);
    } catch (error) {
      status.textContent = "Bo'lmadi — kodni qo'lda belgilang";
      console.log(`${error.name}: ${error.message}`);
    }
    clearTimeout(timerId);
    timerId = setTimeout(() => (status.textContent = ""), 2000);
  });
</script>

«Nusxalash» ni bosing: tugma ostida Nusxalandi ✓ chiqadi va konsolda Buferda: BAHOR10. Boshqa joyga Ctrl + V qiling — kod o'sha yerda. Biz tugmani bosib tekshirdik: Promise bajarildi, yozuv 2 soniyadan keyin o'chdi.

Bu kodda to'rt muhim detal bor:

  • Natijani ko'rsatish. Bufer ko'rinmaydi — foydalanuvchi nusxalandimi-yo'qmi bilmaydi. Shuning uchun Nusxalandi ✓ yozuvi shart.
  • role="status" — yozuv Malika'ning ekran o'quvchisiga ham yetadi. Dinamik UI'da qulaylik darsidagi "jonli hudud" (aria-live) ning o'zi: status roli aria-live="polite" ni o'z ichiga oladi.
  • try/catch. Nusxalash xato berishi mumkin (keyingi bo'lim). Xato bo'lsa, foydalanuvchiga boshqa yo'l aytiladi.
  • clearTimeout. Tugmani tez-tez bossangiz, eski taymer yangi yozuvni vaqtidan oldin o'chirmasin.

2.3 Qachon yozib bo'lmaydi

Bu jonli oynaga bir tajriba qo'shib ko'rdik: writeText ni sahifa ochilishi bilan, tugmasiz chaqirdik. Chrome 154 rad etdi:

text
NotAllowedError: Failed to execute 'writeText' on 'Clipboard': Document is not focused.

Tarjimasi: "hujjat fokusda emas". Brauzer buferga faqat foydalanuvchi hozir ishlayotgan sahifaga yozdiradi. Aks holda fondagi tab sizning buferingizni jimgina almashtirib qo'yishi mumkin edi. Amalda qoida bitta: writeText ni foydalanuvchi harakatida chaqiring — tugma bosilganda. DevTools konsolidan sinasangiz ham shu xato chiqadi: fokus konsolda, sahifada emas.

Ikkinchi shart — xavfsiz kontekst (secure context). Bu — HTTPS orqali (yoki kompyuterning o'zida, localhost da) ochilgan sahifa: yo'lda hech kim uni o'zgartira olmaydi. Vazifalar ilovasi loyihasi darsida ko'rgan edik: ba'zi API'lar faqat shunday sahifada bor. navigator.clipboard — shulardan biri. Biz bir sahifani http://localhost va http://bahor.test (oddiy HTTP domen) da ochdik:

Manzil isSecureContext navigator.clipboard
http://localhost (Live Server) true obyekt
http://bahor.test false undefined

HTTP sahifada navigator.clipboard umuman yo'q — navigator.clipboard.writeText(...) yozsangiz, TypeError: Cannot read properties of undefined (reading 'writeText') chiqadi. Live Server localhost da ochadi — u yerda ishlaydi. GitHub Pages — HTTPS, u yerda ham ishlaydi. Faqat telefoningizda kompyuter IP manzili orqali (http://192.168...) ochsangiz — ishlamaydi.

Tekshirib ko'ring: Sardor nusxalash kodini DOMContentLoaded ichiga qo'ydi: "sahifa ochilishi bilan telefon raqami buferga tushsin". Kompyuterida ishlamadi. Nega?

Javob

Sahifa ochilganda foydalanuvchi hech narsa bosmagan. Chrome bunday paytda yozishni rad etadi (NotAllowedError ... Document is not focused). Bu himoya: aks holda har sayt ruxsatsiz buferni almashtirardi. Nusxalash faqat foydalanuvchi harakatiga javob bo'lishi kerak — tugma bosilganda.

3. Buferdan o'qish: readText

3.1 O'qish — xavfliroq

Yozish bilan o'qish teng emas. Buferda nima bo'lishi mumkin? Boshqa ilovadan nusxalangan parol, karta raqami, shaxsiy xabar. Har sayt uni jimgina o'qiy olsa — bu katta xavf. Shuning uchun:

js
const text = await navigator.clipboard.readText();

Haqiqiy brauzerda birinchi marta ruxsat oynasi chiqadi: "Sayt buferni ko'rmoqchi — Ruxsat berish / Bloklash". Foydalanuvchi rad etsa — NotAllowedError.

Biz localhost dagi sahifada (Chrome 154) ikki holatni sinadik. Ruxsat berilmaganda:

text
NotAllowedError: Failed to execute 'readText' on 'Clipboard': Read permission denied.

Ruxsat oldindan berilganda (manzil satridagi sayt sozlamalarida «Ruxsat berish») esa readText() biz yozgan Bahor: +998 90 000 00 00 ni qaytardi. Ruxsatlar qanday ishlashi — keyingi darsda.

3.2 Bu darsdagi jonli oynalarda o'qish yo'q

Jonli oynalar xavfsizlik uchun sayt sahifasidan ajratilgan. Ularga faqat buferga yozish ruxsati berilgan, o'qish emas:

html
<button id="read" type="button">Buferni o'qish</button>
<script>
  const readButton = document.getElementById("read");
  readButton.addEventListener("click", async () => {
    try {
      const text = await navigator.clipboard.readText();
      console.log(`Buferda: ${text}`);
    } catch (error) {
      console.log(error.name);
      console.log(error.message);
    }
  });
</script>

Tugmani bosing. Konsolda (Chrome 154 da, tugmani bosgandan keyin olingan):

text
NotAllowedError
Failed to execute 'readText' on 'Clipboard': The Clipboard API has been blocked because of a permissions policy applied to the current document. See https://crbug.com/414348233 for more details.

Tarjimasi: "Clipboard API joriy hujjatga qo'llangan ruxsatlar siyosati (permissions policy) tufayli bloklandi". Ruxsatlar siyosati — sahifa ichidagi oynaga qaysi imkoniyatlar berilishini belgilaydigan qoidalar. Bu ham keyingi dars mavzusi. O'qishni sinash uchun — Live Server.

3.3 Yaxshiroq yo'l: paste hodisasi

Ko'p hollarda readText umuman kerak emas. Foydalanuvchi Ctrl + V bossa, brauzer paste hodisasini yuboradi va unda buferdagi narsa bor — ruxsat oynasisiz:

js
field.addEventListener("paste", (event) => {
  const text = event.clipboardData.getData("text/plain");
  console.log(`Qo'yildi: ${text}`);
});

Farqi: bu yerda foydalanuvchining o'zi "qo'y" degan. Ruxsat — uning harakati. Sayt faqat shu bir marta ko'radi, keyin emas. «Bahor» bron formasida telefon raqami qo'yilganda +998 (90) 000-00-00 dan raqamlarni ajratib olish — shu hodisaning ishi.

Tekshirib ko'ring: Nega brauzer writeText ga darhol ruxsat beradi, readText ga esa ruxsat oynasi chiqaradi?

Javob

Yozish faqat sizning matningizni buferga qo'yadi — foydalanuvchi o'zi bosgan tugma natijasi. O'qish esa boshqa ilovalardan qolgan narsani (parol, xabar) ko'rsatishi mumkin. Shuning uchun o'qish uchun alohida, aniq rozilik kerak.

4. Matndan boshqa narsalar: ClipboardItem

4.1 HTML va oddiy matn birga

Buferda bir vaqtda bir nechta format bo'lishi mumkin. Word yoki Telegram'ga qo'yilsa — qalin harflar bilan (HTML), Bloknot'ga — oddiy matn. Buning uchun navigator.clipboard.write va ClipboardItem:

js
const html = new Blob(["<b>Osh</b> — 35 000 so'm"], {
  type: "text/html",
});
const plain = new Blob(["Osh — 35 000 so'm"], { type: "text/plain" });

await navigator.clipboard.write([
  new ClipboardItem({ "text/html": html, "text/plain": plain }),
]);

Blob — "fayl ko'rinishidagi ma'lumot bo'lagi". Uni File va Blob darsida to'liq o'rganamiz. Hozircha: matn va uning turi (type). ClipboardItem — bitta "nusxa", ichida bir nechta format. Qo'yiladigan dastur o'ziga mosini tanlaydi.

localhost da sinadik: yozib, keyin navigator.clipboard.read() bilan o'qib oldik — ikkala format (text/plain, text/html) joyida edi.

4.2 Rasm

Rasm ham shunday: ClipboardItem({ "image/png": blob }). blob — masalan Canvas dan olingan PNG. Lekin har format ham yozilmaydi. Chrome 154 da:

Format ClipboardItem.supports(...)
text/plain, text/html true
image/png, image/svg+xml true
image/jpeg false

JPEG yozishga urinsak — NotAllowedError: Failed to execute 'write' on 'Clipboard': Type image/jpeg not supported on write. Qoida: rasmni PNG ga aylantirib yozing. Brauzer qo'llashini oldindan ClipboardItem.supports(tur) bilan tekshiring — bu statik metod 2025-yil martidan barcha asosiy brauzerlarda bor (Baseline, web-features).

5. copy hodisasi va "nusxa almashtirish"

5.1 Nusxani o'zgartirish

Foydalanuvchi matnni belgilab Ctrl + C bossa, brauzer copy hodisasini yuboradi. Sahifa unda nima nusxalanishini almashtira oladi:

js
document.addEventListener("copy", (event) => {
  const selected = getSelection().toString().trim();
  event.clipboardData.setData("text/plain", selected);
  event.preventDefault(); // brauzerning o'z nusxasi o'rniga
});

Bu foydali bo'lishi mumkin: masalan, menyu jadvalidan nusxalanganda ortiqcha bo'sh joylarni tozalash. preventDefault() siz setData e'tiborsiz qoladi — brauzer o'z nusxasini yozadi.

6. Hujumchi nigohi

6.1 Ko'rinadi bitta, nusxalanadi boshqa

Saytda terminal buyrug'i ko'rsatilgan: npm install. Siz uni belgilab nusxalaysiz va terminalga qo'yasiz. Lekin copy hodisasi nusxani boshqa buyruqqa almashtirgan bo'lsa-chi? Bu hujum pastejacking ("qo'yishni o'g'irlash") deb ataladi. Ko'ring — bu zararsiz namuna:

html
<style>
  body { font: 1rem/1.5 system-ui, sans-serif; margin: 1rem; }
  pre { background: #1f2a24; color: #e6efe9; padding: 0.75rem; }
  textarea { width: 100%; font: 0.9rem ui-monospace, monospace; }
</style>
<p>O'rnatish uchun buyruqni nusxalang:</p>
<pre id="cmd">npm install</pre>
<p>Bu yerga qo'ying (Ctrl + V):</p>
<textarea rows="3"></textarea>
<script>
  // ❌ HUJUM NAMUNASI — o'zingiz uchun, faqat tushunish maqsadida
  document.addEventListener("copy", (event) => {
    event.clipboardData.setData(
      "text/plain",
      `npm install && echo "Siz buni ko'rmadingiz"\n`,
    );
    event.preventDefault();
  });
</script>

npm install ni belgilab Ctrl + C, keyin maydonga Ctrl + V qiling. Maydonda boshqa buyruq paydo bo'ladi, oxirida yangi qator bilan. Terminalda bu yangi qator Enter degani — buyruq siz o'qib ulgurmasdan bajariladi. Biz buni Ctrl + C va Ctrl + V bosib tekshirdik: ko'ringan npm install o'rniga almashtirilgan matn qo'yildi.

6.2 Haqiqiy hayotda

ClickFix hujumi shu g'oyaga qurilgan. Xavfsizlik kompaniyasi Proofpoint uni 2024-yil iyunida birinchilardan bo'lib batafsil yozgan. U yerda soxta "Chrome xatosi" yoki "Word xatosi" oynasi «Tuzatish» tugmasini ko'rsatardi. 2024-yil noyabridagi hisobotida esa ClickFix keng tarqalganini va soxta "Men robot emasman" (CAPTCHA) sahifalari paydo bo'lganini yozdi. Sxema bir xil: tugma bosilganda sahifa buferga zararli PowerShell buyrug'ini yozadi va "Win + R bosing, Ctrl + V, Enter" deb ko'rsatma beradi. Odam o'z qo'li bilan zararli dasturni ishga tushiradi. Bu yerda writeText foydalanuvchi bosishida chaqirilgan — ya'ni brauzer nuqtai nazaridan hammasi "qonuniy".

Himoya:

  • Foydalanuvchi sifatida: saytdan nusxalangan buyruqni terminalga qo'yishdan oldin matn muharririga qo'yib o'qing. Hech qaysi haqiqiy CAPTCHA "Win + R" so'ramaydi.
  • Dasturchi sifatida: copy hodisasida nusxani faqat tozalang, mazmunini almashtirmang. Ko'rinadigan matn va nusxalanadigan matn bir xil bo'lsin. «Nusxalash» tugmasi yozadigan matn tugma yonida ko'rinib tursin — xuddi BAHOR10 misolidagidek.
  • Buferni o'qimang, kerak bo'lmasa. Kerak bo'lsa — paste hodisasi orqali, foydalanuvchi o'zi qo'yganda.

Diqqat: Nusxaga "Manba: sayt.uz" qo'shib qo'yish ham copy hodisasi bilan qilinadi. Bu hujum emas, lekin foydalanuvchini asabiylashtiradi: u faqat bir jumla olmoqchi edi. Yaxshi UX — foydalanuvchi nusxalagan narsa aynan u belgilagan narsa.

7. "Ulashish" oynasi: navigator.share

7.1 Birinchi misol

Telefonda «Ulashish» bosilganda pastdan tizim oynasi chiqadi — Telegram, WhatsApp, SMS, Bluetooth. Web Share API shu oynani sahifadan ochadi:

js
await navigator.share({
  title: "«Bahor» — osh",
  text: "Bugun «Bahor»da osh 35 000 so'm",
  url: "https://example.com/menyu#osh",
});

Uch maydon bor: title (sarlavha), text (matn), url (havola). Kamida bittasi bo'lishi shart. Foydalanuvchi ilovani tanlab yuborganda Promise bajariladi. Oynani yopib, hech narsa yubormasa — AbortError bilan rad etiladi. Bu xato emas, foydalanuvchining qarori: uni jim o'tkazib yuboring.

7.2 Qayerda ishlaydi

Web Share — hamma joyda emas. web-features (2026-oktabr) ma'lumoti:

Brauzer navigator.share
Android Chrome, iOS Safari bor
Chrome (Windows, ChromeOS, macOS), Safari bor
Firefox (kompyuter) yo'q
Chrome (Linux) yo'q

Ya'ni mehmon Dilshod akaning telefonida — deyarli doim bor, uning Firefox'li kompyuterida — yo'q. Shuning uchun Web Share doim zaxira yo'li bilan yoziladi.

7.3 Talablar va xatolar

writeText dagi shartlar bu yerda ham bor, yana qattiqroq. Biz localhost da sinadik:

  • Faqat foydalanuvchi harakatida. Sahifa ochilishi bilan chaqirilsa: NotAllowedError: Failed to execute 'share' on 'Navigator': Must be handling a user gesture to perform a share request. — "ulashish so'rovi faqat foydalanuvchi harakatiga javob bo'lishi kerak".
  • Bitta bosish — bitta ulashish. Brauzer har bosishdan keyin sahifaga qisqa muddatli "ruxsat" beradi — buni foydalanuvchi faolligi (user activation) deyishadi. Xuddi bir martalik chipta kabi: share uni ishlatsa, chipta yirtiladi. Bitta tinglovchida ikki marta chaqirsak, ikkinchisi rad etildi: birinchi chaqiruv chiptani sarflab qo'ydi. Oyna ochiq turganda yana chaqirilsa: InvalidStateError: ... An earlier share has not yet completed.
  • Bo'sh ma'lumot. navigator.share({}) → TypeError: ... No known share data fields supplied. — "ulashish uchun ma'lum maydon berilmagan".
  • Xavfsiz kontekst — HTTP sahifada navigator.share umuman yo'q.

7.4 canShare: oldindan so'rash

navigator.canShare(data) — "shu ma'lumotni ulasha olamanmi?" degan savol. U true yoki false qaytaradi va hech narsa ochmaydi. Fayl ulashishda ayniqsa kerak: ba'zi qurilmalar havolani ulashadi, faylni emas.

js
const file = new File(["..."], "menyu.pdf", {
  type: "application/pdf",
});

if (navigator.canShare?.({ files: [file] })) {
  await navigator.share({ files: [file], title: "«Bahor» menyusi" });
}

?. — optional chaining: canShare umuman yo'q brauzerda xato bermaydi, shunchaki undefined (shart bajarilmaydi).

7.5 Zaxira yo'li bilan «Ulashish» tugmasi

Mana to'liq naqsh: ulasha olsa — tizim oynasi, olmasa — havolani nusxalash. Avval yo'lni sxemada ko'ring — har tarmoq qayerda tugashiga qarang:

flowchart TD
  A["«Ulashish» bosildi"] --> B{"canShare(data)?"}
  B -- ha --> C["navigator.share"]
  C -- yuborildi --> D["Ulashildi"]
  C -- AbortError --> E["jim: o'zi yopdi"]
  C -- boshqa xato --> X["Xato"]
  B -- yo'q --> F["clipboard.writeText"]
  F -- bo'ldi --> G["Havola nusxalandi ✓"]
  F -- xato --> H["Havolani ekranda ko'rsatish"]
html
<style>
  body { font: 1rem/1.5 system-ui, sans-serif; margin: 1rem; }
  button { font: inherit; padding: 0.4rem 0.8rem; }
  #status { min-height: 1.5em; }
</style>
<h3>Osh — 35 000 so'm</h3>
<button id="share" type="button">Ulashish</button>
<p id="status" role="status"></p>
<script>
  const data = {
    title: "«Bahor» — osh",
    url: "https://example.com/menyu#osh",
  };
  const status = document.getElementById("status");
  const shareButton = document.getElementById("share");

  shareButton.addEventListener("click", async () => {
    if (navigator.canShare?.(data)) {
      try {
        await navigator.share(data);
        status.textContent = "Ulashildi";
      } catch (error) {
        if (error.name !== "AbortError") status.textContent = "Xato";
      }
      return;
    }
    try {
      await navigator.clipboard.writeText(data.url);
      status.textContent = "Havola nusxalandi ✓";
      console.log("zaxira: havola nusxalandi");
    } catch {
      status.textContent = `Havola: ${data.url}`;
    }
  });
</script>

Bu jonli oynada «Ulashish» ni bosing — konsolda zaxira: havola nusxalandi, tugma ostida Havola nusxalandi ✓. Nega tizim oynasi ochilmadi? Jonli oynaga ulashish ruxsati berilmagan — bu yerda canShare(data) false qaytaradi (Chrome 154 da tekshirdik). Kod buni o'zi sezdi va zaxiraga o'tdi. Tizim oynasini ko'rish uchun shu kodni Live Server'da (localhost) Windows yoki macOS'dagi Chrome'da oching. Telefonda esa sahifa HTTPS bo'lishi kerak (masalan, GitHub Pages). Live Server'ni telefondan http://192.168... orqali ochsangiz, u xavfsiz kontekst emas — navigator.share umuman bo'lmaydi.

Uch qatlamli zaxira: ulashish → nusxalash → havolani ekranda ko'rsatish. Qaysi qurilmada bo'lmasin, foydalanuvchi qo'li bo'sh qolmaydi. AbortError ataylab jim: foydalanuvchi oynani o'zi yopdi, "Xato" deyish noto'g'ri.

Tekshirib ko'ring: Nega if (navigator.share) emas, navigator.canShare?.(data) tekshirildi?

Javob

navigator.share mavjud bo'lishi — "ishlaydi" degani emas. Bu jonli oynada share funksiyasi bor, lekin ruxsatlar siyosati uni to'sgan: chaqirsangiz NotAllowedError. canShare(data) esa aniq shu ma'lumotni, aniq shu joyda ulashish mumkinligini aytadi. Fayl ulashishda ham farq katta: share bor, lekin fayl qo'llanmasligi mumkin.

8. Eski usul: execCommand("copy")

Eski kodda shunday nusxalashni ko'rasiz: yashirin <textarea> yaratiladi, matn belgilanadi va document.execCommand("copy") chaqiriladi. Bu Clipboard API'dan oldingi yagona yo'l edi. Hozir u eskirgan (deprecated) — sinxron, belgilashni buzadi va standartda "ishlamay qolishi mumkin" deb belgilangan. Yangi kodda — faqat navigator.clipboard. Ba'zi kutubxonalar (masalan, clipboard.js) hali uni zaxira sifatida ishlatadi.

9. Ko'p uchraydigan xatolar

9.1 Natijani ko'rsatmaslik

Tugma bosildi — hech narsa o'zgarmadi. Foydalanuvchi yana bosadi, yana... va ishonmay qo'lda nusxalaydi. Tuzatish: har doim Nusxalandi ✓ kabi qisqa xabar, role="status" bilan.

9.2 await siz writeText

js
// ❌ xato bo'lsa ham "Nusxalandi" chiqadi
navigator.clipboard.writeText(text);
status.textContent = "Nusxalandi ✓";

Promise kutilmadi — xabar natijadan oldin chiqdi. Rad etilsa, konsolda Uncaught (in promise) NotAllowedError qoladi (suzib yurgan Promise). Tuzatish: await + try/catch.

9.3 Tugmasiz nusxalash

DOMContentLoaded yoki taymerda writeText — Document is not focused. Tuzatish: faqat click (yoki klaviatura bilan bosish) tinglovchisida.

9.4 HTTP'da sinash

Telefonda http://192.168.1.5:5500 orqali ochilgan sahifada navigator.clipboard — undefined. Tuzatish: kompyuterda localhost, telefonda — HTTPS (GitHub Pages) yoki DevTools'ning qurilma rejimi.

9.5 AbortError ni xato deb ko'rsatish

Foydalanuvchi "Ulashish" oynasini yopdi — sayt "Xato yuz berdi!" deydi. Tuzatish: error.name === "AbortError" bo'lsa — hech narsa demang.

10. Mashqlar

1-mashq (oson): Telefon raqamini nusxalash

«Bahor» kontaktlar blokida telefon raqami: +998 90 000 00 00. Yonida «Nusxalash» tugmasi. Bosilganda raqam buferga yozilsin va tugmaning o'z matni 2 soniyaga Nusxalandi ✓ ga almashsin, keyin yana Nusxalash. Konsolga Buferda: +998 90 000 00 00 chiqsin.

Yechim
html
<style>
  body { font: 1rem/1.5 system-ui, sans-serif; margin: 1rem; }
  button { font: inherit; padding: 0.3rem 0.7rem; min-width: 9rem; }
</style>
<p>Telefon: <span id="phone">+998 90 000 00 00</span>
  <button id="copy" type="button" aria-live="polite">
    Nusxalash</button>
</p>
<script>
  const button = document.getElementById("copy");
  let timerId;

  button.addEventListener("click", async () => {
    const phone = document.getElementById("phone").textContent;
    try {
      await navigator.clipboard.writeText(phone);
      button.textContent = "Nusxalandi ✓";
      console.log(`Buferda: ${phone}`);
    } catch {
      button.textContent = "Bo'lmadi";
    }
    clearTimeout(timerId);
    timerId = setTimeout(() => {
      button.textContent = "Nusxalash";
    }, 2000);
  });
</script>

Bosing — tugma matni Nusxalandi ✓, 2 soniyadan keyin qaytadi (bosib tekshirdik). aria-live="polite" tugmaning o'zida: matn o'zgarganda ekran o'quvchi uni o'qiydi. min-width — matn uzunligi o'zgarganda tugma "sakramasin".

2-mashq (o'rta): Har kod blokiga tugma

Sahifada bir nechta <pre><code> bor. JavaScript har biriga «Nusxalash» tugmasini qo'shsin. Bitta tinglovchi butun sahifada bo'lsin (Event delegation), bosilgan tugma o'z blokining matnini nusxalasin va konsolga nusxalandi: N belgi yozsin.

Yechim
html
<style>
  body { font: 1rem/1.5 system-ui, sans-serif; margin: 1rem; }
  .block { position: relative; }
  pre { background: #f0f4f2; padding: 0.75rem; padding-right: 7rem; }
  .copy { position: absolute; top: 0.4rem; right: 0.4rem;
    font: inherit; font-size: 0.85rem; }
</style>
<pre><code>git switch -c feature/tema</code></pre>
<pre><code>npm test</code></pre>
<script>
  for (const pre of document.querySelectorAll("pre")) {
    const wrap = document.createElement("div");
    wrap.className = "block";
    pre.replaceWith(wrap);
    const button = document.createElement("button");
    button.type = "button";
    button.className = "copy";
    button.textContent = "Nusxalash";
    wrap.append(pre, button);
  }

  document.addEventListener("click", async (event) => {
    const button = event.target.closest(".copy");
    if (!button) return;
    const code = button.parentElement.querySelector("code");
    const text = code.textContent;
    await navigator.clipboard.writeText(text);
    button.textContent = "Nusxalandi ✓";
    console.log(`nusxalandi: ${text.length} belgi`);
  });
</script>

Birinchi tugmani bosing — nusxalandi: 26 belgi, ikkinchisini — nusxalandi: 8 belgi. Ikkalasini bosib tekshirdik. Tugmalar JavaScript bilan qo'shildi: JavaScript ishlamasa, ishlamaydigan tugma ham ko'rinmaydi. Haqiqiy loyihada bu yerga try/catch ham qo'shing (1-mashqdagidek).

3-mashq (qiyin): shareOrCopy yordamchisi

shareOrCopy(data) funksiyasini yozing. U Promise qaytarsin va natija satri bilan bajarilsin: "shared" (tizim oynasi orqali yuborildi), "cancelled" (foydalanuvchi oynani yopdi), "copied" (havola nusxalandi) yoki "failed". Sinov: tugma bosilganda natijani konsolga chiqaring. Bu jonli oynada "copied" chiqishi kerak.

Yechim
html
<button id="share" type="button">Ulashish</button>
<script>
  async function shareOrCopy(data) {
    if (navigator.canShare?.(data)) {
      try {
        await navigator.share(data);
        return "shared";
      } catch (error) {
        if (error.name === "AbortError") return "cancelled";
        // boshqa xato — nusxalashga o'tamiz
      }
    }
    try {
      await navigator.clipboard.writeText(data.url ?? data.text);
      return "copied";
    } catch {
      return "failed";
    }
  }

  const shareButton = document.getElementById("share");
  shareButton.addEventListener("click", async () => {
    const result = await shareOrCopy({
      title: "«Bahor»",
      url: "https://example.com/menyu",
    });
    console.log(result);
  });
</script>

Bu oynada — copied (bosib tekshirdik). Telefonda — shared yoki cancelled. Bitta nozik joy: share NotAllowedError bilan rad etilsa, kod nusxalashga o'tadi. Lekin bosish "chiptasi" (foydalanuvchi faolligi) share ga sarflangan bo'lishi mumkin — unda writeText ham rad etiladi va failed chiqadi. Shuning uchun failed da foydalanuvchiga havolani ekranda ko'rsating.

4-mashq: Amaliy tajriba — vazifalar ro'yxatini nusxalash

Kanon kodga tegmasdan tajriba: kurs/mashqlar/13/06-nusxalash.js — Live Server'da ochilgan vazifalar sahifasining DevTools konsoliga qo'yiladigan kod. U sarlavha ostiga «Ro'yxatni nusxalash» tugmasini qo'shsin. Bosilganda ekrandagi vazifalar matn sifatida buferga yozilsin: bajarilgan — [x] matn, qolgani — [ ] matn, har biri yangi qatorda. Natija #elon ga emas, tugmaning o'ziga yozilsin (kanon e'lonlariga aralashmasin).

Yechim
js
const copyButton = document.createElement("button");
copyButton.type = "button";
copyButton.textContent = "Ro'yxatni nusxalash";
document.querySelector("h1").after(copyButton);

copyButton.addEventListener("click", async () => {
  const lines = [...document.querySelectorAll("#royxat li")].map(
    (li) => {
      const done = li.querySelector("input[type=checkbox]")?.checked;
      const text = li.querySelector("label")?.textContent ?? "";
      return `${done ? "[x]" : "[ ]"} ${text}`;
    },
  );
  try {
    await navigator.clipboard.writeText(lines.join("\n"));
    copyButton.textContent = `Nusxalandi: ${lines.length} ta`;
  } catch (error) {
    copyButton.textContent = `Bo'lmadi: ${error.name}`;
  }
});

Telegram'ga yoki Bloknot'ga qo'ying — ro'yxat tayyor. Sahifa tuzilmasi (#royxat li, ichidagi checkbox va label) kanon render.js dagi oddiyQator dan; Elements panelida tekshiring, farq bo'lsa selektorni moslang. Tugmani konsoldan bosmang — konsol fokusda bo'lsa, Document is not focused chiqadi; sahifadagi tugmani sichqoncha bilan bosing.

bash
git add 13/06-nusxalash.js
git commit -m "13/06: vazifalar ro'yxatini buferga nusxalash"

11. Real ishda

  • Nusxalash tugmalari — GitHub (kod bloklari, klon havolasi), Payme va Click (karta raqami), promo-kodlar, Telegram'da havolani nusxalash. Hammasi writeText + qisqa xabar.
  • Ulashish — internet do'konlar, yangiliklar saytlari, YouTube: telefonda tizim oynasi, kompyuterda o'zining menyusi yoki "Havolani nusxalash".
  • PWA — o'rnatilgan veb ilova boshqalardan ulashilgan narsani qabul qila oladi (Share Target) — PWA darsida eslatamiz.
  • Xavfsizlik — ClickFix kabi hujumlar tufayli korporativ xavfsizlik bo'limlari "buferdan terminalga qo'yish" bo'yicha xodimlarni o'qitadi.
  • Intervyu. "Nega readText ruxsat so'raydi, writeText esa yo'q?", "Web Share bo'lmasa nima qilasiz?" — frontend intervyularida uchraydi.

Xulosa

  • navigator.clipboard.writeText(matn) — Promise; faqat xavfsiz kontekstda, fokusdagi sahifada, foydalanuvchi harakatida.
  • O'qish (readText, read) — ruxsat so'raydi; ko'p hollarda paste hodisasi yetarli.
  • ClipboardItem — bir nechta format (HTML + matn, PNG); ClipboardItem.supports bilan tekshiring.
  • copy hodisasida nusxani almashtirish mumkin — pastejacking/ClickFix shunga qurilgan; ko'ringan va nusxalangan matn bir xil bo'lsin.
  • navigator.share — tizim oynasi; canShare bilan tekshiring, AbortError ni jim o'tkazing, zaxira — nusxalash.

Keyingi dars: Permissions, Notifications va Geolocation — ruxsat so'raydigan API'lar: brauzer ruxsatni qanday saqlaydi, qachon so'rash kerak va joylashuvni qanday olish mumkin.

Manbalar

  • W3C: "Clipboard API and events" — w3c.github.io/clipboard-apis
  • W3C: "Web Share API" — w3c.github.io/web-share
  • MDN: Clipboard, ClipboardItem, Navigator.share(), Navigator.canShare() — developer.mozilla.org
  • web.dev: "Unblocking clipboard access" va "Integrate with the OS sharing UI with the Web Share API" — web.dev
  • Proofpoint: "From Clipboard to Compromise: A PowerShell Self-Pwn" (2024-yil 17-iyun) — proofpoint.com
  • Proofpoint: "Security Brief: ClickFix Social Engineering Technique Floods Threat Landscape" (2024-yil 18-noyabr) — proofpoint.com
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
JavaScript Clipboard va Web Share API: nusxalash tugmasi va "Ulashish" oynasi — IlmHamroh