API-های شخص ثالث در جاوا اسکریپت — راهنمای جامع

۵۲۲ بازدید
آخرین به‌روزرسانی: ۸ شهریور ۱۴۰۲
زمان مطالعه: ۲۱ دقیقه
دانلود PDF مقاله
API-های شخص ثالث در جاوا اسکریپت — راهنمای جامع

در بخش‌های قبلی این سلسله مقالات راهنمای جامع جاوا اسکریپت به معرفی برخی از API-های داخلی مرورگرها پرداختیم، اما این موارد همه API-ها را شامل نمی‌شوند. بسیاری از وب‌سایت‌ها و سرویس‌های بزرگ مانند گوگل مپ، توییتر، فیسبوک، پی‌پل و غیره، API-هایی ارائه می‌کنند که به توسعه‌دهندگان امکان استفاده از داده‌هایشان را می‌دهد. بدین ترتیب می‌توان برای مثال توییت ها را در بلاگ نمایش داد یا در مورد سرویس‌ها، از امکان لاگین فیسبوک برای ورود به وب‌سایت‌های ثالث استفاده کرد. در این مقاله به تفاوت‌های بین API-های مرورگر و API-های شخص ثالث می‌پردازیم و برخی کاربردهای نوعیِ API-های شخص ثالث را نیز معرفی می‌کنیم.

997696

برای مطالعه بخش قبلی این مجموعه مقالات آموزشی روی لینک زیر کلیک کنید:

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

API-های شخص ثالث چه هستند؟

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

در ادامه به بررسی یک مثال ساده API از Mapquest را بررسی می‌کنیم و از آن برای نمایش تفاوت‌های API-های شخص ثالث از API-های مرورگر استفاده می‌کنیم.

API-های شخص ثالث روی سرورهای شخص ثالث هستند

API-های مرورگر داخل مرورگرها قرار دارند و می‌توان با استفاده از جاوا اسکریپت بی‌درنگ به آن‌ها دسترسی یافت. برای نمونه API به نام Web Audio با استفاده از شیء بومی AudioContext در مرورگر قابل دسترسی است. به مثال زیر توجه کنید:

1const audioCtx = new AudioContext();
2  ...
3const audioElement = document.querySelector('audio');
4  ...
5const audioSource = audioCtx.createMediaElementSource(audioElement);
6// etc.

از سوی دیگر، API-های شخص ثالث در سرورهای شخص ثالث قرار دارند و برای دسترسی به آن‌ها از طریق جاوا اسکریپت باید ابتدا به کارکرد API وصل شویم و آن را در صفحه وب‌سایت خود فعال کنیم. این کار عموماً ابتدا شامل پیوند یافتن به یک کتابخانه جاوا اسکریپت روی سرور از طریق عنصر <script> است که در مثال Mapquest مشاهده می‌کنیم:

1<script src="https://api.mqcdn.com/sdk/mapquest-js/v1.3.2/mapquest.js"></script>
2<link type="text/css" rel="stylesheet" href="https://api.mqcdn.com/sdk/mapquest-js/v1.3.2/mapquest.css"/>

سپس می‌توان از اشیای موجود در کتابخانه استفاده کرد. به مثال زیر توجه کنید:

1var L;
2
3var map = L.mapquest.map('map', {
4  center: [53.480759, -2.242631],
5  layers: L.mapquest.tileLayer('map'),
6  zoom: 12
7});

در کد فوق ما یک متغیر برای ذخیره اطلاعات نقشه می‌سازیم. سپس یک نقشه جدید با استفاده از متد ()mapquest.map می‌سازیم که به عنوان پارامترهایش، ID یک عنصر <div> که قرار است در نقشه نمایش یابد و یک شیء گزینه‌ها که شامل جزییات نقشه‌ای است که قرار است نمایش یابد را دریافت می‌کند. در این حالت، مختصات مرکز نقشه، یک لایه نقشه از نوع map برای نمایش که با استفاده از متد ()mapquest.tileLayer ساخته شده و سطح بزرگ‌نمایی پیش‌فرض تعیین می‌شود.

این همه اطلاعاتی است که API مربوط به Mapquest برای رسم نقشه نیاز دارد. سروری که به آن وصل می‌شویم، همه کارهای پیچیده موردنیاز مانند نمایش بخش‌های صحیح نقشه برای ناحیه مورد نظر و غیره را انجام می‌دهد.

نکته: برخی API-ها دسترسی به کارکردهای خود را به روشی نسبتاً متفاوت مدیریت می‌کنند و از توسعه‌دهنده می‌خواهند که یک درخواست HTTP با الگوی URL خاص برای بازیابی داده‌ها ارائه کند. این API-ها به نام API-های RESTful مشهور هستند.

API-های شخص ثالث عموماً نیازمند کلیدهای API هستند

امنیت API-های مرورگر به وسیله اعلان‌های امنیتی تأمین می‌شود. هدف از این کار آن است که کاربر بداند در وب‌سایتی که بازدید می‌کند چه اتفاقاتی می‌افتد و بدین ترتیب احتمال این که قربانی استفاده از API-های خرابکارانه شود پایین می‌آید.

API-های شخص ثالث، سیستم مجوز نسبتاً متفاوتی دارند و عموماً از کلیدهای توسعه‌دهنده برای ارائه مجوز دسترسی به کارکرد API بهره می‌گیرند. در این روش امنیت ارائه‌دهنده مهم‌تر از امنیت کاربر است. در مثال API مربوط به Mapquest یک خط مانند زیر مشاهده می‌کنید:

1L.mapquest.key = 'YOUR-API-KEY-HERE';

این خط کد نشان می‌دهد که API یا کلید توسعه‌دهنده باید در اپلیکیشن مورد استفاده قرار گیرد، یعنی توسعه‌دهنده باید تقاضای دریافت کلید بکند و سپس آن را در کد خود قرار دهد تا اجازه دسترسی به کارکرد API را به دست آورد. در این مثال ما از یک placeholder استفاده کرده‌ایم.

API-های دیگر ممکن است نیازمند گنجاندن کلید به روش نسبتاً متفاوتی باشند، اما الگوی کار در اغلب آن‌ها نسبتاً مشابه است.

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

بسط مثال Mapquest

در این بخش مقداری بر جزییات مثال Mapquest می‌افزاییم تا شیوه استفاده از برخی قابلیت‌های این API را بررسی کنیم.

ابتدا یک کپی از کد زیر روی سیستم خود در فایلی به نام starter-file.html در یک دایرکتوری جدید بسازید:

1<!DOCTYPE html>
2<html>
3  <head>
4    <meta charset="utf-8">
5    <title>Mapquest example</title>
6    <style>
7      #map {
8        width: 600px;
9        height: 600px;
10      }
11    </style>
12    <script src="https://api.mqcdn.com/sdk/mapquest-js/v1.3.2/mapquest.js"></script>
13    <link type="text/css" rel="stylesheet" href="https://api.mqcdn.com/sdk/mapquest-js/v1.3.2/mapquest.css"/>
14    <script>
15      // 1. The basic part of the example
16      var L;
17      window.onload = function() {
18        L.mapquest.key = 'YOUR-API-KEY-HERE';
19        // 'map' refers to a <div> element with the ID map
20        var map = L.mapquest.map('map', {
21          center: [53.480759, -2.242631],
22          layers: L.mapquest.tileLayer('map'),
23          zoom: 12
24        });
25      }
26    </script>
27  </head>
28  <body>
29    <h1>Simple Mapquest example</h1>
30
31    <div id="map"></div>
32  </body>
33</html>

اگر ریپازیتوری مثال‌ها (+) را کلون کرده‌اید، این فایل روی سیستم شما قرار گرفته است و در مسیر دایرکتوری javascript/apis/third-party-apis/mapquest موجود است.

سپس باید به سایت توسعه‌دهندگان Mapquest (+) بروید و یک حساب ایجاد کنید. بدین ترتیب یک کلید توسعه‌دهنده برای استفاده در این مثال به دست می‌آورید. در زمان نگارش این مقاله این کلید به نام «consumer key» خوانده می‌شد و در فرایند ایجاد کد از کاربر یک URL قابل کلیک اختیاری نیز خواسته می‌شود. لزومی به پر کردن این گزینه وجود ندارد و می‌توانید آن را خالی بگذارید. فایل آغازین را باز کنید و کلید API پیش‌فرض را با کلید خودتان عوض کنید.

تغییر دادن نوع نقشه

انواع مختلفی از نقشه وجود دارد که می‌توان با استفاده از API مربوط به Mapquest نمایش داد. به این منظور باید از خط زیر استفاده کنید:

1layers: L.mapquest.tileLayer('map')

تلاش کنید مقدار map را به hybrid تغییر دهید تا یک نقشه با سبک هیبرید نمایش یابد. مقادیر دیگر را نیز می‌توانید امتحان کنید. گزینه‌های مختلفی که به این منظور می‌توان استفاده کرد را به همراه اطلاعات بیشتر دیگر می‌توانید در صفحه رفرنس tileLayer (+) مشاهده کنید.

افزودن کنترل‌های مختلف

این نقشه کنترل‌های مختلفی دارد. به صورت پیش‌فرض صرفاً یک کنترل بزرگنمایی دیده می‌شود، اما می‌توان کنترل‌ها را با استفاده از متد ()map.addControl بسط داد و خط زیر را به کد و درون دستگیره window.onload اضافه کرد:

1map.addControl(L.mapquest.control());

متد ()mapquest.control یک مجموعه کنترل با امکانات کامل و ساده ایجاد می‌کند آن را به طور پیش‌فرض در گوشه راست-بالا قرار می‌دهد. می‌توان از طریق تعیین یک شیء opyions به عنوان پارامتر برای کنترل که شامل مشخصه position است، مکان آن را تغییر داد. مقدار این مشخصه یک رشته است که موقعیت کنترل را تعیین می‌کند به این منظور به مثال زیر توجه کنید:

1map.addControl(L.mapquest.control({ position: 'bottomright' }));

انواع دیگری از کنترل نیز وجود دارند. برای نمونه ()mapquest.searchControl() ،mapquest.satelliteControl و برخی انواع کاملاً پیچیده و قدرتمند دیگر.

افزودن یک نشانگر سفارشی

افزودن یک آیکون نشانگر (marker) در یک موقعیت خاص روی نقشه، کار آسانی است. به این منظور کافی است از متد ()L.marker استفاده کنید. کد زیر را نیز درون window.onload به مثال خود اضافه کنید:

1L.marker([53.480759, -2.242631], {
2  icon: L.mapquest.icons.marker({
3    primaryColor: '#22407F',
4    secondaryColor: '#3B5998',
5    shadow: true,
6    size: 'md',
7    symbol: 'A'
8  })
9})
10.bindPopup('This is Manchester!')
11.addTo(map);

چنان که می‌بینید این متد در ساده‌ترین حالت خود دو پارامتر می‌گیرد که یکی آرایه‌ای شامل مختصات نقطه مورد نظر برای نمایش نشانگر و دیگری شیء options است که شامل مشخصه icon است و آیکونی که باید نمایش پیدا کند را تعریف می‌کند.

آیکون با استفاده از متد ()mapquest.icons.marker تعریف شده است که شامل اطلاعاتی در مورد رنگ و اندازه نشانگر است.

در انتهای متد اول یک کد به صورت زیر نیز وجود دارد:

1.bindPopup('This is Manchester!')

این کد محتوایی که در زمان کلیک شدن نشانگر نمایش خواهد یافت را تعریف می‌کند. در نهایت addTo(map)‎. را به انتهای زنجیره اضافه می‌کنیم تا نشانگر عملاً به نقشه اضافه شود.

تلاش کنید با گزینه‌های مختلفی که در مستندات نشان داده شده کار کنید و نتیجه کار را بررسی کنید. Mapquest برخی کارکردهای واقعاً پیشرفته مانند جهت‌یابی، جستجو و غیره نیز دارد.

نکته: اگر در راه انداختن مثال خود مشکل داشید، کدتان را با کد کامل شده زیر مقایسه کنید و اشکال را بیابید:

1<!DOCTYPE html>
2<html>
3  <head>
4    <meta charset="utf-8">
5    <title>Mapquest example</title>
6    <style>
7      #map {
8        width: 600px;
9        height: 600px;
10      }
11    </style>
12    <script src="https://api.mqcdn.com/sdk/mapquest-js/v1.3.2/mapquest.js"></script>
13    <link type="text/css" rel="stylesheet" href="https://api.mqcdn.com/sdk/mapquest-js/v1.3.2/mapquest.css"/>
14    <script>
15      var L;
16      window.onload = function() {
17        L.mapquest.key = 'YOUR-API-KEY-HERE';
18        // 'map' refers to a <div> element with the ID map
19        var map = L.mapquest.map('map', {
20          center: [53.480759, -2.242631],
21          // 1. change 'map' to 'hybrid', try other type of map
22          layers: L.mapquest.tileLayer('hybrid'),
23          zoom: 12
24        });
25        // 2. Add control
26        map.addControl(L.mapquest.control());
27        // 3. Add icon
28        L.marker([53.480759, -2.242631], {
29          icon: L.mapquest.icons.marker({
30            primaryColor: '#22407F',
31            secondaryColor: '#3B5998',
32            shadow: true,
33            size: 'md',
34            symbol: 'A'
35          })
36        })
37        .bindPopup('This is Manchester!')
38        .addTo(map);
39      }
40    </script>
41  </head>
42  <body>
43    <h1>Simple Mapquest example</h1>
44
45    <div id="map"></div>
46  </body>
47</html>

نکاتی در مورد گوگل مپ

گوگل مپ قطعاً محبوب‌ترین API نقشه است، بنابراین در این بخش به بررسی برخی مثال‌ها از این API می‌پردازیم.

برای آشنایی بیشتر با این API می‌توانید به مثال زیر توجه کنید:

1<!DOCTYPE html>
2<html>
3  <head>
4    <meta charset="utf-8">
5    <title>Google Maps example</title>
6    <style>
7      #map_canvas {
8        width: 600px;
9        height: 600px;
10      }
11    </style>
12  </head>
13  <body>
14    <h1>Simple maps example</h1>
15
16    <div id="map_canvas"></div>
17    <script>
18    function initMap() {
19      var myOptions = {
20        zoom: 8,
21        center: {lat: 53.480759, lng: -2.242631},
22        mapTypeId: google.maps.MapTypeId.TERRAIN,
23        disableDefaultUI: true,
24        // Step 4. Customize displayed controls
25        zoomControl: true,
26        mapTypeControl: true,
27        scaleControl: true
28      }
29      var map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
30      // Step 2. Add custom icon
31      var iconBase = 'https://maps.google.com/mapfiles/kml/shapes/';
32      var marker = new google.maps.Marker({
33        position: {lat: 53.480759, lng: -2.242631},
34        icon: iconBase + 'flag_maps.png',
35        map: map
36      });
37      // Step 3. Add info window
38      var contentString = '<div id="content"><h2 id="firstHeading" class="firstHeading">Custom info window</h2><p>This is a cool custom info window.</p></div>';
39      var infowindow = new google.maps.InfoWindow({
40        content: contentString
41      });
42      marker.addListener('click', function() {
43        infowindow.open(map, marker);
44      });
45    }
46    </script>
47    <script src="https://maps.googleapis.com/maps/api/js?key=ENTER-API-KEY-HERE&callback=initMap"
48    async defer></script>
49  </body>
50</html>

اما در نهایت ما به چند دلیل مجدداً از مثال Mapquest استفاده می‌کنیم:

آغاز کار با Mapquest بسیار آسان‌تر است. در مورد API-های گوگل به طور کلی باید حساب گوگل داشته باشید و وارد کنسول پلتفرم کلود گوگل (+) شوید تا کلیدهای API را بسازید. به طور خاص برای API گوگل مپ (+) باید اطلاعات کارت اعتباری خود را به منظور صدور صورتحساب وارد کنید. با توجه به این که استفاده ابتدایی رایگان است، این وضعیت برای یک راهنمای مقدماتی قابل قبول نیست.

ما می‌خواهیم شما را با جایگزین‌های دیگر به جز گوگل نیز آشنا کنیم.

API به صورت RESTful به نام NYTimesSection

در این بخش به بررسی مثال دیگری از API می‌پردازیم که به روزنامه نیویورک‌تایمز مربوط است. این API امکان بازیابی اطلاعات اخبار نیویورک‌تایمز و نمایش آن در وب‌سایت‌های دیگر را فراهم می‌سازد. این نوع از API به نام RESTful API شناخته می‌شود و در آن به جای دریافت داده‌ها با استفاده از قابلیت‌های یک کتابخانه جاوا اسکریپت مانند مپ‌کوئست، داده‌ها را با استفاده از ایجاد درخواست‌های HTTP به URL-های خاص به دست می‌آوریم. در این فرایند داده‌هایی مانند کلیدواژه‌های جستجو و دیگر مشخصه‌ها در URL انکود می‌شوند. این روش در زمان استفاده از API-ها بسیار رایج است.

رویکردی برای استفاده از API-های شخص ثالث

در ادامه تمرینی را مطرح می‌کنیم که شیوه استفاده از API نیویورک‌تایمز را به شما نشان می‌دهد و مجموعه‌ای کلی‌تر از مراحل موردنیاز برای کار کردن با API-های تازه را در معرض دید شما قرار می‌دهد.

یافتن مستندات

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

دریافت کلید توسعه‌دهنده

اغلب API-ها نیازمند استفاده از نوعی کلید توسعه‌دهنده هستند. دلیل این الزام، مسائل امنیتی و پاسخگو بودن توسعه‌دهنده در برابر کارهای خود است. برای ثبت نام دریافت یک کلید API نیویورک‌تایمز می‌توانید از مراحلی که در این صفحه (+) توصیف شده‌اند پیروی کنید.

  • ابتدا یک کلید برای API جستجوی مقاله درخواست می‌کنیم، یک اپلیکیشن تازه ایجاد می‌کنیم و آن را به عنوان API-یی که قرار است استفاده کنیم انتخاب می‌کنیم. به این منظور نام و توضیح را وارد کنید، سوئیچ Article Search API را در حالت فعال قرار دهید و سپس روی Create کلیک کنید.
  • کلید API را از صفحه بعدی دریافت کنید.
  • اکنون می‌توانید با تهیه یک کپی از فایل‌های nytimes_start.html و nytimes.css زیر روی سیستم خود در یک دایرکتوری جدید شروع به ساخت مثال بکنید:

فایل nytimes_start.html

1<!DOCTYPE html>
2<html>
3  <head>
4    <meta charset="utf-8">
5    <title>NY Times API example</title>
6    <link href="nytimes.css" rel="stylesheet">
7  </head>
8  <body>
9    <h1>NY Times video search</h1>
10
11    <div class="wrapper">
12
13      <div class="controls">
14        <form>
15          <p>
16            <label for="search">Enter a SINGLE search term (required): </label>
17            <input type="text" id="search" class="search" required>
18          </p>
19          <p>
20            <label for="start-date">Enter a start date (format YYYYMMDD): </label>
21            <input type="date" id="start-date" class="start-date" pattern="[0-9]{8}">
22          </p>
23          <p>
24            <label for="end-date">Enter an end date (format YYYYMMDD): </label>
25            <input type="date" id="end-date" class="end-date" pattern="[0-9]{8}">
26          </p>
27          <p>
28            <button class="submit">Submit search</button>
29          </p>
30        </form>
31      </div>
32
33      <div class="results">
34        <nav>
35          <button class="prev">Previous 10</button>
36          <button class="next">Next 10</button>
37        </nav>
38
39        <section>
40        </section>
41      </div>
42
43    </div>
44
45    <script>
46      // Defining a baseURL and key to as part of the request URL
47      var baseURL = 'https://api.nytimes.com/svc/search/v2/articlesearch.json';
48      var key = 'INSERT-YOUR-API-KEY-HERE';
49      var url;
50      // Grab references to all the DOM elements you'll need to manipulate
51      var searchTerm = document.querySelector('.search');
52      var startDate = document.querySelector('.start-date');
53      var endDate = document.querySelector('.end-date');
54      var searchForm = document.querySelector('form');
55      var submitBtn = document.querySelector('.submit');
56      var nextBtn = document.querySelector('.next');
57      var previousBtn = document.querySelector('.prev');
58      var section = document.querySelector('section');
59      var nav = document.querySelector('nav');
60      // Hide the "Previous"/"Next" navigation to begin with, as we don't need it immediately
61      nav.style.display = 'none';
62      // define the initial page number and status of the navigation being displayed
63      var pageNumber = 0;
64      var displayNav = false;
65      // Event listeners to control the functionality
66    </script>
67
68
69  </body>
70</html>

فایل nytimes.css

1body {
2  font-family: sans-serif;
3}
4
5.wrapper {
6  width: 80%;
7  margin: 0 auto;
8  display: flex;
9}
10
11.controls, .results {
12  flex: 1;
13  padding: 10px;
14}
15
16form p:nth-of-type(1) {
17  margin-top: 0;
18}
19
20h1 {
21  text-align: center;
22}
23
24h2 {
25  font-size: 20px;
26}
27
28article p {
29  font-size: 14px;
30  line-height: 1.5;
31}
32
33article p:nth-of-type(2) {
34  font-size: 14px;
35  line-height: 2;
36}
37
38nav {
39  margin-bottom: 50px;
40}
41
42.prev {
43  float: left;
44}
45
46.next {
47  float: right;
48}
49
50article {
51  padding: 10px;
52  margin-bottom: 20px;
53  background-color: #ddd;
54  border: 1px solid #ccc;
55}
56
57img {
58  float: right;
59  margin-left: 20px;
60  max-width: 200px;
61}
62
63.clearfix {
64  clear: both;
65}
66
67span {
68  background-color: #ccc;
69  padding: 5px;
70  margin: 5px;
71}

در ابتدا عنصر <script> شامل تعدادی متغیرهای مورد نیاز برای راه‌اندازی مثال است. در ادامه کارکردهای دیگر را که مورد نیاز است را نیز وارد می‌کنیم.

اپلیکیشنی که در انتها به دست خواهد آمد، امکان وارد کردن کلمه‌های کلیدی جستجو و همچنین به صورت اختیاری وارد کردن تاریخ آغاز و انتها را می‌دهد که از آن‌ها برای کوئری زدن به API جستجوی مقاله و نمایش نتایج جستجو استفاده خواهد شد.

اتصال API به اپلیکیشن

ابتدا باید یک اتصال بین API و اپلیکیشن خود برقرار کنید. در مورد این API خاص باید کلید API را هر بار به صورت یک پارامتر Get در داده‌های درخواست خود به سرویس به صورت یک URL صحیح ارائه کنید.

خط زیر را پیدا کنید:

1var key = ' ... ';

کلید API موجود را با کلید API واقعی خودتان که در بخش قبل به دست آوردید، جایگزین کنید. بخش توضیح زیر را در کد جاوا اسکریپت پیدا کنید:

1// Event listeners to control the functionality

خط کد زیر را در ادامه آن وارد کنید. این کد زمانی که فرم تحویل می‌شود، یک تابع به نام ()submitSearch اجرا می‌کند:

1searchForm.addEventListener('submit', submitSearch);

اکنون تعاریف تابع‌های ()submitSearch و ()fetchResults را در ادامه کد قبلی وارد کنید:

1function submitSearch(e) {
2  pageNumber = 0;
3  fetchResults(e);
4}
5
6function fetchResults(e) {
7  // Use preventDefault() to stop the form submitting
8  e.preventDefault();
9
10  // Assemble the full URL
11  url = baseURL + '?api-key=' + key + '&page=' + pageNumber + '&q=' + searchTerm.value + '&fq=document_type:("article")';
12
13  if(startDate.value !== '') {
14    url += '&begin_date=' + startDate.value;
15  };
16
17  if(endDate.value !== '') {
18    url += '&end_date=' + endDate.value;
19  };
20
21}

تابع ()submitSearch شماره صفحه را به 0 بازگشت می‌دهد تا به ابتدای نتایج بروید و سپس ()fetchResults را فراخوانی می‌کند این تابع ابتدا ()preventDefault را روی شیء رویداد فراخوانی می‌کند تا عملاً فرم را از تحویل شدن باز دارد. این کار به منظور عملی ساختن مثال ضروری است. سپس از نوعی دستکاری رشته برای ساختن URL کامل که در درخواست ارائه می‌کنیم بهره می‌گیریم. کار خود را با تجمیع بخش‌هایی که در این دمو ضروری هستند آغاز می‌کنیم:

  • URL مبنا (که از متغیر ()preventDefault) گرفته شده است.
  • کلید API که در یک پارامتر URL به نام api-key تعیین می‌شود و مقدار گرفته شده از متغیر key است.
  • شماره صفحه که باید در پارامتر URL به نام page ذکر شود و مقدار آن از متغیر pageNumber گرفته می‌شود.
  • کلمه جستجو که باید در پارامتر q مربوط به URL قید شود و مقدار آن از متن SearchTerm عنصر <input> به دست می‌آید.
  • نوع سند نتایج بازگشتی که در عبارت ارسالی در پارامتر URL به نام fq مشخص می‌شود. در این مثال ما می‌خواهیم مقاله‌ها بازگشت یابند.

سپس از چند گزاره ()if برای بررسی این که startDate و <endDate <input مقادیری دارند یا نه استفاده می‌کنیم. اگر مقادیری وارد شده باشد، مقادیر مربوطه به URL الحاق می‌شوند و به ترتیب در پارامترهای URL به نام begin_date و end_date قرار می‌گیرند.

بنابراین در نهایت URL تکمیل شده چیزی مانند زیر خواهد بود:

1https://api.nytimes.com/svc/search/v2/articlesearch.json?api-key=YOUR-API-KEY-HERE&page=0&q=cats
2&fq=document_type:("article")&begin_date=20170301&end_date=20170312

نکته: برای مشاهده جزییات بیشتر در مورد پارامترهای URL به مستندات توسعه‌دهندگان نیویورک‌تایمز (+) مراجعه کنید.

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

درخواست داده از API

اکنون که URL خود را ساخته‌ایم، با استفاده از آن درخواستی ارسال می‌کنیم. به این منظور از Fetch API استفاده خواهیم کرد.

بلوک کد زیر را درون تابع ()fetchResults درست بالاتر از آکولاد پایانی وارد کنید:

1// Use fetch() to make the request to the API
2fetch(url).then(function(result) {
3  return result.json();
4}).then(function(json) {
5  displayResults(json);
6});

در این کد درخواست را از طریق ارسال متغیر url خودمان به ()fetch اجرا می‌کنیم و بدنه پاسخ را با استفاده از تابع ()json به قالب JSON تبدیل می‌کنیم. سپس JSON حاصل را به تابع ()displayResults ارسال می‌کنیم تا داده‌ها بتوانند در رابط کاربری‌مان نمایش پیدا کنند.

نمایش دادن داده‌ها

اینک نوبت آن رسیده است که به بررسی روش نمایش داده‌ها بپردازیم. تابع زیر را در زیر تابع ()fetchResults اضافه کنید:

1function displayResults(json) {
2  while (section.firstChild) {
3      section.removeChild(section.firstChild);
4  }
5
6  var articles = json.response.docs;
7
8  if(articles.length === 10) {
9    nav.style.display = 'block';
10  } else {
11    nav.style.display = 'none';
12  }
13
14  if(articles.length === 0) {
15    var para = document.createElement('p');
16    para.textContent = 'No results returned.'
17    section.appendChild(para);
18  } else {
19    for(var i = 0; i < articles.length; i++) {
20      var article = document.createElement('article');
21      var heading = document.createElement('h2');
22      var link = document.createElement('a');
23      var img = document.createElement('img');
24      var para1 = document.createElement('p');
25      var para2 = document.createElement('p');
26      var clearfix = document.createElement('div');
27
28      var current = articles[i];
29      console.log(current);
30
31      link.href = current.web_url;
32      link.textContent = current.headline.main;
33      para1.textContent = current.snippet;
34      para2.textContent = 'Keywords: ';
35      for(var j = 0; j < current.keywords.length; j++) {
36        var span = document.createElement('span');
37        span.textContent += current.keywords[j].value + ' ';
38        para2.appendChild(span);
39      }
40
41      if(current.multimedia.length > 0) {
42        img.src = 'http://www.nytimes.com/' + current.multimedia[0].url;
43        img.alt = current.headline.main;
44      }
45
46      clearfix.setAttribute('class','clearfix');
47
48      article.appendChild(heading);
49      heading.appendChild(link);
50      article.appendChild(img);
51      article.appendChild(para1);
52      article.appendChild(para2);
53      article.appendChild(clearfix);
54      section.appendChild(article);
55    }
56  }
57}

چنان که مشاهده می‌کنید، کد گسترده‌ای است. در ادامه آن را گام به گام توضیح می‌دهیم.

حلقه while یک الگوی رایج برای حذف محتوای یک عنصر DOM محسوب می‌شود که در این مورد عنصر <section> است. ما دائماً بررسی می‌کنیم که آیا <section> یک فرزند دارد یا نه و اگر چنین بود فرزند نخست را حذف می‌کنیم. حلقه زمانی خاتمه می‌یابد که <section> دیگر هیچ فرزندی نداشته باشد.

سپس متغیر articles را برابر با json.response.docs قرار می‌دهیم که آرایه‌ای شامل همه اشیای نمایش‌دهنده مقاله است و در نتیجه جستجو بازگشت یافته است. این کار صرفاً به منظور ساده‌تر ساختن کدهای بعدی انجام می‌یابد.

بلوک نخست ()if بررسی می‌کند که آیا 10 مقاله بازگشت یافته است یا نه (API هر بار 10 مقاله بازگشت می‌دهد). اگر چنین باشد، عنصر <nav> را نمایش می‌دهیم که شامل دکمه‌های صفحه‌بندی «Previous 10» و «Next 10» است. اگر کمتر از 10 مقاله بازگشت یافته باشند، همه آن‌ها در یک صفحه جای می‌گیرند و از این رو نیازی به نمایش دکمه‌های صفحه‌بندی وجود ندارد. کارکرد صفحه‌بندی را در بخش بعدی تکمیل می‌کنیم.

بلوک ()if بعدی بررسی می‌کند که آیا هیچ مقاله‌ای بازگشت یافته است یا نه. اساساً اگر مقاله‌ای بازگشت نیافته باشد، ما هیچ چیز نمایش نمی‌دهیم و صرفاً یک پاراگراف <p> می‌سازیم و عبارت «No results returned.» را در آن نشان می‌دهیم. این پاراگراف را درون یک <section> درج می‌کنیم.

اگر برخی مقالات بازگشت یافته باشند، قبل از هر چیز همه عناصری که می‌خواهیم برای نمایش هر خبر استفاده کنیم را ایجاد می‌کنیم، محتوای صحیح را درون هر جزء قرار می‌دهیم و سپس آن‌ها را در مکان‌های مناسبی درون DOM درج می‌کنیم. برای فهمیدن این که هر مشخصه در شیء مقاله شامل داده‌های صحیح برای نمایش یافتن است به مستندات Article Search API (+) مراجعه کنید. اغلب این عملیات کاملاً بدیهی است؛ اما چند نکته ارزش یادآوری دارد.

ما از حلقه for زیر برای تعریف حلقه‌ای روی همه کلیدواژه‌های مرتبط با هر مقاله استفاده کرده‌ایم و هر کدام را درون یک عنصر <span> درون یک <p> قرار داده‌ایم. بدین ترتیب کار استایل‌بندی آن‌ها آسان‌تر می‌شود:

1for(var j = 0; j < current.keywords.length; j++) { ... }

ما از یک بلوک ()if به صورت زیر برای بررسی این که هر مقاله دارای تصاویر است یا نه، استفاده کرده‌ایم، چون برخی مقالات تصویری ندارند. در صورت وجود تصویر، نخستین تصویر موجود را نمایش می‌دهیم، در غیر این صورت خطایی ایجاد خواهد شد:

1if(current.multimedia.length > 0) { ... }

ما به عنصر <div> خود یک کلاس clearfix داده‌ایم، بنابراین می‌توانیم به سادگی «فاصله‌بندی» (clearing) را روی آن اعمال کنیم.

طراحی نهایی دکمه‌های صفحه‌بندی

برای این که دکمه‌های صفحه‌بندی کار کنند، مقدار متغیر pageNumber را افزایش (یا کاهش) خواهیم داد و سپس درخواست fetch را مجدداً با قرار دادن مقدار جدید در پارامتر page مربوط به URL اجرا خواهیم کرد. دلیل این کار آن است که API نیویورک‌تایمز هر بار تنها 10 نتیجه بازگشت می‌دهد. در صورتی که بیش از 10 نتیجه وجود داشته باشند این API در صورت تعیین مقدار 0 برای پارامتر page در URL یا در صورتی که هیچ پارامتری تعیین نشده باشد (چون مقدار پیش‌فرض 0 است) تنها 10 نتیجه اول یعنی شماره 0 تا 9 را بازگشت می‌دهد. در صورتی که مقدار این پارامتر 1 باشد، 10 نتیجه بعدی یعنی نتایج 10 تا 19 بازگشت می‌یابند و همین طور تا آخر ادامه پیدا می‌کند.

بدین ترتیب می‌توانیم یک تابع صفحه‌بندی ساده را به روشی آسان بنویسیم:

زیر فراخوانی ()addEventListener موجود، این دو فراخوانی را اضافه می‌کنیم که باعث می‌شوند تابع‌های ()nextPage و ()previousPage هنگام کلیک شدن دکمه‌های مربوطه اجرا شوند:

1nextBtn.addEventListener('click', nextPage);
2previousBtn.addEventListener('click', previousPage);

زیر دکمه صفحه قبلی (previous) دو تابع تعریف می‌کنیم و کد زیر را اضافه می‌کنیم:

1function nextPage(e) {
2  pageNumber++;
3  fetchResults(e);
4};
5
6function previousPage(e) {
7  if(pageNumber > 0) {
8    pageNumber--;
9  } else {
10    return;
11  }
12  fetchResults(e);
13};

تابع اول ساده است. این تابع متغیر pageNumber را افزایش می‌دهد و سپس تابع ()fetchResults را بار دیگر برای نمایش نتیجه صفحه بعدی اجرا می‌کند.

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

کد کامل مثال API نیویورک‌تایمز را می‌توانید در ادامه مشاهده کنید:

1<!DOCTYPE html>
2<html>
3  <head>
4    <meta charset="utf-8">
5    <title>NY Times API example</title>
6    <link href="nytimes.css" rel="stylesheet">
7  </head>
8  <body>
9    <h1>NY Times video search</h1>
10
11    <div class="wrapper">
12
13      <div class="controls">
14        <form>
15          <p>
16            <label for="search">Enter a SINGLE search term (required): </label>
17            <input type="text" id="search" class="search" required>
18          </p>
19          <p>
20            <label for="start-date">Enter a start date (format YYYYMMDD): </label>
21            <input type="date" id="start-date" class="start-date" pattern="[0-9]{8}">
22          </p>
23          <p>
24            <label for="end-date">Enter an end date (format YYYYMMDD): </label>
25            <input type="date" id="end-date" class="end-date" pattern="[0-9]{8}">
26          </p>
27          <p>
28            <button class="submit">Submit search</button>
29          </p>
30        </form>
31      </div>
32
33      <div class="results">
34        <nav>
35          <button class="prev">Previous 10</button>
36          <button class="next">Next 10</button>
37        </nav>
38
39        <section>
40        </section>
41      </div>
42
43    </div>
44
45    <script>
46      // Defining a baseURL and key to as part of the request URL
47      var baseURL = 'https://api.nytimes.com/svc/search/v2/articlesearch.json';
48      var key = '3Cm3bHxG1I3ROE2N94Y8vw7347XEAaQk';
49      var url;
50      // Grab references to all the DOM elements you'll need to manipulate
51      var searchTerm = document.querySelector('.search');
52      var startDate = document.querySelector('.start-date');
53      var endDate = document.querySelector('.end-date');
54      var searchForm = document.querySelector('form');
55      var submitBtn = document.querySelector('.submit');
56      var nextBtn = document.querySelector('.next');
57      var previousBtn = document.querySelector('.prev');
58      var section = document.querySelector('section');
59      var nav = document.querySelector('nav');
60      // Hide the "Previous"/"Next" navigation to begin with, as we don't need it immediately
61      nav.style.display = 'none';
62      // define the initial page number and status of the navigation being displayed
63      var pageNumber = 0;
64      var displayNav = false;
65      // Event listeners to control the functionality
66      searchForm.addEventListener('submit', submitSearch);
67      nextBtn.addEventListener('click', nextPage);
68      previousBtn.addEventListener('click', previousPage);
69      function submitSearch(e){
70        pageNumber = 0;
71        fetchResults(e);
72      }
73      function fetchResults(e) {
74        // Use preventDefault() to stop the form submitting
75        e.preventDefault();
76        // Assemble the full URL
77        url = baseURL + '?api-key=' + key + '&page=' + pageNumber + '&q=' + searchTerm.value + '&fq=document_type:("article")';
78        if(startDate.value !== '') {
79          url += '&begin_date=' + startDate.value;
80        };
81        if(endDate.value !== '') {
82          url += '&end_date=' + endDate.value;
83        };
84        // Use fetch() to make the request to the API
85        fetch(url).then(function(result) {
86          return result.json();
87        }).then(function(json) {
88          displayResults(json);
89        });
90      }
91      function displayResults(json) {
92        while (section.firstChild) {
93            section.removeChild(section.firstChild);
94        }
95        var articles = json.response.docs;
96        if(articles.length === 10) {
97          nav.style.display = 'block';
98        } else {
99          nav.style.display = 'none';
100        }
101        if(articles.length === 0) {
102          var para = document.createElement('p');
103          para.textContent = 'No results returned.'
104          section.appendChild(para);
105        } else {
106          for(var i = 0; i < articles.length; i++) {
107            var article = document.createElement('article');
108            var heading = document.createElement('h2');
109            var link = document.createElement('a');
110            var img = document.createElement('img');
111            var para1 = document.createElement('p');
112            var para2 = document.createElement('p');
113            var clearfix = document.createElement('div');
114            var current = articles[i];
115            console.log(current);
116            link.href = current.web_url;
117            link.textContent = current.headline.main;
118            para1.textContent = current.snippet;
119            para2.textContent = 'Keywords: ';
120            for(var j = 0; j < current.keywords.length; j++) {
121              var span = document.createElement('span');
122              span.textContent = current.keywords[j].value + ' ';
123              para2.appendChild(span);
124            }
125            if(current.multimedia.length > 0) {
126              img.src = 'http://www.nytimes.com/' + current.multimedia[0].url;
127              img.alt = current.headline.main;
128            }
129            clearfix.setAttribute('class','clearfix');
130            article.appendChild(heading);
131            heading.appendChild(link);
132            article.appendChild(img);
133            article.appendChild(para1);
134            article.appendChild(para2);
135            article.appendChild(clearfix);
136            section.appendChild(article);
137          }
138        }
139      };
140      function nextPage(e) {
141        pageNumber++;
142        fetchResults(e);
143      };
144      function previousPage(e) {
145        if(pageNumber > 0) {
146          pageNumber--;
147        } else {
148          return;
149        }
150        fetchResults(e);
151      };
152    </script>
153
154
155  </body>
156</html>

مثال یوتیوب

در این بخش یک مثال دیگر نیز برای مطالعه و آشنایی بیشتر ایجاد می‌کنیم.

فایل index.html

1<!DOCTYPE html>
2<html>
3  <head>
4    <meta charset="utf-8">
5    <title>YouTube APIs example</title>
6    <link href="youtube.css" rel="stylesheet">
7  </head>
8  <body>
9    <h1>YouTube video search</h1>
10
11    <div class="wrapper">
12
13      <div class="controls">
14        <form>
15          <p>
16            <label for="search">Enter a search term (required): </label>
17            <input type="text" id="search" class="search" required>
18          </p>
19          <p>
20            <button class="submit">Submit search</button>
21          </p>
22        </form>
23      </div>
24
25      <div class="results">
26        <section>
27        </section>
28      </div>
29
30    </div>
31
32    <!--
33      Apply both necessary API scripts to your HTML. The first is for the YouTube Data
34      API, and the second is for the YouTube Iframe Player API
35    -->
36    <script src="https://apis.google.com/js/client.js" type="text/javascript"></script>
37    <script src="https://www.youtube.com/iframe_api" type="text/javascript"></script>
38
39    <script>
40      // Get references to DOM elements we need to manipulate
41      var searchTerm = document.querySelector('.search');
42      var searchForm = document.querySelector('form');
43      var submitBtn = document.querySelector('.submit');
44      var section = document.querySelector('section');
45      // When the window (tab) has finished loading, run onClientLoad() to get
46      // It all started
47      window.onload = onClientLoad;
48      // Load and inialize the API, then run the onYouTubeApiLoad() function once this is done
49      function onClientLoad() {
50        gapi.client.load('youtube', 'v3', onYouTubeApiLoad);
51      }
52      // Attach your key to the API
53      function onYouTubeApiLoad() {
54        // To get a key for your own application:
55        // 1. Go to https://console.cloud.google.com/apis/dashboard
56        // 2. Create a new project if you've not already got one
57        // 3. Click the Enable API button
58        // 4. Choose YouTube Data API
59        // 5. Click the Enable button
60        // 6. Click create credentials
61        // 7. Select "Web browser (JavaScript)" from the second dropdown
62        // 8. Click the "Public data" radio button
63        // 9. Click the "What credentials do I need?" button
64        // 10. Copy your API key and paste it in below
65        gapi.client.setApiKey('ENTER-API-KEY-HERE');
66        // Attach an event listener to the form so that a search is carried out
67        // when it is submitted — the search() function
68        searchForm.addEventListener('submit', search);
69      }
70      function search(e) {
71        // use preventDefault() to stop form actually submitting
72        e.preventDefault();
73        // create a search request using the Data API;
74        var request = gapi.client.youtube.search.list({
75          // set what kind of data the response will include
76          part: 'snippet',
77          // set the number of results that will be returned
78          maxResults: 10,
79          // set the search query to search for
80          q: searchTerm.value
81        });
82        // send the request, and specify a function to run when the response returns
83        request.execute(onSearchResponse);
84      }
85      // This function automatically has the response passed in as a parameter
86      function onSearchResponse(response) {
87        // Empty the <section> element
88        while (section.firstChild) {
89            section.removeChild(section.firstChild);
90        }
91        // Store the actual results of the search in a variable
92        var results = response.items;
93        // loop through the results and run displayVideo() on each
94        for(var i = 0; i < results.length; i++) {
95          displayVideo(results[i], i);
96        }
97      }
98      function displayVideo(result, i) {
99        // Create a div with a unique ID for each video, and append it to the <section>
100        // The YouTube Iframe Player API will replace each one with
101        // an <iframe> containing the corresponding video
102        var vid = document.createElement('div');
103        vidId = 'vid' + i;
104        vid.id = vidId;
105        section.appendChild(vid);
106        // Use the YT.Player() constructor to create a new video player object,
107        // Specifying the ID of the element to be replaced by it (the <div>),
108        // A height and width, and an event handler to handle the custom onReady event
109        var player = new YT.Player(vidId, {
110          height: '360',
111          width: '480',
112          videoId: result.id.videoId,
113          events: {
114            'onReady': onPlayerReady
115          }
116        });
117        // The onPlayerReady() handler grabs the ID of each video, and checks its duration
118        // If the duration is 0, the video can't be played, so we just delete it
119        function onPlayerReady(e) {
120          var myId = e.target.a.id;
121          var duration = e.target.getDuration();
122          if(duration === 0) {
123            console.log('Video ' + myId + ' cannot be played, so it was deleted.');
124            section.removeChild(e.target.a);
125          } else {
126            var myId = e.target.a.id;
127            console.log('Video ' + myId + ' ready to play.');
128          }
129        }
130      }
131    </script>
132
133
134  </body>
135</html>

فایل youtube.css

1body {
2  font-family: sans-serif;
3}
4
5.wrapper {
6  width: 80%;
7  margin: 0 auto;
8  display: flex;
9}
10
11.controls, .results {
12  flex: 1;
13  padding: 10px;
14}
15
16form p:nth-of-type(1) {
17  margin-top: 0;
18}
19
20h1 {
21  text-align: center;
22}
23
24h2 {
25  font-size: 20px;
26}
27
28article p {
29  font-size: 14px;
30  line-height: 1.5;
31}
32
33article p:nth-of-type(2) {
34  font-size: 14px;
35  line-height: 2;
36}
37
38nav {
39  margin-bottom: 50px;
40}
41
42.prev {
43  float: left;
44}
45
46.next {
47  float: right;
48}
49
50article {
51  padding: 10px;
52  margin-bottom: 20px;
53  background-color: #ddd;
54  border: 1px solid #ccc;
55}
56
57img {
58  float: right;
59  margin-left: 20px;
60  max-width: 200px;
61}
62
63.clearfix {
64  clear: both;
65}
66
67span {
68  background-color: #ccc;
69  padding: 5px;
70  margin: 5px;
71}

این مثال از دو API مرتبط استفاده می‌کند:

  • YouTube Data API برای جستجوی ویدئوهای یوتیوب و بازگشت نتایج استفاده می‌شود.
  • YouTube IFrame Player API برای نمایش مثال‌هایی از ویدئوهای بازگشتی درون پخش‌کننده ویدئوی iFrame استفاده می‌شود تا کاربر بتواند آن‌ها را تماشا کند.

این مثال جالب است، زیرا دو API شخص ثالث مرتبط را که همراه با هم برای ساخت یک اپلیکیشن استفاده می‌شوند نمایش می‌دهد.API نخست از نوع RESTful است، در حالی که دومی بیشتر شبیه به مثال Mapquest عمل می‌کند و متدهای خاص API دارد. با این حال لازم به ذکر است که هر دو API نیازمند یک کتابخانه جاوا اسکریپت برای استفاده در یک صفحه هستند. API به شکل RESTful تابع‌هایی دارد که ایجاد درخواست‌های HTTP و بازگشت نتایج را مدیریت می‌کنند.

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

اگر این مطلب برای شما مفید بوده است، آموزش‌های زیر نیز به شما پیشنهاد می‌شوند:

==

بر اساس رای ۱ نفر
آیا این مطلب برای شما مفید بود؟
اگر بازخوردی درباره این مطلب دارید یا پرسشی دارید که بدون پاسخ مانده است، آن را از طریق بخش نظرات مطرح کنید.
منابع:
developer.mozilla
نظر شما چیست؟

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *