Глава 18
Форми и полета на форми

Аз ще трябва този същи ден, на докторския празник,
своите задължения за услугата, надлежно да платя.
Но едно нещо! В интерес на застраховка, моля,
дай ми ред или два поне.”

Mephistopheles, in Goethe's Faust

Формите бяха показани за кратко в предишната глава, като начин за представяне на информация, предоставена от потребителя през HTTP. Те са били предназначени за предварителен JavaScript Web, като се предполага, че взаимодействието със сървъра винаги се случва, когато навигираме към новата страница.

Но техните елементи са част от DOM, както и останалата част от страницата и елементите на DOM, които представляват полета на форми и подкрепят редица свойства и събития, които не присъстват в други елементи. Това прави възможно да се инспектират и контролират такива полета за въвеждане с програми на JavaScript и правят неща , като например добавяне на функционалност към традиционната форма или като се използват такива формуляри и полета за градивни елементи в JavaScript приложения.

Полета (fields)

Уеб формата се състои от произволен брой полета за въвеждане групирани във <form> таг. HTML позволява няколко различни стилове на полета, вариращи от прости on/off отметки (checkbox) до падащи менюта (drop-down) и полета за въвеждане (input) на текст. В тази книга няма да се опитваме да разгледаме обстойно всички видове полета, но ще започнем с един общ преглед.

Много видове полета използват <input> таг. Този маркер използва type атрибут за определяне на стила на полето. Това са само някои често използвани видове <input> :

text Едноредово текстово поле
password Същото като text, но скрива текста, който е написан
checkbox on/off прекъсвач
radio Част от поле за множествен избор
file Позволява на потребителя да избере файл от компютъра си

Форма полетата не трябва непременно да са във <form> таг. Можем да ги поставим навсякъде в една страница. Такива полета не могат да бъдат предоставени (само form, като цяло може), но в отговор на въвеждане с JavaScript, ние често не искаме да представим нашите полета нормално, все пак.

<p><input type="text" value="abc"> (text)</p>
<p><input type="password" value="abc"> (password)</p>
<p><input type="checkbox" checked> (checkbox)</p>
<p><input type="radio" value="A" name="choice">
   <input type="radio" value="B" name="choice" checked>
   <input type="radio" value="C" name="choice"> (radio)</p>
<p><input type="file"> (file)</p>

Интерфейса на JavaScript за такива елементи се различава с типа на елемента. Ще разгледаме всеки един от тях по-късно в тази глава.

Многоредовите текстови полета имат собствен таг <textarea>, най-вече защото използването на атрибут за определяне на начална многоредова стойност би било неудобно. <textarea> изисква затварящ таг </textarea> и използва текста между тези два тага, вместо да използва своя value атрибут, като изходен текст.

<textarea>
one
two
three
</textarea>

И накрая <select> тага се използва за създаване на поле, което позволява на потребителя да избере между няколко предварително дефинирани опции.

<select>
  <option>Pancakes</option>
  <option>Pudding</option>
  <option>Ice cream</option>
</select>

Всеки път, когато стойността на формата на полето се промени, се създава ефект на "change" събитие.

Фокус

За разлика от повечето елементи в един HTML документ, полето на формата може да получи фокуса на клавиатурата. При кликване или активиране по някакъв друг начин, по който елемента става активен в момента, той е основният получател на въвеждането от клавиатурата.

Ако даден документ има текстово поле, там може да се въведе текст, само когато полето е фокусирано. Другите полета реагират по различен начин на събития с клавиатурата. Например <select> менюто се опитва да премине към вариант, който съдържа текста на потребителя и отговаря на клавишите със стрелките, като движим избора на горе или на долу.

Можем да контролираме фокуса на 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>

За някои страници потребителя очаква да взаимодейства с поле на формуляр веднага. JavaScript може да се използва за фокусиране на тази област, когато е зареден документа, но HTML също осигурява autofocus атрибут, който произвежда същия ефект, като позволява на браузъра да знае, какво се опитваме да постигнем. Това дава възможност на браузъра да забрани поведението, когато това е необходимо, като например, когато потребителя фокусира нещо друго.

<input type="text" autofocus>

Браузъра също така позволява на потребителя да мести фокуса през документа чрез натискане на клавиша Tab. Ние можем да влияем на реда по-който елементите получават фокус с атрибута tabindex. Следният пример на документ ще позволи на фокуса да скача от въвеждането на текст към бутона OK, вместо да минава през връзката на помощния линк.

<input type="text" tabindex=1> <a href=".">(help)</a>
<button onclick="console.log('ok')" tabindex=2>OK</button>

По подразбиране повечето видове HTML елементи не могат да бъдат фокусирани. Но можем да добавим tabindex атрибут на всеки елемент, което ще го направи фокусируем.

Забраняване на полета

Всички полета на формите могат да бъдат деактивирани чрез disabled атрибута, който съществува, като свойство на елементите в DOM обекта.

<button>I'm all right</button>
<button disabled>I'm out</button>

Деактивираните полета не могат да бъдат фокусирани или променяни и за разлика от активните области, те обикновено изглеждат сиви и бледи.

Когато една програма е в процес на обработка на действие, причинено от някой бутон или друго управление, което може да изисква комуникация със сървъра и по този начин отнема известно време, е може би добра идея да се забрани контрола, докато приключи действието. По този начин, когато потребителя нетърпеливо щрака отново, елементите не отговарят на неговото действие.

Формата като цяло

Когато едно поле се съдържа във <form> елемент, неговия DOM елемент ще има свойство form свързващо обратно формата към DOM елемента. <form> елемента от своя страна има свойство наречено elements, което съдържа масиво-подобна колекция от полета вътре в него.

В name атрибута на формуляра се определя начина, по който неговата стойност да бъде идентифицирана, когато се подава формата. Той може да се използва, като име на свойство за достъп до свойствата на елементите на формата, която действа едновременно, като масиво-подобен обект (достъпен с номера) и карта (достъпна с имена).

<form action="example/submit.html">
  Name: <input type="text" name="name"><br>
  Password: <input type="password" name="password"><br>
  <button type="submit">Log in</button>
</form>
<script>
  var form = document.querySelector("form");
  console.log(form.elements[1].type);
  // → password
  console.log(form.elements.password.type);
  // → password
  console.log(form.elements.name.form == form);
  // → true
</script>

Бутон с type атрибут submit, когато е натиснат предизвиква формата да бъде изпратена. Натискането на Enter, когато полето на формуляра е фокусирано има същия ефект.

Изпращането на формуляр обикновено означава, че браузърът навигира към страницата, посочена от action атрибута на формата или с помоща на GET или POST искане. Но преди това да се случи, "submit" събитието е изстреляно. Това събитие може да се обработва от JavaScript и манипулатора може да предотврати поведението по подразбиране, като извикаме preventDefault върху обекта на събитието.

<form action="example/submit.html">
  Value: <input type="text" name="value">
  <button type="submit">Save</button>
</form>
<script>
  var form = document.querySelector("form");
  form.addEventListener("submit", function(event) {
    console.log("Saving value", form.elements.value.value);
    event.preventDefault();
  });
</script>

Прихващането на "submit" събитие в JavaScript има различни приложения. Можете да напишете код, който да провери дали въведените стойности от потребителя имат смисъл и ако нямат веднага да покаже съобщение за грешка, вместо да изпраща формуляра. Или можете да забраните нормалния начин за изпращане на формуляра изцяло, както в предишния пример и ако вашата програма има обработка на входа, като евентуално използвате XMLHttpRequest, да го изпратите на сървъра без презареждане на страницата.

Текстови полета

Полетата създаени от <input> тагове с type = text или password, както и textarea таговете, споделят общ интерфейс. Техните DOM елементи имат value свойство, което държи текущото им съдържание, като стойност на string. Променянето на това свойство с друг string променя съдържанието на полето.

Свойствата selectionStart и selectionEnd на текстовите полета ни дават информация за курсора и селекцията в текста. Когато нищо не е избрано, тези две свойства притежават същия номер, който показва позицията на курсора. Например, 0 показва началото на текста, а 10 показва, че курсора е след 10-ия характер. Когато е избрана част от полето, двете свойства ще се различават, което ни дава началото и края на избрания текст. Подобно на value тези свойства също могат да бъдат презаписвани.

Като пример си представете, че пишете статия за Khasekhemwy но имате някои проблеми с правописа на името му. Следният код вмъква в <textarea> тага манипулатор за събитие и когато натиснете F2 вмъква string “Khasekhemwy” вместо вас.

<textarea></textarea>
<script>
  var textarea = document.querySelector("textarea");
  textarea.addEventListener("keydown", function(event) {
    // The key code for F2 happens to be 113
    if (event.keyCode == 113) {
      replaceSelection(textarea, "Khasekhemwy");
      event.preventDefault();
    }
  });
  function replaceSelection(field, word) {
    var from = field.selectionStart, to = field.selectionEnd;
    field.value = field.value.slice(0, from) + word +
                  field.value.slice(to);
    // Put the cursor after the word
    field.selectionStart = field.selectionEnd =
      from + word.length;
  }
</script>

Функцията replaceSelection замества избраната в момента част от съдържанието на текстовото поле с дадената дума и след това премества курсора след тази дума, така че потребителя да може да продължи да пише.

В случай на "change" събитие, текстовото поле не се активира всеки път, когато нещо е въведено. Вместо това, то се активира, когато областта губи фокус, след като неговото съдържание е променено. Вместо това, за да се реагира незабавно на промени в текстовото поле, можем да регистрираме манипулатор за събитие "input", който реагира всеки път, когато потребителя въведе характер, изтрива текст или по друг начин манипулира съдържанието на полето.

Следващият пример показва текстово поле и брояч показващ текущата дължина на въведения текст.

<input type="text"> length: <span id="length">0</span>
<script>
  var text = document.querySelector("input");
  var output = document.querySelector("#length");
  text.addEventListener("input", function() {
    output.textContent = text.value.length;
  });
</script>

Чек-боксове и радио бутони

Полето на чек-бокса е прост бинарен превключвател. Стойността му може да бъде извлечена или променена чрез неговото checked свойство, което притежава Булева стойност.

<input type="checkbox" id="purple">
<label for="purple">Make this page purple</label>
<script>
  var checkbox = document.querySelector("#purple");
  checkbox.addEventListener("change", function() {
    document.body.style.background =
      checkbox.checked ? "mediumpurple" : "";
  });
</script>

Тага <label> се използва за асоцииране на част от текст с поле за въвеждане. Неговия for атрибут трябва да се отнася към id на полето. Кликването върху етикета ще активира областа, която се фокусира или превключва стойността си, когато това е чек-бокс или радио бутон.

Радио бутона е подобен на чек-бокса, но е косвено свързан с други радио бутони със същия name атрибут, така че само един от тях може да бъде активен по едно и също време.

Color:
<input type="radio" name="color" value="mediumpurple"> Purple
<input type="radio" name="color" value="lightgreen"> Green
<input type="radio" name="color" value="lightblue"> Blue
<script>
  var buttons = document.getElementsByName("color");
  function setColor(event) {
    document.body.style.background = event.target.value;
  }
  for (var i = 0; i < buttons.length; i++)
    buttons[i].addEventListener("change", setColor);
</script>

Метода document.getElementsByName ни дава всички елементи с даден name атрибут. Примерът минава с цикъл над тези бутони (с редовен for цикъл, а не с forEach, защото връща колекция, а не реален масив) и регистрира манипулатор за събитие за всеки елемент. Не забравяйте, че обекта на събитието има target свойство, отнасящо се до елемента, който предизвиква събитието. Това често е полезно при манипулатори, като този, когато се извикват върху различни елементи и се нуждаят от начин за достъп до текущата цел.

Селектиране на полета

Изборът на полета е концептуално подобен на радио бутоните, което позволява на потребителя да избере от набор от опции. Но докато един радио бутон поставя оформлението на опциите под наш контрол, появата на <select> тага се определя от браузъра.

Селектирането на полета също има вариант, който е по-близко до списък с чек-боксове, отколкото с радио бутони. Когато се даде multiple атрибут, <select> тага ще позволи на потребителя да избира произволен брой опции, а не само една опция.

<select multiple>
  <option>Pancakes</option>
  <option>Pudding</option>
  <option>Ice cream</option>
</select>

В повечето браузъри това поле за избор се появява по различен начин, като обикновено се представя, като drop-down контрол, който показва опциите едва, когато го отворите.

Атрибута size на <select> тага се използва за задаване на броя на опциите, които са видими по едно и също време, което ни дава контрол върху външния вид на падащото меню. Например, определянето на атрибута size с "3" ще направи видимо поле с три линии, независимо дали опцията multiple е активирана или не.

Всеки <option> таг има стойност. Тази стойност може да се определи с value атрибут, но ако той не е зададен, текста вътре в опцията се брои за такава стойност на опцията. Свойството value на елемента <select> отразява текущо избраната опция. За поле направено с multiple, това свойство не означава много, тъй като ще даде стойността на само една от опциите избрани в момента.

<option> таговете на полето <select> могат да бъдат достъпни, като масиво-подобен обект, чрез полето на options свойството. Всяка опция има свойство selected, което показва дали тази опция е избрана в момента. С това свойство може да се запише, избере или откаже опция.

Следващият пример извлича избрани стойности от селектирано с multiple поле и ги използва за да композира бинарно число от отделни битове. Задръжте Ctrl (или Command на Mac) за да изберете няколко опции.

<select multiple>
  <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>
<script>
  var select = document.querySelector("select");
  var output = document.querySelector("#output");
  select.addEventListener("change", function() {
    var number = 0;
    for (var i = 0; i < select.options.length; i++) {
      var option = select.options[i];
      if (option.selected)
        number += Number(option.value);
    }
    output.textContent = number;
  });
</script>

Файл полета

Файл полетата първоначално са били предназначени, като начин за качване на файлове от машината на браузъра чрез форма. В съвременните браузъри те също така предоставят начин за четене на такива файлове от програми на JavaScript. Полето действа, като начин за контролиран достъп. Скрипта не може просто да започне да чете лични файлове от компютъра на потребителя, но ако потребителя избере файл в такова поле, браузърът интерпретира това действие, като знак, че скрипта може да прочете файла.

Файл полето обикновено прилича на бутон маркиран с нещо, като “изберете файл” или “преглед” с информацията за избрания файл в непосредствена близост до него.

<input type="file">
<script>
  var input = document.querySelector("input");
  input.addEventListener("change", function() {
    if (input.files.length > 0) {
      var file = input.files[0];
      console.log("You chose", file.name);
      if (file.type)
        console.log("It has type", file.type);
    }
  });
</script>

Свойството files на елемента на файл полето е масиво-подобен обект (отново нереален масив), съдържащ файлове избрани в полето. То първоначално е празно. Причината, че не е просто едно файл свойство, е че файловите полета също поддържат multiple атрибут, който дава възможност за избор на множество файлове едновременно.

Обектите в свойството files имат свойства, като например name (името на файла), size (размер на файла в битове) и type (за типа носител на файла, като text/plain или image/jpeg).

Какво не трябва да съдържа свойството, което съдържа съдържанието на файла. Това е получаване на малко повече достъп. Четене на файл от диск може да отнеме време и за това интерфейса трябва да бъде асинхронен за да се избегне замръзване на документа. Можем да мислим за FileReader конструктора, като подобен на XMLHttpRequest, но за файлове.

<input type="file" multiple>
<script>
  var input = document.querySelector("input");
  input.addEventListener("change", function() {
    Array.prototype.forEach.call(input.files, function(file) {
      var reader = new FileReader();
      reader.addEventListener("load", function() {
        console.log("File", file.name, "starts with",
                    reader.result.slice(0, 20));
      });
      reader.readAsText(file);
    });
  });
</script>

Четенето на файл се извършва чрез създаване на FileReader обект, регистрирайки "load" за обработка на събитие и извиквайки своя readAsText метод давайки му файла, който искаме да прочете. След приключване на зареждането, свойството result на reader съдържа съдържанието на файла.

Примерът използва Array.prototype.forEach за обхождане на масива, тъй като нормален цикъл би бил неудобен за получаване на правилните file и reader обекти от манипулатора на събитието. Променливите ще бъдат споделени от всички повторения на цикъла.

FileReader също има ефект за "error" събитие, ако четенето на файла не успее по някаква причина. Самия обект за грешка ще попадне в свойството error на reader. Ако не искаме да си спомняме подробности от някоя несъвместимост с асинхронния интерфейс, можем да го увием в Promise (виж Глава 17) по подобен начин:

function readFile(file) {
  return new Promise(function(succeed, fail) {
    var reader = new FileReader();
    reader.addEventListener("load", function() {
      succeed(reader.result);
    });
    reader.addEventListener("error", function() {
      fail(reader.error);
    });
    reader.readAsText(file);
  });
}

Възможно е да се чете само част от файл чрез извикване на slice метода върху него и подаване на result (така наречения blob обект) към файла reader.

Съхраняване на данни от страна на клиента

Обикновени HTML страници с малко JavaScript могат да бъдат чудесна среда за “мини приложения” - малки помощни програми, които автоматизират ежедневни неща. Чрез свързване на няколко полета на формуляр с манипулатори на събития, можем да направим нещо от преобразуване на градуси по Целзий и Фаренхайт до изчисляване на пароли от главна парола и име на уеб сайт.

Когато такова приложение трябва да си спомни нещо между сесиите, можем да използваме JavaScript променливи преди те да се изхвърлят всеки път, когато страницата се затвори. Можем да настроим сървъра, като го свържем с Интернет и нашето приложение да складира нещо там. Ще видим как се прави това в Глава 20. Но това добавя много допълнителна работа и сложност. Понякога е достатъчно просто да съхраняваме данните в браузъра. Но как?

Можем да съхраняваме string данни по начин, който оцелява при презареждане на страницата, като ги поставим в localStorage обекта. Този обект ни позволява да подадем string стойности по имена (също като strings), както в този пример:

localStorage.setItem("username", "marijn");
console.log(localStorage.getItem("username"));
// → marijn
localStorage.removeItem("username");

Стойности в localStorage остават докато не се променят, те се отстраняват с removeItem или когато потребителя изчисти своите локални данни.

Сайтове от различни домейни получават различни отделения за съхранение. Това означава, че данните съхранявани в localStorage от даден уеб сайт могат по принцип да се четат (или презаписват) само чрез скриптове на същия този сайт.

Браузърите също налагат ограничения върху размера на данните на сайта, които могат да се съхраняват в localStorage обикновено от порядъка на няколко мегабайта. Това ограничение заедно с факта, че запълването на твърдите дискове на хората с боклуци не е изгодно, предотвратява тази функция от изяждане на твърде много място.

Следващия код реализира приложение за просто водене на бележки. То запазва бележките на потребителя, като обект, асоциирайки заглавията със string съдържание. Тази задача се кодира, като JSON и се съхранява в localStorage. Потребителя може да избере една бележка от <select> полето и промени текста на бележката в <textarea>. Бележката може да се добави чрез натискане на бутона.

Notes: <select id="list"></select>
<button onclick="addNote()">new</button><br>
<textarea id="currentnote" style="width: 100%; height: 10em">
</textarea>

<script>
  var list = document.querySelector("#list");
  function addToList(name) {
    var option = document.createElement("option");
    option.textContent = name;
    list.appendChild(option);
  }

  // Initialize the list from localStorage
  var notes = JSON.parse(localStorage.getItem("notes")) ||
              {"shopping list": ""};
  for (var name in notes)
    if (notes.hasOwnProperty(name))
      addToList(name);

  function saveToStorage() {
    localStorage.setItem("notes", JSON.stringify(notes));
  }

  var current = document.querySelector("#currentnote");
  current.value = notes[list.value];

  list.addEventListener("change", function() {
    current.value = notes[list.value];
  });
  current.addEventListener("change", function() {
    notes[list.value] = current.value;
    saveToStorage();
  });

  function addNote() {
    var name = prompt("Note name", "");
    if (!name) return;
    if (!notes.hasOwnProperty(name)) {
      notes[name] = "";
      addToList(name);
      saveToStorage();
    }
    list.value = name;
    current.value = notes[name];
  }
</script>

Скрипта инициализира notes променлива към стойността съхранявана в localStorage или ако липсва такава, само прост обект с празен "shopping list" бележка в него. Четене на поле, което не съществува в localStorage ще получи null. Подавайки null към JSON.parse, той ще направи разбор на string "null" и ще върне null. По този начин || оператора може да се използва за предоставяне на стойност по подразбиране в ситуация, като тази.

Всеки път, когато се променят данните в бележката (когато се добавя нова бележка или се променя старата), се извиква функцията saveToStorage за да актуализира полето за съхранение. Ако това приложение е предназначено да обработва хиляди бележки, а не само няколко, това би било твърде скъпо и трябва да излезем с по-сложен начин за съхраняването им, като дадем на всяка бележка собствено поле за съхранение.

Когато потребителя добави нова бележка, кодът трябва да актуализира текстовото поле изрично, въпреки че <select> полето има "change" манипулатор, който прави същото нещо. Това е необходимо, тъй като ефекта на "change" събитията се появява само, когато потребителя промени стойността на полето, а не когато скрипта го прави.

Има и друг обект подобен на localStorage , наречен sessionStorage. Разликата между двата е, че съдържанието на sessionStorage се забравя в края на всяка сесия, което за повечето браузъри означава, когато браузъра се затваря.

Резюме

HTML може да изразява различни видове полета на форми, като например, текстови полета, полета с отметки, множествен избор на полета и файлови колектори.

Такива полета могат да бъдат инспектирани и манипулирани с JavaScript. Те ползват ефекта на "change" събитие за да ги променят, на "input" събитие, когато текста е написан, както и различни видове клавишни събития. Тези събития ни позволяват да забележим, когато потребителя взаимодейства с полетата. Информация, като value (за текст или избор на поле) или checked (за чек-бокс и радио бутони) се използват за четене или настройване на съдържанието на полето.

Когато се изпраща форма се използва ефекта на нейното "submit" събитие. Ако JavaScript манипулатора извика preventDefault върху такова събитие се предотвратява изпащането да се случи. Елементите на полетата на формите не трябва да бъдат опаковани във <form> тагове.

Когато потребителя е избрал файл от тяхната локална файлова система в колекторното поле на файла, интерфейса на FileReader може да се използва за достъп до съдържанието на този файл от програма на JavaScript.

Обектите localStorage и sessionStorage могат да се използват за съхраняване на информация по начин, който оцелява при презареждане на страниците. Първият запазва данните за винаги (или докато потребителя не реши да ги изчисти), а втория ги запазва до затваряне на браузъра.

Упражнения

Работна маса на JavaScript

Изградете интерфейс, който позволява на хората да работят и пускат парчета JavaScript код.

Сложете един бутон в близост до полето на <textarea>, който когато бъде натиснат, използва Function конструктора, който видяхме в Глава 10 за увиване на текста във функция и го извиква. Конвертирайте крайния резултат от изпълнението на функцията или грешката, която се повдига в string, който да се покаже след текстовото поле.

<textarea id="code">return "hi";</textarea>
<button id="button">Run</button>
<pre id="output"></pre>

<script>
  // Your code here.
</script>

Използвайте document.querySelector или document.getElementById да получите достъп до елементите, определени в HTML. С един манипулатор на събитие за "click" или "mousedown" върху бутона, може да получите value свойството на текстовото поле и да извикате new Function върху него.

Уверете се, че сте увили повикването към new Function и повиквнето към резултата в try блок, така че да можете да хванете изключенията, които той произвежда. В този случай ние наистина не знаем какъв тип изключение търсим, така че трябва да хваща всичко.

Свойството textContent на изходния елемент може да се използва, за да се напълни със string съобщение. Или ако искате да запазите старото съдържание, създайте ново текстово разклонение, като използвате document.createTextNode и го добавете към елемента. Не забравяйте да добавите характера за нов ред на края, така че всичко на изхода да не се появява на един ред.

Автоматично довършване

Разширете текстовото поле, така че когато потребителя въвежда, да се покаже списък с предложени стойности, който да се покаже по-долу на полето. Направете набор от възможни стойности на разположение, като трябва да се покажат тези, с които започва текста, който е въведен. Когато едно предложение се натисне, то да замени сегашната стойност в текста на полето.

<input type="text" id="field">
<div id="suggestions" style="cursor: pointer"></div>

<script>
  // Builds up an array with global variable names, like
  // 'alert', 'document', and 'scrollTo'
  var terms = [];
  for (var name in window)
    terms.push(name);

  // Your code here.
</script>

Най-добрия начин за актуализиране на списъка с предложения е "input", тъй като ще се изстреля незабавно при промяна на съдържанието в полето.

После минете с цикъл над масива от термини, за да видите дали е започнат с даден string. Например, може да извикате indexOf и да видите дали резултата е нулев. За всяко съвпадение на string добавете елемент към предложенията <div>. Вероятно също и за празни, така че всеки път, когато стартирате актуализирането на предложенията, например, като му зададете textContent за празен string.

Можете също да добавите манипулатор за "click" събитие на всеки елемент предложение или да добавите един към външния <div> който ги притежава и погледнете в target свойството на събитието, за да разберете кое предложение е било кликнато.

За да получите текста с предложението от едно DOM разклонение, можете да погледнете неговия textContent или определите атрибут за изрично съхраняване на текст, когато създавате елемента.

Игра- Живота на Конуей

Играта живота на Конуей е проста симулация, която създава изкуствен живот в една мрежа от клетки, като всяка от тези клетки е жива или не. На всяко поколение (което включите) се прилагат следните правила:

Един съсед се определя от всяка прилежаща съседна клетка включително и прилежащите съседни по диагонал.

Имайте в предвид, че тези правила се прилагат за цялата мрежа наведнъж, а не на даден квадрат в определен момент. Това означава, че преброяването на съседите се основава на ситуацията в началото на поколението, както и промените, които се случват на съседните клетки по време на това поколение не трябва да влияят на новото състояние на дадена клетка.

Изпълнението на тази игра използвайки структура от данни е поучаващо. Използвайте Math.random за населване на мрежата със случаен модел първоначално. Покажете я, като мрежа от полета с отметки (чек-бокс) с един бутон до нея за да преминете към следващото поколение. Когато потребителя маркира или немаркира квадратчета, техните промени трябва да бъдат включени, когато се изчислява новото поколение.

<div id="grid"></div>
<button id="next">Next generation</button>

<script>
  // Your code here.
</script>

За да се реши проблема с промените концептуално случващи се по едно и също време, опитайте да видите изчисляването на едно поколение, като чиста функция, която взема една клетка и произвежда нова клетка, която представя на следващото завъртане.

Представяне на мрежата може да се направи с всеки от начините показани в Глави 7 и 15. Преброяване на живите съседи може да се направи с два вложени цикъла, минаващи над съседни координати. Внимавайте да не брои клетки извън терена и да не обръща внимание на клетки в центъра, чиито съседи преброяваме.

Извършените промени в checkboxes влизат в сила при следващото поколение и могат да се направят по два начина. Един манипулатор на събитие би могъл да забележи тези промени и да обнови текущата мрежа за да ги отрази или може да генерирате нова мрежа от стойностите в квадратчетата преди изчисляване на следващото завъртане.

Ако изберете манипулатори на събития, трябва да прикачите атрибути, които определят позицията, на която отговаря всяко квадратче, така че лесно да разберете кои клетки се променят.

За да направите мрежа от отметки, можете да използвате <table> елемент (виж Глава 13) или просто да ги поставите в един и същ елемент, като сложите <br> елемента (за нов ред) между редовете.