Выбрать главу

HTML

Серверы по общепринятой схеме в случае запроса пути, соответствующего директории, отдают файл под именем index.html из этой директории. Модуль файлового сервера ecstatic поддерживает это соглашение. При запросе пути / сервер ищет файл ./public/index.html (где ./public – это корневая директория) и возвращает его, если он там есть.

Значит, если надо показать страницу, когда браузер будет запрашивать наш сервер, её надо положить в public/index.html. Вот начало файла index:

<!doctype html>

<title>Обмен опытом</title>

<link rel="stylesheet" href="skillsharing.css">

<h1>Обмен опытом</h1>

<p>Ваше имя: <input type="text" id="name"></p>

<div id="talks"></div>

Определяется заголовок и включается таблица стилей, где определяются стили – в числе прочего, рамочка вокруг тем. Затем добавлен заголовок и поле name. Пользователь должен вписать своё имя, чтобы оно было присоединено к его темам и комментариям.

Элемент <div> с ID “talks” будет содержать список тем. Скрипт заполняет список, когда он получает его с сервера.

Затем идёт форма для создания новой темы.

<form id="newtalk">

  <h3>Submit a talk</h3>

  Заголовок: <input type="text" style="width: 40em" name="title">

  <br>

  Summary: <input type="text" style="width: 40em" name="summary">

  <button type="submit">Отправить </button>

</form>

Скрипт добавит обработчик события “submit” в форму, из которого он сможет сделать HTTP-запрос, сообщающий серверу про тему.

Затем идёт загадочный блок, у которого стиль display установлен в none, и который поэтому не виден на странице. Догадаетесь, зачем он нужен?

<div id="template" style="display: none">

  <div>

    <h2>{{title}}</h2>

    <div>by <span>{{presenter}}</span></div>

    <p>{{summary}}</p>

    <div></div>

    <form>

      <input type="text" name="comment">

      <button type="submit">Добавить комментарий</button>

      <button type="button">Удалить тему</button>

    </form>

  </div>

  <div>

    <span>{{author}}</span>: {{message}}

  </div>

</div>

Создание сложных структур DOM через JavaScript приводит к уродливому коду. Можно сделать его покрасивее при помощи вспомогательных функций типа elt из главы 13, но результат всё равно будет выглядеть хуже, чем HTML, который в каком-то смысле является языком для построения DOM-структур.

Для создания DOM-структур для тем обсуждений, наша программа определит простую систему шаблонов, которая использует скрытые структуры, включаемые в документ, для создания новых структур – заменяя метки в файле между двойными фигурными кавычками на значения для конкретной темы.

И наконец, HTML включает файл скрипта, содержащего клиентский код.

<script src="skillsharing_client.js"></script>

Запуск

Первое, что клиент должен сделать при загрузке страницы, это запросить с сервера текущий набор тем. Так как мы будем делать много HTTP-запросов, мы определим небольшую обёртку вокруг XMLHttpRequest, которая примет объект для настройки запроса и обратного вызова по окончанию запроса.

function request(options, callback) {

  var req = new XMLHttpRequest();

  req.open(options.method || "GET", options.pathname, true);

  req.addEventListener("load", function() {

    if (req.status < 400)

      callback(null, req.responseText);

    else

      callback(new Error("Request failed: " + req.statusText));

  });

  req.addEventListener("error", function() {

    callback(new Error("Network error"));

  });

  req.send(options.body || null);

}

Начальный запрос показывает полученные темы на экране и начинает процесс длинных запросов, вызывая waitForChanges.

var lastServerTime = 0;

request({pathname: "talks"}, function(error, response) {

  if (error) {

    reportError(error);

  } else {

    response = JSON.parse(response);

    displayTalks(response.talks);

    lastServerTime = response.serverTime;

    waitForChanges();

  }

});

Переменная lastServerTime используется для отслеживания времени последнего обновления, полученного с сервера. После начального запроса, вид тем у клиента соответствует виду тем сервера, которые был у него в момент запроса. Таким образом, свойство serverTime, включаемое в ответ, предоставляет правильное начальное значение lastServerTime.