Куки

Одну из возможностей сохранения данных в JavaScript представляет использование куки. Для работы с куками в объекте document предназначено свойство cookie.

Для установки куков достаточно свойству document.cookie присвоить строку с куками:

document.cookie = "login=tom32;";

В данном случае устанавливается кука, которая называется "login" и которая имеет значение "tom32". И в большинстве браузеров мы можем посмотреть ее, узнать всю информацию о ней и в дальнейшем ее можно использовать в приложении. Строка куки принимает до шести различных параметров: имя куки, значение, срок окончания действия (expires), путь (path), домен (domain) и secure. Выше использовались только два параметра: имя куки и значение. То есть в случае со строкой "login=tom32;" куки имеет имя login и значение tom32.

Но подобная куки имеет очень ограниченный срок жизни: если явным образом не установить срок действия, то кука будет удалена с закрытием браузера. Подобная ситуация, возможно, идеальна для тех случаев, когда необходимо удалять всю информацию после завершения работы с веб-приложением и закрытия браузера. Однако данное поведение не всегда подходит.

- Получение куки

Для простейшего извлечения куки из браузера достаточно обратиться к свойству document.cookie:

var expire = new Date();
expire.setHours(expire.getHours() + 4);

document.cookie = "city=Berlin;expires="+expire.toUTCString()+";";
document.cookie = "country=Germany;expires="+expire.toUTCString()+";";
document.cookie = "login=tom32;";

document.write(document.cookie);

Здесь были установлены три куки, и браузер выведет нам все эти куки.


Web storage

Хотя куки позволяют сохранять информацию, они имеют ряд ограничений. Например, браузер имеет ограничения на размер куков - каждая кука не может превышать 4 кб. Куки имеют срок действия, после которого удаляются. Куки являются неотъемлемой чертой протокола HTTP и при каждом запросе к серверу передаются вместе с запросом на сервер. Однако для работы с куками на стороне клиента в коде JavaScript не имеет значения передача куков на сервер. Кроме того, для извлечения сохраненных куков надо написать некоторую порцию кода.

Поэтому в HTML5 была внедрена новая концепция для хранения данных - web storage. Web storage состоит из двух компонентов: session storage и local storage.

Session storage представляет временное хранилище информации, которая удаляется после закрытия браузера.

Local storage представляет хранилище для данных на постоянной основе. Данные из local storage автоматически не удаляются и не имеют срока действия. Эти данные не передаются на сервер в запросе HTTP. Кроме того, объем local storage составляет в Chrome и Firefox 5 Mб для домена, а в IE - 10 Mб.

Все данные в web storage представляют набор пар ключ-значение. То есть каждый объект имеет уникальное имя-ключ и определенное значение.

Для работы с local storage в JavaScript используется объект localStorage, а для работы с session storage - объект sessionStorage.

Для сохранения данных надо передать в метод setItem() объекта localStorage:

localStorage.setItem("login", "[email protected]");

В этот метод передаются два значения: ключ и значение сохраняемого объекта.

Если в localStorage уже есть объект с ключом "login", то его значение заменяется новым.

Для получения сохраненных данных надо вызвать метод getItem():

var login = localStorage.getItem("login");
console.log(login);  // [email protected]

В этот метод передается ключ объекта.

Чтобы удалить объект, применяется метод removeItem(), который принимает ключ удаляемого объекта:

localStorage.removeItem("login");

И для полного удаления всех объектов из localStorage можно использовать метод clear():

localStorage.clear();

С сохранением простых объектов все просто, однако при этом надо учитывать, что данные в localStorage сохраняются в виде строки:

localStorage.setItem("age", 23);
var age = localStorage.getItem("age");
age=parseInt(age)+10;
document.write(age); //33

Если в данном случае не преобразовать значение к числу с помощью parseInt(), то age будет действовать как строка.

Трудности могут возникнуть с сохранением сложных объектов:

var user ={
    name: "Tom",
    age: 23,
    married: false
};

localStorage.setItem("user", user);
var savedUser = localStorage.getItem("user");
document.write(savedUser); //[object Object]
document.write(savedUser.name); // undefined - savedUser - строка, а не объект

В этом случае нам надо использовать сериализацию в формат JSON:

var user ={
    name: "Tom",
    age: 23,
    married: false
};

localStorage.setItem("user", JSON.stringify(user));
var savedUser = JSON.parse(localStorage.getItem("user"));
document.write(savedUser.name + " " + savedUser.age +" " + savedUser.married); // Tom 23 false

AJAX

Современные веб-приложения, как правило, разделяются на две части: клиент и сервер. Клиент представляет собой веб-страницу с кодом JavaScript. К серверным технологиям относятся PHP, Ruby, Node.js, ASP.NET и т.д., которые получают запрос от клиента, обрабатывают и отправляют в ответ результат обработки.

AJAX представляет технологию для отправки запросов к серверу из клиентского кода JavaScript без перезагрузки страницы. Сам термин расшифровывается как Asynchronous JavaScript And XML. То есть изначально AJAX предполагал асинхронное взаимодействие клиента и сервера посредством данных в формате XML. Хотя сейчас XML во многом вытеснил формат JSON. В любом случае AJAX революционизировал веб-среду, позволив создавать динамичные отзывчивые веб-приложения.

- Объект XMLHttpRequest

Для создания приложений, использующих AJAX, применяются различные способы. Но самым распространенным способом является использование объекта XMLHttpRequest:

var request = new XMLHttpRequest();

После создания объекта XMLHttpRequest можно отправлять запросы к серверу. Но для начала надо вызвать метод open() для инициализации:

request.open("GET", "http://localhost/hello.txt", false);

Метод open() принимает три параметра: тип запроса (GET, POST, HEAD, PUT), адрес запроса и третий необязательный параметр - логическое значение true или false, указывающее, будет ли запрос осуществляться в асинхронном режиме. То есть в данном случае запрос будет иметь тип GET, он будет направляться по адресу "http://localhost/hello.txt в синхронном режиме, так как стоит значение false (для асинхронного режима указывается значение true).

Синхронный и асинхронный режим отличаются тем, что запрос в синхронном режиме пока запрос не выполнится, остальной код JavaScript не может выполняться. По умолчанию, если третий параметр не используется, то запрос отправляется в асинхронном режиме, что позволяет параллельно с выполнением запроса выполнять также и другой код JavaScript. И в большинстве случаев, как правило, используется именно асинхронный режим.

Кроме того, метод open() может принимать еще два параметра: логин и пароль пользователя, если для выполнения запроса нужна аутентификация:

request.open("GET", "http://localhost/home.php", true, "login", "password");

После инициализации запроса методом open() необходимо отправить запрос с помощью метода send():

request.send();

- Свойства XMLHttpRequest

Объект XMLHttpRequest имеет ряд свойств, которые позволяют проконтролировать выполнение запроса:

  • status: содержит статусный код ответа HTTP, который пришел от сервера. С помощью статусного кода можно судить об успешности запроса или об ошибках, которые могли бы возникнуть при его выполнении. Например, статусный код 200 указывает на то, что запрос прошел успешно. Код 403 говорит о необходимости авторизации для выполнения запроса, а код 404 сообщает, что ресурс не найден и так далее.
  • statusText: возвращает текст статуса ответа, например, "200 OK"
  • responseType: возвращает тип ответа. Есть следующие типы:
    ""
    "arraybuffer"
    "blob"
    "document"
    "json"
    "text"
  • response: возвращает ответ сервера
  • responseText: возвращает текст ответа сервера
  • responseXML: возвращает xml, если ответ от сервера в формате xml

Например, выполним запрос к текстовому файлу, который находится на локальном веб-сервере. Для выполнения ajax-запросов потребуется запущенный локальный веб-сервер, на котором будет лежать файл hello.txt, в котором будет содержаться одна строка: "Привет мир".

Код веб-страницы (пусть она называется test.html) будет следующим:

var request = new XMLHttpRequest();
request.open("GET", "/hello.txt", false);
request.send();

var status = request.status;

if(status == 200) {
    document.write("Текст ответа: " + request.responseText)
} else if(status == 404) {
    document.write("Ресурс не найден")
} else {
    document.write(request.statusText)
}

И после загрузки страницы выполнится ajax-запрос к ресурсу /hello.txt. Но важно отметить, что получение статуса сразу после вызова метода request.send() будет работать только для синхронного запроса.

- Асинхронные запросы

Хотя синхронные запросы вполне работают и их можно использовать, но в то же время их рекомендуется избегать. Поскольку нередко запрос может занять продолжительное время, то это может заблокировать выполнение остального кода и работу с html-страницей до окончания выполнения запроса. Поэтому рекомендуется использовать преимущественно асинхронные запросы.

Работа с асинхронными запросами чуть более сложна, чем с синхронными, поскольку нам надо еще обработать событие readystatechange объекта XMLHttpRequest.

При асинхронном запросе объект XMLHttpRequest использует свойство readyState для хранения состояния запроса. Состояние запроса представляет собой число:

  • 0: объект XMLHttpRequest создан, но метод open() еще не был вызван для инициализации объекта
  • 1: метод open() был вызван, но запрос еще не был отправлен методом send()
  • 2: запрос был отправлен, заголовки и статус ответа получены и готовы к использованию
  • 3: ответ получен от сервера
  • 4: выполнение запроса полностью завершено (даже если получен код ошибки, например, 404)

Событие readystatechange возникает каждый раз, когда изменяется значение свойства readyState. Например, выполним асинхронный запрос:

var request = new XMLHttpRequest();

function reqReadyStateChange() {
    if (request.readyState == 4) {
        var status = request.status;

        if (status == 200) {
            document.write(request.responseText);
        } else {
            document.write("Ответ сервера " + request.statusText);
        }
    }
}

request.open("GET", "/hello.txt");
request.onreadystatechange = reqReadyStateChange;
request.send();

Кроме обработки события readystatechange для получения ответа сервера можно также обрабатывать событие load, которое возникает после выполнения запроса. Его использование аналогично:

var request = new XMLHttpRequest();

function responceLoad() {
    if (request.readyState == 4) {
        var status = request.status;

        if (status == 200) {
            document.write(request.responseText);
        } else {
            document.write("Ответ сервера " + request.statusText);
        }
    }
}

request.open("GET", "/hello.txt");
request.onload = responceLoad;
request.send();

Отправка данных

Принцип отправки данных может отличаться в различных ситуациях. Рассмотрим эти ситуации.

- Отправка GET-запроса

GET-запрос характеризуется тем, что данные могут отправляться в строке запроса:

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8" />
	</head>
	<body>
		<div id="output"></div>
		
		<script>
			// объект для отправки
			var user = {
				name: "Tom",
				age: 23
			};
			
			var request = new XMLHttpRequest();
			
			function reqReadyStateChange() {
				if (request.readyState == 4) {
					var status = request.status;
					
					if (status == 200) {
						document.getElementById("output").innerHTML = request.responseText;
					}
				}
			}
			
			// строка с параметрами для отправки
			var body = "name=" + user.name + "&age="+user.age;
			request.open("GET", "http://localhost:8080/postdata.php?"+body);
			request.onreadystatechange = reqReadyStateChange;
			request.send();
		</script>
	</body>
</html>

Для отправки берем свойства объекта user и формируем из их значений сроку с параметрами: "name=" + user.name + "&age="+user.age. Затем эта строка добавляется к строке запроса в методе open("GET", "http://localhost:8080/postdata.php?"+body)

- Кодирование параметров

Все отправляемые в GET-запросе параметры разделяются знаком амперсанда(&). Но что, если какой-нибудь параметр имеет знак амперсанда. Например,

var user = {
    name: "Tom&Tim",
    age: 23
};
// строка с параметрами для отправки
var body = "name=" + user.name + "&age="+user.age;

В этом случае при получении параметров скрипт на стороне сервера может неправильно обработать данные и неправильно извлечь параметры. Поэтому, чтобы кодировать все передаваемые данные, нужно применять функцию encodeURIComponent():

var body = "name=" + encodeURIComponent(user.name) + "&age="+encodeURIComponent(user.age);

При этом строка "Tom&Tim" будет кодирована в следующую строку: "Tom%26Tim".

При необходимости мы можем выполнить обратное декодирование с помощью функции decodeURIComponent():

var encodeName = encodeURIComponent(user.name); // Tom%26Tim
var decodeName = decodeURIComponent(encodeName); // Tom&Tim

- POST-запросы

Отправка данных в POST-запросах будет немного отличаться:

var user = {
    name: "Tom",
    age: 23
};
 
var request = new XMLHttpRequest();
function reqReadyStateChange() {
    if (request.readyState == 4 && request.status == 200)
        document.getElementById("output").innerHTML=request.responseText;
}
var body = "name=" + user.name + "&age="+user.age;
request.open("POST", "http://localhost:8080/postdata.php");
request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
request.onreadystatechange = reqReadyStateChange;
request.send(body);

Для отправки данных методом POST надо установить заголовок Content-Type с помощью метода setRequestHeader(). В данном случае заголовок имеет значение application/x-www-form-urlencoded.

- Отправка данных в формате JSON

Для отправки данных в формате JSON нам необходимо установить соответствующий заголовок и сериализовать данные с помощью метода JSON.stringify:

// объект для отправки
var user = {
    username: "Tom",
    age: 23
};
var json = JSON.stringify(user);
var request = new XMLHttpRequest();
request.open("POST", "http://localhost:8080/postjson.php");
request.setRequestHeader('Content-type', 'application/json; charset=utf-8');
request.onreadystatechange = function () {
        if (request.readyState == 4 && request.status == 200)
            document.getElementById("output").innerHTML=request.responseText;
}
request.send(json);