{{meta {}}}
{{quote {author: «Tim Berners-Lee», chapter: true}}
Людям часто було важко зрозуміти, що крім URL-адрес, HTTP і HTML не було нічого іншого. Не було центрального комп'ютера, який би «керував» Інтернетом, не було єдиної мережі, в якій би працювали ці протоколи, не було навіть організації, яка б «керувала» Інтернетом.
Інтернет не був фізичною «річчю», яка існувала в певному «місці». Це був «простір», в якому могла існувати інформація. quote}}
{{index «Філдінг, Рой»}}
{{figure {url: «img/chapter_picture_18.jpg», alt: «Ілюстрація, що показує веб-форму реєстрації на сувої пергаменту», »chapter: «Обрамлення"}}}
{{індекс [браузера, середовища]}}
Протокол передачі гіпертексту, представлений у главі ?, є механізмом, за допомогою якого запитуються та надаються дані у ((всесвітній павутині)). У цій главі більш детально описується ((протокол)) і пояснюється, яким чином JavaScript браузера має доступ до нього.
{{index «IP-адреса»}}
Якщо ви наберете eloquentjavascript.net/18_http.html в ((адресному рядку)) вашого браузера, ((браузер)) спочатку знайде ((адресу)) сервера, пов'язаного з eloquentjavascript.net, і спробує відкрити ((TCP)) ((з'єднання)) з ним через ((порт)) 80, порт за замовчуванням для ((HTTP)) трафіку. Якщо ((сервер)) існує і приймає з'єднання, браузер може надіслати щось на зразок цього:
GET /18_http.html HTTP/1.1
Host: eloquentjavascript.net
User-Agent: Назва вашого браузера
Потім сервер відповідає через те саме з'єднання.
HTTP/1.1 200 OK
Content-Length: 87320
Content-Type: text/html
Востаннє змінено: Fri, 13 Oct 2023 10:05:41 GMT
<!doctype html>
... решта документа
Браузер бере частину ((відповідь)) після порожнього рядка, його тіло (не плутати з тегом HTML <body>
), і відображає його як ((HTML)) документ.
{{індекс HTTP}}
Інформація, яку надсилає клієнт, називається ((запит)). Вона починається з цього рядка:
GET /18_http.html HTTP/1.1
{{індекс «метод DELETE», «метод PUT», «метод GET», [метод, HTTP]}}
Перше слово - це метод ((запиту)). GET
означає, що ми хочемо отримати вказаний ресурс. Інші поширені методи: DELETE
для видалення ресурсу, PUT
для створення або заміни ресурсу і POST
для надсилання інформації до нього. Зауважте, що ((сервер)) не зобов'язаний виконувати кожен запит, який він отримує. Якщо ви підійдете до випадкового сайту і скажете йому DELETE
його головну сторінку, він, ймовірно, відмовиться.
{{index [шлях, URL], GitHub, [файл, ресурс]}}
Частина після імені методу - це шлях до ((ресурсу)), до якого застосовується запит. У найпростішому випадку ресурс - це просто файл на ((сервері)), але протокол не вимагає, щоб він був саме таким. Ресурсом може бути будь-що, що може бути передане так, ніби це файл. Багато серверів генерують відповіді «на льоту». Наприклад, якщо ви відкриваєте https://github.com/marijnh, сервер шукає у своїй базі даних користувача на ім'я «marijnh», і якщо знаходить його, то генерує сторінку профілю для цього користувача.
Після шляху до ресурсу в першому рядку запиту згадується HTTP/1.1
для позначення ((версії)) ((протоколу)) ((HTTP)), який він використовує.
На практиці багато сайтів використовують HTTP версії 2, яка підтримує ті ж концепції, що і версія 1.1, але набагато складніша, тому може бути швидшою. Браузери автоматично перемикаються на відповідну версію протоколу при спілкуванні з певним сервером, і результат запиту буде однаковим незалежно від того, яка версія використовується. Оскільки версія 1.1 є більш простою і з нею легше гратися, ми будемо використовувати її для ілюстрації протоколу.
{{index «status code»}}
Відповідь сервера ((response)) також починатиметься з версії, за якою слідуватиме статус відповіді, спочатку у вигляді тризначного коду статусу, а потім у вигляді рядка, що читається людиною.
HTTP/1.1 200 OK
{{index «200 (код статусу HTTP)», «відповідь про помилку», «404 (код статусу HTTP)»}}
Коди статусу, що починаються з 2, означають, що запит виконано успішно. Коди, що починаються з 4, означають, що з ((запитом)) було щось не так. Найвідомішим кодом статусу HTTP, мабуть, є 404, який означає, що ресурс не вдалося знайти. Коди, що починаються з 5, означають, що сталася помилка на ((сервері)) і запит не винен.
{{index HTTP}}
{{id headers}}
За першим рядком запиту або відповіді може слідувати будь-яка кількість ((заголовків))s. Це рядки у вигляді name: value
, які вказують додаткову інформацію про запит або відповідь. Ці заголовки були частиною прикладу ((response)):
Content-Length: 87320
Content-Type: text/html
Востаннє змінено: Fri, 13 Oct 2023 10:05:41 GMT
{{index «Content-Length header», «Content-Type header», «Last-Modified header»}}
Це показує нам розмір і тип документа-відповіді. У цьому випадку це HTML-документ розміром 87 320 байт. Він також повідомляє нам, коли цей документ було востаннє змінено.
Клієнт і сервер вільні вирішувати, які ((заголовки)) включати в свої ((запити)) або ((відповіді)). Але деякі з них необхідні для роботи. Наприклад, без заголовка Content-Type
у відповіді браузер не знатиме, як відобразити документ.
{{index «метод GET», «метод DELETE», «метод PUT», «метод POST», «тіло (HTTP)»}}
Після заголовків як запити, так і відповіді можуть містити порожній рядок, за яким слідує тіло, що містить власне документ, який надсилається. Запити GET
і DELETE
не надсилають жодних даних, а запити PUT
і POST
надсилають. Деякі типи відповідей, такі як відповіді на помилки, також не потребують тіла.
{{index HTTP, [файл, ресурс]}}
Як ми бачили, ((браузер)) зробить запит, коли ми введемо ((URL)) в його ((адресний рядок)). Коли результуюча HTML-сторінка посилається на інші файли, такі як ((зображення)) та файли JavaScript, вона також отримає їх.
{{індексний паралелізм, «метод GET»}}
Помірно складний ((веб-сайт)) може легко включати від 10 до 200 ((ресурсів)). Щоб мати змогу швидко отримати їх, браузери роблять декілька GET
-запитів одночасно, замість того, щоб чекати на відповіді по черзі.
HTML-сторінки можуть містити ((форми)), які дозволяють користувачеві заповнювати інформацію та надсилати її на сервер. Ось приклад такої форми:
<form method=«GET» action=«example/message.html»>
<p>Назва: <input type=«text» name=«name»></p></p> <input type=«text» name=«name»></p> </p> </p> </p> <input type="text
<p>Повідомлення:<br><textarea name=«message»></textarea></p></p>
<p><button type=«submit»>Надіслати</button></p></p> <p><button type=«submit»>Надіслати</button></p>
</form> </form
{{index form, «method attribute», «GET method»}}
Цей код описує форму з двома полями: маленьким, де запитується ім'я, і великим, де можна написати повідомлення. Коли ви натискаєте кнопку «Надіслати», форма відправляється, тобто вміст її полів упаковується в HTTP-запит, і браузер переходить до результату цього запиту.
Коли атрибут method
елемента <form>
має значення GET
(або відсутній), інформація з форми додається в кінець URL-адреси action
як ((рядок запиту)). Браузер може зробити запит до цієї URL-адреси:
GET /example/message.html?name=Jean&message=Yes%3F HTTP/1.1
{{index «символ амперсанду»}}
Символ ((знак питання)) вказує на кінець частини шляху до URL-адреси і початок запиту. За ним слідують пари імен і значень, що відповідають атрибуту name
в елементах полів форми і вмісту цих елементів відповідно. Для розділення пар використовується символ амперсанд (&
).
{{index [escape, «в URLs»], «шістнадцяткове число», «функція кодуванняURIComponent», «функція декодуванняURIComponent»}}
Фактичне повідомлення, закодоване в URL, - це «Yes?», але знак питання замінено дивним кодом. Деякі символи в рядках запиту повинні бути екрановані. Знак питання, представлений як %3F
, є одним з них. Здається, існує неписане правило, що для кожного формату потрібен свій власний спосіб екранування символів. Цей спосіб, який називається ((URL-кодування)), використовує знак ((знак відсотка)), за яким слідують дві шістнадцяткові (основа 16) цифри, що кодують код символу. У цьому випадку 3F, що в десятковій системі числення дорівнює 63, є кодом символу знаку питання. JavaScript надає функції encodeURIComponent
та decodeURIComponent
для кодування та декодування цього формату.
console.log(encodeURIComponent(«Yes?»));
// → Так%3F
console.log(decodeURIComponent(«Yes%3F»));
// → Так?
{{index «body (HTTP)», «POST method»}}
Якщо ми змінимо атрибут method
HTML-форми у прикладі, який ми бачили раніше, на POST
, то запит ((HTTP)) на відправку ((форми)) буде використовувати метод POST
і помістить ((рядок запиту)) в тіло запиту замість того, щоб додавати його до URL-адреси.
POST /example/message.html HTTP/1.1
Content-length: 24
Content-type: application/x-www-form-urlencoded
name=Jean&message=Yes%3F
Запити GET
слід використовувати для запитів, які не мають ((побічних ефектів)), а просто запитують інформацію. Запити, які змінюють щось на сервері, наприклад, створення нового облікового запису або відправлення повідомлення, слід виражати іншими методами, такими як POST
. Клієнтське програмне забезпечення, таке як браузер, знає, що не слід сліпо робити запити POST
, але часто неявно робить запити GET
- наприклад, для попередньої вибірки ресурсу, який, на його думку, незабаром знадобиться користувачеві.
Ми повернемося до форм і того, як взаємодіяти з ними за допомогою JavaScript [пізніше в цьому розділі] (http#forms).
{{id fetch}}
{{index «fetch function», «Promise class», [інтерфейс, модуль]}}
Інтерфейс, через який JavaScript браузера може робити HTTP-запити, називається fetch
.
fetch(«example/data.txt»).then(response => {
console.log(response.status);
// → 200
console.log(response.headers.get(«Content-Type»));
// → text/plain
});
{{індекс «Клас відповіді», «властивість стану», «властивість заголовків»}}
Виклик fetch
повертає обіцянку, яка перетворюється на об'єкт Response
, що містить інформацію про відповідь сервера, таку як код статусу та заголовки. Заголовки обгорнуто в об'єкт типу Map
, який розглядає свої ключі (назви заголовків) як нечутливі до регістру, оскільки назви заголовків не повинні бути чутливими до регістру. Це означає, що headers.get(«Content-Type»)
і headers.get(«content-TYPE»)
повернуть те саме значення.
Зауважте, що обіцянка, яку повертає fetch
, успішно виконується, навіть якщо сервер відповів кодом помилки. Вона також може бути відхилена, якщо виникла мережева помилка або якщо ((сервер)), до якого адресовано запит, не може бути знайдено.
{{index [шлях, URL], «відносний URL»}}
Перший аргумент fetch
- це URL-адреса, яку слід запросити. Якщо ця адреса ((URL)) не починається з назви протоколу (наприклад, http:), вона розглядається як відносна, що означає, що вона інтерпретується відносно поточного документа. Якщо він починається з косої риски (/), він замінює поточний шлях, тобто частину після імені сервера. Якщо ні, то перед відносною URL-адресою ставиться частина поточного шляху до останнього символу ((коса риска) включно.
{{index «text method», «body (HTTP)», «Promise class»}}
Щоб отримати фактичний вміст відповіді, ви можете використати її метод text
. Оскільки початкова обіцянка виконується одразу після отримання заголовків відповіді, а читання тіла відповіді може зайняти деякий час, цей метод знову повертає обіцянку.
fetch(«example/data.txt»)
.then(resp => resp.text())
.then(text => console.log(text));
// → Це вміст файлу data.txt
{{index «метод json»}}
Аналогічний метод, який називається json
, повертає обіцянку, яка перетворюється на значення, отримане при розборі тіла як ((JSON)) або відхиляється, якщо це не валідний JSON.
{{індекс «метод GET», «тіло (HTTP)», «метод DELETE», «властивість методу»}}
За замовчуванням fetch
використовує для запиту метод GET
і не включає тіло запиту. Ви можете налаштувати його по-іншому, передавши об'єкт з додатковими параметрами як другий аргумент. Наприклад, цей запит намагається видалити example/data.txt
:
fetch(«example/data.txt», {метод: «DELETE»}).then(resp => {
console.log(resp.status);
// → 405
});
{{index «405 (код статусу HTTP)»}}
Код статусу 405 означає «метод не дозволено», це спосіб сказати HTTP-серверу: «Боюся, я не можу цього зробити».
{{index «Range header», «body property», «headers property»}}
Щоб додати тіло запиту для PUT
або POST
запитів, ви можете включити опцію body
. Щоб задати заголовки, використовується параметр headers
. Наприклад, цей запит містить заголовок Range
, який вказує серверу повернути лише частину документа.
fetch(«example/data.txt», {headers: {Range: «bytes=8-19»}})
.then(resp => resp.text())
.then(console.log);
// → вміст
Браузер автоматично додасть деякі запити ((заголовки)), такі як «Host» і ті, що необхідні серверу для визначення розміру тіла. Але додавання власних заголовків часто буває корисним, щоб включити такі речі, як інформація для автентифікації або вказати серверу, який формат файлу ви бажаєте отримати.
{{id http_sandbox}}
{{index sandbox, [браузер, безпека]}}
Виконання ((HTTP)) запитів у скриптах веб-сторінок знову викликає занепокоєння щодо ((безпеки)). Особа, яка контролює скрипт, може не мати тих самих інтересів, що й особа, на чиєму комп'ютері він виконується. Зокрема, якщо я відвідую themafia.org, я не хочу, щоб його скрипти могли зробити запит до mybank.com, використовуючи ідентифікаційну інформацію з мого браузера, з інструкціями перевести всі мої гроші.
З цієї причини браузери захищають нас, забороняючи скриптам робити HTTP-запити до інших ((доменів)) (таких як themafia.org і mybank.com).
{{index «Access-Control-Allow-Origin header», «cross-domain request»}}
Це може бути прикрою проблемою при створенні систем, які хочуть отримати доступ до декількох доменів з легітимних причин. На щастя, ((сервер))и можуть включати такий ((заголовок)) у свої ((відповідь)), щоб явно вказати браузеру, що запит може надходити з іншого домену:
Access-Control-Allow-Origin: *
{{index client, HTTP, [interface, HTTP]}}
При побудові системи, яка вимагає ((зв'язку)) між програмою на JavaScript, запущеною у ((браузері)) (на стороні клієнта) та програмою на ((сервері)) (на стороні сервера), існує декілька різних способів моделювання цього зв'язку.
{{індекс [мережа, абстракція], абстракція}}
Найчастіше використовується модель ((віддалений виклик процедури))s. У цій моделі взаємодія відбувається за зразком звичайного виклику функції, за винятком того, що функція насправді виконується на іншій машині. Її виклик включає в себе запит до сервера, який містить ім'я функції та аргументи. Відповідь на цей запит містить значення, що повертається.
Якщо мислити в термінах віддаленого виклику процедур, то HTTP є лише засобом зв'язку, і ви, швидше за все, напишете рівень абстракції, який повністю його приховує.
{{index «media type», «document format», [method, HTTP]}}
Інший підхід полягає в тому, щоб побудувати вашу комунікацію навколо концепції ((ресурсу)) і ((HTTP)) методів. Замість віддаленої процедури з назвою addUser
ви використовуєте запит PUT
до /users/larry
. Замість того, щоб кодувати властивості користувача в аргументах функції, ви визначаєте формат документа JSON (або використовуєте існуючий формат), який представляє користувача. Тіло запиту PUT
для створення нового ресурсу є таким документом. Ресурс отримується за допомогою запиту GET
до URL-адреси ресурсу (наприклад, /users/larry
), який знову повертає документ, що представляє ресурс.
Цей другий підхід полегшує використання деяких можливостей HTTP, таких як підтримка кешування ресурсів (зберігання копії ресурсу на клієнті для швидкого доступу). Концепції, що використовуються в HTTP, які добре розроблені, можуть стати корисним набором принципів для проектування інтерфейсу вашого сервера.
{{index «man-in-the-middle», security, HTTPS, [мережа, безпека]}}
Дані, що подорожують інтернетом, зазвичай проходять довгий і небезпечний шлях. Щоб дістатися до місця призначення, їм доводиться перестрибувати через що завгодно - від точок доступу Wi-Fi у кав'ярнях до мереж, контрольованих різними компаніями та державами. У будь-якій точці маршруту його можуть перевірити або навіть змінити.
{{підробка індексу}}
Якщо важливо, щоб щось залишалося в таємниці, наприклад, ((пароль)) до вашого ((електронна адреса)) облікового запису, або щоб воно надійшло до місця призначення без змін, наприклад, номер рахунку, на який ви переказуєте гроші через веб-сайт вашого банку, простого HTTP недостатньо.
{{index cryptography, encryption}}
{{покажчик «Безпечний HTTP», HTTPS, [браузер, безпека]}}
Безпечний ((HTTP)) протокол, що використовується для ((URL)), починаючи з https://, обгортає HTTP-трафік таким чином, щоб його було важче прочитати і підробити. Перед обміном даними клієнт перевіряє, чи є сервер тим, за кого себе видає, запитуючи у нього криптографічний ((сертифікат)), виданий центром сертифікації, який розпізнається браузером. Далі всі дані, що проходять через ((з'єднання)), шифруються таким чином, щоб запобігти підслуховуванню і підробці.
Таким чином, коли він працює правильно, ((HTTPS)) не дозволяє іншим людям видавати себе за веб-сайт, з яким ви намагаєтеся поговорити, і не дозволяє підглядати за вашим спілкуванням. Він не ідеальний, і були різні інциденти, коли HTTPS не працював через підроблені або викрадені сертифікати і зламане програмне забезпечення, але він набагато безпечніший, ніж звичайний HTTP.
{{id forms}}
Форми спочатку були розроблені для веб-сторінок, що не підтримували JavaScript, щоб дозволити веб-сайтам надсилати інформацію, надану користувачем, у HTTP-запиті. Такий дизайн передбачає, що взаємодія з сервером завжди відбувається шляхом переходу на нову сторінку.
{{index [DOM, fields]}}
Однак елементи форми є частиною DOM, як і решта сторінки, а елементи DOM, що представляють форму ((поле)), підтримують ряд властивостей і подій, яких немає в інших елементах. Це дає змогу перевіряти і контролювати такі поля введення за допомогою програм на JavaScript і робити такі речі, як додавання нової функціональності до форми або використання форм і полів як будівельних блоків у додатку на JavaScript.
{{index «форма (HTML-тег)»}}
Веб-форма складається з довільної кількості полів вводу, згрупованих у тезі <form>
. HTML допускає кілька різних стилів полів, починаючи від простих прапорців увімкнути/вимкнути і закінчуючи випадаючими меню та полями для введення тексту. У цій книзі ми не намагатимемося всебічно обговорити всі типи полів, але почнемо з приблизного огляду.
{{index «input (HTML-тег)», «type attribute»}}
Багато типів полів використовують тег <input>
. Атрибут type
цього тегу використовується для вибору стилю поля. Нижче наведено деякі з найпоширеніших типів <input>
:
{{index «поле пароля», «прапорець», «перемикач», «поле файлу»}}
{{table {cols: [1,5]}}}
| text
| Один рядок ((текстове поле))
| password
| Те саме, що й text
, але приховує текст, що вводиться
| checkbox
| Перемикач увімкнення/вимкнення
| color
| Колір
| date
| Календарна дата
| radio
| (Частина) поля ((множинний вибір))
| file
| Дозволяє користувачеві вибрати файл зі свого комп'ютера
{{index «атрибут значення», «перевірений атрибут», «форма (HTML-тег)»}}
Поля форми не обов'язково повинні відображатися у тезі <form>
. Ви можете розмістити їх будь-де на сторінці. Такі безформні поля не можна ((відправити)) відправити (відправити можна лише форму в цілому), але, відповідаючи на введення за допомогою JavaScript, ми часто не хочемо відправляти наші поля у звичайному вигляді.
<p><input type=«text» value=«abc»> (текст)</p>
<p><input type=«password» value=«abc»> (пароль)</p>
<p><input type=«checkbox» checked> (прапорець)</p>
<p><input type=«color» value=«orange»> (колір)</p>
<p><input type=«date» value=«2023-10-13»> (дата)</p></p> <p><input type=«date» value=«2023-10-13»> (дата)</p>
<p><input type=«radio» value=«A» name=«choice»>
<input type=«radio» value=«B» name=«choice» checked>>.
<input type=«radio» value=«C» name=«choice»> (радіо)</p> (радіо)</p> <p style=«text» type=«text» value=«C» name=«choice»> (радіо)</p>
<p><input type=«file»> (файл)</p>
{{if book
Поля, створені за допомогою цього HTML-коду, виглядають наступним чином:
{{figure {url: «img/form_fields.png», alt: «Знімок екрана з різними типами тегів введення», width: “4cm”}}}}
if}}
Інтерфейс JavaScript для таких елементів відрізняється залежно від типу елемента.
{{index «textarea (HTML-тег)», «текстове поле»}}
Багаторядкові текстові поля мають власний тег <textarea>
, головним чином тому, що використання атрибута для визначення багаторядкового початкового значення було б незручним. Тег <textarea>
вимагає відповідного закриваючого тегу </textarea>
і використовує текст між ними, а не атрибут value
, як початковий текст.
<textarea>
один
два
три
<textarea>
{{index «select (HTML-тег)», «опція (HTML-тег)», «множинний вибір», «випадаюче меню»}}
Нарешті, тег <select>
використовується для створення поля, яке дозволяє користувачеві вибирати з декількох попередньо визначених варіантів.
<select>
<option>Млинці</option>
<option>Пудинг</option>
<option>Морозиво</option>
</select>
{{if book
Таке поле виглядає так:
{{figure {url: «img/form_select.png», alt: «Знімок екрана з полем вибору», width: “4cm”}}}}
if}}
{{index «change event»}}
Щоразу, коли значення поля форми змінюється, буде згенеровано подію «change»
.
{{index keyboard, focus}}
{{indexsee «keyboard focus», focus}}
На відміну від більшості елементів у HTML-документах, поля форми можуть отримати клавіатуру ((фокус)). При натисканні, переході на них за допомогою [вкладки]{ім'я_клавіші} або активації в інший спосіб вони стають поточним активним елементом і отримувачем клавіатури ((введення)).
{{index «option (HTML-тег)», «select (HTML-тег)»}}
Таким чином, ви можете вводити текст у ((текстове поле)) лише тоді, коли воно сфокусоване. Інші поля реагують на події клавіатури інакше. Наприклад, меню <select>
намагається перейти до пункту, який містить введений користувачем текст, і реагує на натискання клавіш зі стрілками переміщенням свого виділення вгору і вниз.
{{index «метод фокусування», «метод розмиття», «властивість activeElement»}}
Ми можемо керувати ((focus)) з JavaScript за допомогою методів focus
та blur
. Перший переміщує фокус на DOM-елемент, на якому він викликається, а другий знімає фокус. Значення в document.activeElement
відповідає поточному фокусу елемента.
<input type=«text»>
<script>
document.querySelector(«input»).focus();
console.log(document.activeElement.tagName);
// → INPUT
document.querySelector(«input»).blur();
console.log(document.activeElement.tagName);
// → BODY
</script> </span> </span> </span> </span> </p
{{index «autofocus attribute»}}
На деяких сторінках очікується, що користувач захоче негайно взаємодіяти з полем форми. JavaScript можна використовувати для ((фокусування)) цього поля під час завантаження документа, але HTML також надає атрибут autofocus
, який дає той самий ефект, повідомляючи браузеру про те, чого ми намагаємося досягти. Це дає браузеру можливість вимкнути таку поведінку, коли вона є недоречною, наприклад, коли користувач переключив фокус на щось інше.
{{index «клавіша табуляції», keyboard, «атрибут tabindex», «a (HTML-тег)»}}
Браузери дозволяють користувачеві переміщати фокус по документу, натискаючи [tab]{клавіша} для переходу до наступного елемента, на якому встановлено фокус, і [shift-tab]{клавіша} для повернення до попереднього елемента. За замовчуванням елементи переглядаються у тому порядку, у якому вони з'являються у документі. Цей порядок можна змінити за допомогою атрибута tabindex
. У наступному прикладі документу фокус буде переходити від введення тексту до кнопки OK, а не від посилання на довідку:
<input type=«text» tabindex=1> <a href=«.»>(help)</a>
<button onclick=«console.log(“ok”)» tabindex=2>OK</button>
{{index «атрибут tabindex»}}
За замовчуванням більшість типів HTML-елементів не можуть бути сфокусовані. Ви можете додати атрибут tabindex
до будь-якого елемента, щоб зробити його фокусованим. Значення tabindex
, рівне 0, робить елемент фокусованим без зміни порядку фокусування.
{{index «відключений атрибут»}}
Усі ((форма)) ((поле)) можуть бути відключені через їхній атрибут disabled
. Це атрибут, який може бути вказано без значення - сам факт його присутності вимикає елемент.
<button>З мною все гаразд</button>
<button disabled>Я вийшов</button>
Вимкнені поля не можна ((фокусувати)) редагувати або змінювати, а браузери роблять їх сірими і бляклими.
{{if book
{{figure {url: «img/button_disabled.png», alt: «Знімок вимкненої кнопки», width: “3cm”}}}}
if}}
{{index «user experience»}}
Коли програма перебуває у процесі обробки дії, спричиненої деяким ((кнопкою)) або іншим елементом керування, що може вимагати зв'язку з сервером і, таким чином, зайняти деякий час, може бути гарною ідеєю відключити цей елемент керування до завершення дії. Таким чином, коли користувач втратить терпіння і натисне на нього знову, він випадково не повторить свою дію.
{{index «масивний об'єкт», «форма (HTML-тег)», «властивість форми», «властивість елементів»}}
Коли ((поле)) міститься в елементі <form>
, його DOM-елемент матиме властивість form
, що посилається на DOM-елемент форми. Елемент <form>
, у свою чергу, має властивість elements
, яка містить масивну колекцію полів всередині нього.
{{index «властивість елементів», «атрибут name»}}
Атрибут name
поля форми визначає спосіб ідентифікації його значення під час відправлення форми. Він також може бути використаний як ім'я властивості при доступі до властивості elements
форми, яка діє як масивний об'єкт (доступний за номером) і як ((map)) (доступний за ім'ям).
<form action=«example/submit.html»>
Ім'я: <input type=«text» name=«name»><br></p> <br> <
Пароль: <input type=«password» name=«password»><br></input
<button type=«submit»>Увійти</button></button
</form> </form
<скрипт
let form = document.querySelector(«form»);
console.log(form.elements[1].type);
// → пароль
console.log(form.elements.password.type);
// → пароль
console.log(form.elements.name.form == form);
// → true
</script> </script> </script> </script> </script
{{index «button (HTML-тег)», «type attribute», submit, «enter key»}}
Кнопка з атрибутом type
, що має значення submit
, при натисканні призведе до відправлення форми. Натискання [enter]{назва клавіші}, коли поле форми сфокусовано, має такий самий ефект.
{{index «submit event», «event handling», «preventDefault method», «page reload», «GET method», «POST method»}}
Відправлення ((форми)) зазвичай означає, що ((браузер)) переходить на сторінку, вказану в атрибуті action
форми, використовуючи GET
або POST
((запит)). Але перед тим, як це станеться, відбувається подія «submit»
. Ви можете обробити цю подію за допомогою JavaScript і запобігти поведінці за замовчуванням, викликавши preventDefault
на об'єкті події.
<form>
Значення: <input type=«text» name=«value»>
<button type=«submit»>Зберегти</button
</form> </form
<скрипт
let form = document.querySelector(«form»);
form.addEventListener(«submit», event => {})
console.log(«Збереження значення», form.elements.value.value);
event.preventDefault();
});
</script> </span> </span> </span> </span> </p
{{index «submit event», validation}}
Перехоплення події «submit»
в JavaScript має різні застосування. Ми можемо написати код, який перевіряє, чи введені користувачем значення мають сенс, і одразу ж показує повідомлення про помилку замість того, щоб відправити форму. Або ми можемо повністю відключити звичайний спосіб надсилання форми, як у прикладі, і змусити нашу програму обробляти введені дані, можливо, використовуючи fetch
, щоб відправити їх на сервер без перезавантаження сторінки.
{{index «value attribute», «input (HTML-тег)», «text field», «textarea (HTML-тег)», [DOM, fields], [interface, object]}}
Поля, створені тегами <textarea>
або <input>
з типом text
або password
, мають спільний інтерфейс. Їхні DOM-елементи мають властивість value
, яка зберігає їхній поточний вміст у вигляді рядкового значення. Встановлення цієї властивості на інший рядок змінює вміст поля.
{{index «selectionStart property», «selectionEnd property»}}
Властивості selectionStart
і selectionEnd
((текстового поля)) надають нам інформацію про ((курсор)) і ((виділення)) у ((тексті)). Коли нічого не виділено, ці дві властивості мають однакове значення, що вказує на позицію курсору. Наприклад, 0 вказує на початок тексту, а 10 означає, що курсор знаходиться після 10^-го символу ((символ)). Коли частину поля виділено, ці дві властивості будуть відрізнятися, показуючи нам початок і кінець виділеного тексту. Як і value
, ці властивості також можна записувати.
{{index Khasekhemwy, «textarea (HTML-тег)», keyboard, «обробка подій»}}
Уявіть, що ви пишете статтю про Хасехемві, останнього фараона Другої династії, але маєте проблеми з написанням його імені. Наступний код підключає тег <textarea>
з обробником подій, який при натисканні клавіші F2 вставляє рядок «Khasekhemwy» замість вас.
<textarea></textarea>
<script>
let textarea = document.querySelector(«textarea»);
textarea.addEventListener(«keydown», event => {
if (event.key == «F2») {
replaceSelection(textarea, «Khasekhemwy»);
event.preventDefault();
}
});
function replaceSelection(поле, слово) {
нехай from = field.selectionStart, to = field.selectionEnd;
field.value = field.value.slice(0, from) + word +
field.value.slice(to);
// Поставити курсор після слова
field.selectionStart = from + word.length;
field.selectionEnd = from + word.length;
}
</script>
{{index «replaceSelection function», «text field»}}
Функція replaceSelection
замінює поточну виділену частину вмісту текстового поля на задане слово, а потім переміщує ((курсор)) після цього слова, щоб користувач міг продовжити введення.
{{index «change event», «input event»}}
Подія «change»
для ((текстового поля)) не спрацьовує щоразу, коли щось вводиться. Натомість вона спрацьовує, коли поле втрачає фокус після зміни його вмісту. Щоб негайно реагувати на зміни у текстовому полі, вам слід зареєструвати обробник для події «input»
, який спрацьовуватиме щоразу, коли користувач вводитиме символ, видалятиме текст або іншим чином маніпулюватиме вмістом поля.
У наступному прикладі показано текстове поле і лічильник, що відображає поточну довжину тексту в полі:
<input type=«text»> length: <span id=«length»>0</span>
<скрипт
let text = document.querySelector(«input»);
let output = document.querySelector(«#length»);
text.addEventListener(«input», () => {} текст.addEventListener(«input», () => {} текст
output.textContent = text.value.length;
});
</script>
{{index «input (HTML-тег)», «checked attribute»}}
Поле ((checkbox)) - це двійковий перемикач. Його значення можна отримати або змінити за допомогою властивості checked
, яка містить булеве значення.
<label>
<input type=«checkbox» id=«purple»> Зробити цю сторінку фіолетовою
</label> </label
<скрипт
let checkbox = document.querySelector(«#purple»);
checkbox.addEventListener(«change», () => {})
document.body.style.background =
checkbox.checked ? «mediumpurple» : “”;
});
</script>
{{index «for attribute», «id атрибута», focus, «label (HTML-тег)», labeling}}
Тег <label>
пов'язує фрагмент документа з елементом вводу ((полем)). Клацання в будь-якому місці мітки активує поле, яке фокусує його і перемикає значення, якщо це прапорець або перемикач.
{{index «input (HTML-тег)», «multiple-choice»}}
Перемикач ((radio button)) схожий на прапорець, але він неявно пов'язаний з іншими перемикачами з тим самим атрибутом name
, тому лише один з них може бути активним у будь-який момент часу.
Колір:
<label>
<input type=«radio» name=«color» value=«orange»> Помаранчевий
</label></label
<label>
<input type=«radio» name=«color» value=«lightgreen»> Зелений
</label> </label
<label>
<input type=«radio» name=«color» value=«lightblue»> Синій
</label> </label
<скрипт
let buttons = document.querySelectorAll(«[name=color]»);
for (let button of Array.from(buttons)) {
button.addEventListener(«change», () => {} button.addEventListener(«change», () => {})
document.body.style.background = button.value;
});
}
</script>
{{index «атрибут name», «метод querySelectorAll»}}
((квадратні дужки)) у CSS-запиті, переданому querySelectorAll
, використовуються для зіставлення атрибутів. Він вибирає елементи, атрибут name
яких має значення color
.
{{index «select (HTML-тег)», «multiple-choice», «option (HTML-тег)»}}
Поля вибору концептуально схожі на перемикачі - вони також дозволяють користувачеві вибирати з набору опцій. Але якщо перемикач ставить розташування варіантів під наш контроль, то вигляд тегу <select>
визначається браузером.
{{index «multiple attribute», «drop-down menu»}}
Поля вибору також мають варіант, більш схожий на список чекбоксів, а не перемикачів. Коли тег <select>
має атрибут multiple
, він дозволяє користувачеві вибрати будь-яку кількість варіантів, а не лише один. У той час як звичайне поле вибору відображається у вигляді випадаючого списку, який показує неактивні опції лише при відкритті, поле з увімкненим атрибутом multiple
показує декілька опцій одночасно, дозволяючи користувачеві вмикати або вимикати їх окремо.
{{index «опція (HTML-тег)», «атрибут значення»}}
Кожен тег <option>
має значення. Це значення може бути визначено за допомогою атрибута value
. Якщо його не вказано, значенням опції буде вважатися ((текст)) всередині неї. Властивість value
елемента <select>
відображає поточну вибрану опцію. Однак для поля multiple
ця властивість не має особливого значення, оскільки вона відображає значення лише одного з поточно вибраних параметрів.
{{index «select (HTML-тег)», «властивість options», «вибраний атрибут»}}
До тегів <option>
для поля <select>
можна отримати доступ як до масивного об'єкта через властивість поля options
. Кожна опція має властивість selected
, яка вказує, чи вибрано цю опцію на даний момент. Ця властивість також може бути використана для вибору або скасування вибору опції.
{{index «multiple attribute», «binary number»}}
Цей приклад витягує вибрані значення з поля вибору multiple
і використовує їх для складання двійкового числа з окремих бітів. Утримуйте [ctrl]{клавіша} (або [command]{клавіша} на Mac), щоб вибрати декілька варіантів.
<вибрати декілька>
<option value=«1»>0001</option>
<option value=«2»>0010</option>
<option value=«4»>0100</option>
<option value=«8»>1000</option>
</select> = <span id=«output»>0</span> </span
<скрипт
let select = document.querySelector(«select»);
let output = document.querySelector(«#output»);
select.addEventListener(«change», () => {})
let number = 0;
for (let option of Array.from(select.options)) {
if (option.selected) {
number += Number(option.value);
}
}
output.textContent = number;
});
</script>
{{індекс файлу, «жорсткий диск», «файлова система», «безпека», «поле файлу», «вхід (HTML-тег)»}}
Файлові поля спочатку були розроблені як спосіб ((завантаження)) файлів з комп'ютера користувача через форму. У сучасних браузерах вони також надають можливість читати такі файли з програм на JavaScript. Поле виконує роль своєрідного воротаря. Скрипт не може просто почати читати приватні файли з комп'ютера користувача, але якщо користувач вибирає файл у такому полі, браузер інтерпретує цю дію як те, що скрипт може прочитати файл.
Зазвичай поле файлу виглядає як кнопка з написом на кшталт «вибрати файл» або «переглянути», з інформацією про вибраний файл поруч з нею.
<input type=«file»>
<script>
let input = document.querySelector(«input»);
input.addEventListener(«change», () => {
if (input.files.length > 0) {
let file = input.files[0];
console.log(«Ви вибрали», file.name);
if (file.type) console.log(«Він має тип», file.type);
}
});
</script>
{{index «multiple attribute», «files property»}}
Властивість files
елемента ((файлового поля)) - це об'єкт типу масив (знову ж таки, не реальний масив), що містить файли, вибрані у полі. Початково вона є порожньою. Причиною відсутності простої властивості file
є те, що поля файлів також підтримують атрибут multiple
, який дозволяє вибрати декілька файлів одночасно.
{{індекс «Тип файлу»}}
Об'єкти у files
мають такі властивості, як name
(ім'я файлу), size
(розмір файлу у байтах, які є фрагментами по 8 біт) і type
(тип медіафайлу, наприклад, text/plain
або image/jpeg
).
{{index [«асинхронне програмування», «читання файлів», «читання файлів», «клас FileReader»}}
{{id filereader}}
Чого він не має, так це властивості, яка містить вміст файлу. Отримання цієї властивості є дещо складнішим завданням. Оскільки читання файлу з диска може зайняти деякий час, інтерфейс є асинхронним, щоб уникнути зависання вікна.
<input type=«file» multiple>
<script>
let input = document.querySelector(«input»);
input.addEventListener(«change», () => {})
for (let file of Array.from(input.files)) {
let reader = new FileReader();
reader.addEventListener(«load», () => {
console.log(«File», file.name, «starts with»,
reader.result.slice(0, 20));
});
reader.readAsText(file);
}
});
</script>
{{index «клас FileReader», «подія load», «метод readAsText», «властивість result»}}
Читання файлу здійснюється шляхом створення об'єкта FileReader
, реєстрації для нього обробника події load
і виклику його методу readAsText
, передаючи йому файл, який ми хочемо прочитати. Після завершення завантаження властивість result
читача містить вміст файлу.
{{index «error event», «FileReader class», «Promise class»}}
FileReader
також згенерує подію error
, коли читання файлу з будь-якої причини завершиться невдачею. Сам об'єкт помилки буде поміщено у властивість error
зчитувача. Цей інтерфейс було розроблено до того, як обіцянки стали частиною мови. Ви можете загорнути його в обіцянку на зразок цього:
function readFileText(file) {
return new Promise((resolve, reject) => {
let reader = new FileReader();
reader.addEventListener(
«load», () => resolve(reader.result));
reader.addEventListener(
«error», () => reject(reader.error));
reader.readAsText(file);
});
}
{{index «web application»}}
Прості ((HTML)) сторінки з невеликою кількістю JavaScript можуть бути чудовим форматом для «((міні-додатків))» - невеликих допоміжних програм, які автоматизують основні завдання. Поєднавши кілька форм ((полів)) з обробниками подій, ви можете робити що завгодно - від переведення сантиметрів у дюйми до обчислення паролів за головним паролем та назвою веб-сайту.
{{персистентність індексів, [прив'язка, «як стан»], [браузер, сховище]}}
Коли такому додатку потрібно запам'ятати щось між сеансами, ви не можете використовувати прив'язки JavaScript - вони викидаються щоразу, коли сторінка закривається. Ви можете створити сервер, підключити його до інтернету і дозволити вашому додатку зберігати щось там (ми розглянемо, як це зробити у Розділ ?). Але це багато додаткової роботи і складності. Іноді достатньо просто зберігати дані у ((браузері)).
{{index «об'єкт localStorage», «метод setItem», «метод getItem», «метод removeItem»}}
Об'єкт localStorage
можна використовувати для зберігання даних у спосіб, який переживе ((перезавантаження сторінки)). Цей об'єкт дозволяє зберігати рядкові значення під іменами.
localStorage.setItem(«ім'я користувача», «marijn»);
console.log(localStorage.getItem(«username»));
// → marijn
localStorage.removeItem(«username»);
{{index «localStorage object»}}
Значення у localStorage
зберігається доти, доки його не буде перезаписано або видалено за допомогою removeItem
, або доки користувач не очистить свої локальні дані.
{{безпека індексації}}
Сайти з різних ((доменів)) отримують різні сховища. Це означає, що дані, збережені у localStorage
певним сайтом, у принципі, можуть бути прочитані (і перезаписані) лише скриптами цього ж сайту.
{{index «localStorage object»}}
Браузери накладають обмеження на розмір даних, які сайт може зберігати у localStorage
. Це обмеження, а також той факт, що заповнення людських ((жорстких дисків)) непотребом не є вигідним, запобігає тому, щоб ця функція з'їдала надто багато місця.
{{index «localStorage object», «note-taking example», «select (HTML-тег)», «button (HTML-тег)», «textarea (HTML-тег)»}}
Наступний код реалізує простий додаток для ведення нотаток. Він зберігає набір іменованих нотаток і дозволяє користувачеві редагувати нотатки та створювати нові.
Нотатки: <select></select> <button>Додати</button><br>
<textarea style=«width: 100%»></textarea
<скрипт
let list = document.querySelector(«select»);
let note = document.querySelector(«textarea»);
нехай state;
function setState(newState) {
list.textContent = «»;
for (let name of Object.keys(newState.notes)) {
let option = document.createElement(«option»);
option.textContent = name;
if (newState.selected == name) option.selected = true;
list.appendChild(option);
}
note.value = newState.notes[newState.selected];
localStorage.setItem(«Notes», JSON.stringify(newState));
state = newState;
}
setState(JSON.parse(localStorage.getItem(«Notes»)) ?? {
notes: { «список покупок»: «Морква\nРодзинки"},
вибрано: «список покупок»
});
list.addEventListener(«change», () => {})
setState({notes: state.notes, selected: list.value});
});
note.addEventListener(«change», () => {
нехай {selected} = state;
setState({
notes: {...state.notes, [selected]: note.value},
вибране
});
});
document.querySelector(«button»)
.addEventListener(«click», () => {
let name = prompt(«Назва нотатки»);
if (name) setState({
notes: {...state.notes, [name]: «"},
selected: name
});
});
</script>
{{index «метод getItem», JSON, «?? оператор», «значення за замовчуванням»}}
Скрипт отримує початковий стан зі значення «Notes»
, що зберігається в localStorage
, або, якщо воно відсутнє, створює приклад стану, який містить лише список покупок. Читання неіснуючого поля з localStorage
призведе до отримання значення null
. Передача null
до JSON.parse
змусить його розібрати рядок «null»
і повернути null
. Таким чином, оператор ??
можна використовувати для надання значення за замовчуванням у подібних ситуаціях.
Метод setState
переконується, що DOM відображає заданий стан, і зберігає новий стан у localStorage
. Обробники подій викликають цю функцію для переходу до нового стану.
{{індекс [об'єкт, створення], властивість, «обчислена властивість»}}
Синтаксис ...
у прикладі використовується для створення нового об'єкта, який є клоном старого state.notes
, але з додаванням або перезаписом однієї властивості. У ньому використовується синтаксис ((дужки)), щоб спочатку додати властивості зі старого об'єкта, а потім встановити нову властивість. Позначення ((квадратні дужки)) в об'єктному літералі використовується для створення властивості, назва якої базується на деякому динамічному значенні.
{{index «sessionStorage object», [browser, storage]}}
Існує ще один об'єкт, схожий на localStorage
, який називається sessionStorage
. Різниця між ними полягає в тому, що вміст sessionStorage
забувається в кінці кожного ((сеансу)), що для більшості браузерів означає закриття браузера.
У цій главі ми обговорили, як працює протокол HTTP. Клієнт_ надсилає запит, який містить метод (зазвичай GET
) і шлях, який ідентифікує ресурс. Потім сервер вирішує, що робити із запитом, і відповідає кодом статусу і тілом відповіді. І запити, і відповіді можуть містити заголовки, які надають додаткову інформацію.
Інтерфейс, через який JavaScript браузера може робити HTTP-запити, називається fetch
. Запит виглядає наступним чином:
fetch(«/18_http.html»).then(r => r.text()).then(text => {
console.log(`Сторінка починається з ${text.slice(0, 15)}`);
});
Браузери роблять GET
-запити, щоб отримати ресурси, необхідні для відображення веб-сторінки. Сторінка також може містити форми, які дозволяють надсилати інформацію, введену користувачем, як запит на відкриття нової сторінки після заповнення форми.
HTML може представляти різні типи полів форм, такі як текстові поля, прапорці, поля з множинним вибором і перемикачі для вибору файлів. Такі поля можна переглядати та маніпулювати ними за допомогою JavaScript. Вони викликають подію «change» при зміні, викликають подію «input»
при введенні тексту і отримують події клавіатури, коли вони мають фокус клавіатури. Такі властивості, як value
(для текстових полів і полів вибору) або checked
(для прапорців і перемикачів) використовуються для читання або встановлення вмісту поля.
Коли форма надсилається, для неї виконується подія «submit»
. Обробник JavaScript може викликати preventDefault
для цієї події, щоб вимкнути поведінку браузера за замовчуванням. Елементи полів форми також можуть знаходитися поза тегом форми.
Коли користувач вибрав файл зі своєї локальної файлової системи у полі вибору файлу, інтерфейс FileReader
може бути використаний для доступу до вмісту цього файлу з програми JavaScript.
Об'єкти localStorage
і essionStorage
можуть бути використані для збереження інформації таким чином, щоб пережити перезавантаження сторінки. Перший об'єкт зберігає дані назавжди (або поки користувач не вирішить їх очистити), а другий зберігає їх до закриття браузера.
{{індекс «Прийняти заголовок», «тип медіа», «формат документа», «узгодження вмісту (вправа)»}}
Одна з можливостей HTTP називається узгодження вмісту. Заголовок запиту Accept
використовується для того, щоб повідомити серверу, який тип документа бажає отримати клієнт. Багато серверів ігнорують цей заголовок, але коли сервер знає про різні способи кодування ресурсу, він може подивитися на цей заголовок і відправити той, якому віддає перевагу клієнт.
{{index «MIME type»}}
URL-адресу https://eloquentjavascript.net/author налаштовано на відповідь у вигляді звичайного тексту, HTML або JSON, залежно від того, що запитує клієнт. Ці формати ідентифікуються за допомогою стандартних ((тип медіа))s text/plain
, text/html
та application/json
.
{{index «property headers», «fetch function»}}
Надсилайте запити для отримання всіх трьох форматів цього ресурсу. Використовуйте властивість headers
в об'єкті options, переданому функції fetch
, щоб встановити заголовок з назвою Accept
на потрібний тип медіа.
Нарешті, спробуйте запитати тип медіа application/ rainbows+unicorns
і подивіться, який код статусу буде видано.
{{якщо інтерактивний
// Ваш код тут.
if}}
{{hint
{{index «узгодження контенту (вправа)»}}
Базуйте свій код на прикладах fetch
[раніше у розділі] (http#fetch).
{{index «406 (код стану HTTP)», «Прийняти заголовок»}}
Запит несправжнього типу носія поверне відповідь з кодом 406, «Неприйнятно», який сервер має повертати, коли він не може виконати заголовок Accept
.
підказка}}
{{index «JavaScript console», «workbench (exercise)»}}
Створіть інтерфейс, який дозволяє користувачам вводити та виконувати фрагменти JavaScript коду.
{{index «textarea (HTML-тег)», «button (HTML-тег)», «конструктор функцій», «повідомлення про помилку»}}
Помістіть кнопку поруч з полем <textarea>
, яка при натисканні використовує конструктор Function
, який ми бачили в Розділ ?, щоб обернути текст у функцію і викликати її. Перетворіть значення, що повертається функцією, або будь-яку помилку, яку вона викликає, у рядок і виведіть його під текстовим полем.
{{якщо інтерактивний
<textarea id=«code»>повернути «hi»;</textarea
<button id=«button»>Запустити</button
<pre id=«output»></pre> </pre
<script>
// Ваш код тут.
</script>
if}}
{{hint
{{індекс «подія кліку», «подія наведення миші», «конструктор функцій», «робочий стіл (вправа)»}}
Використовуйте document.querySelector
або document.getElementById
для отримання доступу до елементів, визначених у вашому HTML. Обробник подій «click»
або «mousedown»
на кнопці може отримати властивість value
текстового поля і викликати Function
на ньому.
{{index «try keyword», «обробка виключень»}}
Переконайтеся, що ви обернули виклик Function
і виклик її результату в блок try
, щоб ви могли перехоплювати виключення, які вона генерує. У цьому випадку ми дійсно не знаємо, який тип виключення ми шукаємо, тому перехоплюйте все.
{{index «textContent property», output, text, «createTextNode method», «newline character»}}
Властивість textContent
елемента output можна використовувати для заповнення його рядковим повідомленням. Або, якщо ви хочете зберегти старий вміст, створіть новий текстовий вузол за допомогою document.createTextNode
і додайте його до елемента. Не забудьте додати символ переходу на новий рядок у кінці, щоб не виводити всі дані в одному рядку.
підказка}}
{{індекс «гра життя (вправа)», «штучне життя», «Гра життя Конвея»}}
Гра життя Конвея - це проста ((симуляція)), яка створює штучне «життя» на ((сітці)), кожна клітинка якої є або живою, або ні. У кожному ((поколінні)) (ході) застосовуються наступні правила:
-
Будь-яка жива ((клітинка)) з менш ніж двома або більш ніж трьома живими ((сусідами)) вмирає.
-
Будь-яка жива клітина з двома або трьома живими сусідами переходить у наступне покоління.
-
Будь-яка мертва клітина з рівно трьома живими сусідами стає живою клітиною.
Сусідом вважається будь-яка сусідня клітина, включаючи сусідні по діагоналі.
{{index «чиста функція»}}
Зверніть увагу, що ці правила застосовуються до всієї сітки одразу, а не до однієї клітинки за раз. Це означає, що підрахунок сусідів базується на ситуації на початку генерації, і зміни, що відбуваються з сусідніми клітинками протягом цієї генерації, не повинні впливати на новий стан даної клітинки.
{{index «Math.random function»}}
Реалізуйте цю гру, використовуючи будь-яку ((структуру даних)), яку ви вважаєте за потрібне. Використовуйте Math.random
для початкового заповнення сітки випадковим шаблоном. Відобразіть її у вигляді сітки ((прапорець)) ((поле)) з ((кнопка)) поруч, щоб перейти до наступного ((покоління)). Коли користувач встановлює або знімає прапорці, їх зміни слід враховувати при обчисленні наступного покоління.
{{if інтерактивний
<div id=«grid»></div>
<button id=«next»>Наступне покоління</button
<script>
// Ваш код тут.
</script>
if}}
{{hint
{{index «гра життя (вправа)»}}
Щоб вирішити проблему того, щоб зміни концептуально відбувалися одночасно, спробуйте розглядати обчислення ((генерації)) як ((чисту функцію)), яка бере одну ((сітку)) і створює нову сітку, яка представляє наступний хід.
Матрицю можна представити єдиним масивом елементів ширини × висоти, зберігаючи значення рядок за рядком, так, наприклад, третій елемент у п'ятому рядку (з використанням нульової індексації) зберігається у позиції 4 × ширина + 2. Ви можете порахувати живі ((сусідні)) клітинки за допомогою двох вкладених циклів, перебираючи сусідні координати в обох вимірах. Слідкуйте за тим, щоб не рахувати клітинки за межами поля та ігнорувати клітинку в центрі, сусідів якої ми рахуємо.
{{index «event handling», «change event»}}
Переконатися, що зміни до ((checkbox))es набудуть чинності у наступному поколінні, можна двома способами. Обробник подій може помітити ці зміни і оновити поточну сітку, щоб відобразити їх, або ви можете згенерувати нову сітку на основі значень у прапорцях перед обчисленням наступного ходу.
Якщо ви вирішите використовувати обробники подій, ви можете додати ((атрибут))и, які ідентифікують позицію, якій відповідає кожна позначка, щоб було легко дізнатися, яку клітинку потрібно змінити.
{{index drawing, «table (HTML-тег)», «br (HTML-тег)»}}
Для малювання сітки прапорців можна використати елемент <table>
(див. Глава ?) або просто розмістити їх усі в одному елементі, а між рядками поставити елементи <br>
(переведення рядка).
підказка}}