Создаем корзину покупателя на чистом JavaScript и Local Storage

Вариантов создания корзины с использованием jQuery, на просторах интернета достаточно, но так как не все хотят подключать громоздкие библиотеки, особенно для каких-то разовых задач, я хочу показать вариант реализации на чистом JS. К тому же, хранить выбранные пользователем товары, мы будем не в cookie, а Local Storage (локальное хранилище). Эта технология поддерживается практически во всех современных браузерах и даже в IE8.

Буквально два слова о Local Storage для тех, кто с этим способом хранения данных на стороне клиента не знаком. Объем хранимой информации в LS по сравнению с cookie значительно выше: около 5Мб(!) против 4Кб. К тому же, в LS данные хранятся в зашифрованном виде. Однако, как и в cookie, так и в LocalStorage, мы можем записывать только строковые данные. Если нужно добавить массив или объект, то его можно предварительно преобразовать в JSON-строку (JSON.stringify(obj)), а после получения данных из LS - производим обратное преобразование (JSON.parse(json_string)). Работать с Local Storage не просто, а очень просто. Вот его основные методы:

localStorage.setItem('key', 'value');
Обновляет или создает новую запись с ключом "key" и строковым значением "value"
var lsData = localStorage.getItem('key');
Возвращает данные связанные с ключом "key" или "null", если записи с таким ключом не обнаружено
localStorage.removeItem('key');
Удаляет данные со связанным ключом "key"
localStorage.clear();
Удаляет все записи из Local Storage

Переходим к делу и для примера, создадим такую HTML-структуру для вывода товара:

<div class="item_box">
  <h3 class="item_title">Samsung Galaxy S10</h3>
  <p>Цена: <span class="item_price">20</span>$</p>
  <button class="add_item" data-id="7">Добавить в корзину</button>
</div>
<div class="item_box">
  <h3 class="item_title">LG Optimus G E100500</h3>
  <p>Цена: <span class="item_price">100</span>$</p>
  <button class="add_item" data-id="2">Добавить в корзину</button>
</div>
  <div class="item_box">
  <h3 class="item_title">Nokia 2110</h3>
  <p>Цена: <span class="item_price">1000</span>$</p>
  <button class="add_item" data-id="5">Добавить в корзину</button>
</div>
<button id="checkout">Оформить заказ</button>
<button id="clear_cart">Очистить корзину</button>
<div id="cart_content"></div>

Все необходимые данные, такие как наименование или цена товара, мы можем брать прямо из элементов страницы. Остается важная составляющая - ID товара, которую можно выводить в каком-нибудь атрибуте. Для таких целей, я предпочитаю атрибут data-*, который я уже упоминал в других статьях. Его-то и добавим в кнопку "Добавить в корзину" каждого из товаров.
Теперь в дело вступает JavaScript. Ничего сверхъестественного тут нет и большую часть, я прокомментирую прямо в коде:

var d = document,
    itemBox = d.querySelectorAll('.item_box'), // блок каждого товара
    cartCont = d.getElementById('cart_content'); // блок вывода данных корзины
// Функция кроссбраузерной установка обработчика событий
function addEvent(elem, type, handler){
  if(elem.addEventListener){
    elem.addEventListener(type, handler, false);
  } else {
    elem.attachEvent('on'+type, function(){ handler.call( elem ); });
  }
  return false;
}
// Получаем данные из LocalStorage
function getCartData(){
  return JSON.parse(localStorage.getItem('cart'));
}
// Записываем данные в LocalStorage
function setCartData(o){
  localStorage.setItem('cart', JSON.stringify(o));
  return false;
}
// Добавляем товар в корзину
function addToCart(e){
  this.disabled = true; // блокируем кнопку на время операции с корзиной
  var cartData = getCartData() || {}, // получаем данные корзины или создаём новый объект, если данных еще нет
      parentBox = this.parentNode, // родительский элемент кнопки "Добавить в корзину"
      itemId = this.getAttribute('data-id'), // ID товара
      itemTitle = parentBox.querySelector('.item_title').innerHTML, // название товара
      itemPrice = parentBox.querySelector('.item_price').innerHTML; // стоимость товара
  if(cartData.hasOwnProperty(itemId)){ // если такой товар уже в корзине, то добавляем +1 к его количеству
    cartData[itemId][2] += 1;
  } else { // если товара в корзине еще нет, то добавляем в объект
    cartData[itemId] = [itemTitle, itemPrice, 1];
  }
  if(!setCartData(cartData)){ // Обновляем данные в LocalStorage
    this.disabled = false; // разблокируем кнопку после обновления LS
  }
 return false;
}
// Устанавливаем обработчик события на каждую кнопку "Добавить в корзину"
for(var i = 0; i < itemBox.length; i++){
  addEvent(itemBox[i].querySelector('.add_item'), 'click', addToCart);
}
// Открываем корзину со списком добавленных товаров
function openCart(e){
  var cartData = getCartData(), // вытаскиваем все данные корзины
      totalItems = '';
  // если что-то в корзине уже есть, начинаем формировать данные для вывода
  if(cartData !== null){
    totalItems = '<table class="shopping_list"><tr><th>Наименование</th><th>Цена</th><th>Кол-во</th></tr>';
    for(var items in cartData){
      totalItems += '<tr>';
      for(var i = 0; i < cartData[items].length; i++){
        totalItems += '<td>' + cartData[items][i] + '</td>';
      }
      totalItems += '</tr>';
    }
    totalItems += '</table>';
    cartCont.innerHTML = totalItems;
  } else {
    // если в корзине пусто, то сигнализируем об этом
    cartCont.innerHTML = 'В корзине пусто!';
  }
  return false;
}
/* Открыть корзину */
addEvent(d.getElementById('checkout'), 'click', openCart);
/* Очистить корзину */
addEvent(d.getElementById('clear_cart'), 'click', function(e){
  localStorage.removeItem('cart');
  cartCont.innerHTML = 'Корзина очишена.';
});

Объект "cartData" собираем по следующей схеме: ключ к товару - его ID и данные в виде массиве [название_товара, цена_товара, количество_товара]. Если бы вы вывели такой объект средствами php, то получили бы примерно следующее:

stdClass Object (
  [2] => Array (
    [0] => LG Optimus G E100500
    [1] => 100
    [2] => 1
  )
  [7] => Array (
    [0] => Samsung Galaxy S10
    [1] => 20
    [2] => 2
  )
)

Это я показал, чтобы было понимание того, как потом можно работать с этими данными на стороне сервера. И плавно подошли к тому, как же эти данные отправить на сервер. В отличии от cookie, Local Storage работает только на стороне клиента. Кто-то может и записать это в минусы LS, но я не вижу проблемы, т.к. есть достаточно способов превратить минус в плюсы. Легко и непринужденно, мы можем отправить данные Ajax-запросом, а это гораздо приятней посетителю, т.к. его не перебрасывает на другую страницу, экономит время и трафик, что немаловажно, если пользователь зашёл с мобильного устройства или скорость подключения не такая высокая.


Как видите, нет ничего сложного и объем кода, без использования сторонних библиотек, получился совсем небольшим. Если кому-то нужно учитывать более старые версии Internet Explorer, то он может добавить cookie, как "fallback" к Local Storage. То есть, проверять в функциях "getCartData" и "setCartData" возможности браузера и, если он не поддерживает LS, то в качестве хранилища использовать Cookie, а остальной код останется без изменений.

Incode Pro logo

144 комментария

Страница 4 из 6  
Гость 30.01.2016 02:07
Скажите пожалуйста, как именно передать данные из таблицы в форму (названия товаров) Со всем разобрался посредством
var iHTML = document.getElementById("total_sum").innerHTML;
          document.getElementById("Итого").attributes["value"].value = ''+iHTML+' руб';

а как вычленить названия товаров, не пойму.
Incode 30.01.2016 10:43
как именно передать данные из таблицы в форму
Зачем парсить таблицу, когда все данные уже есть в LocalStorage? Если Ajax-ом передача данных не подходит, то все эти данные, которые по сути являются массивом (смотрите в конце статьи), можно или же в цикле вывести в форму, добавляя скрытые поля, или же просто добавить одно поле, куда записать экранированную или url-кодированную json-строку.
// Создали скрытый input с именем order_data
var inp = document.createElement('input');
inp.setAttribute('name', 'order_data');
inp.setAttribute('type', 'hidden');
// кодируем строку из LS и записываем скрытое поле
inp.value = encodeURIComponent(localStorage.getItem('cart'));
// добавляем input в нашу форму
document.getElementById('my_form').appendChild(inp);
На сервере:
$arr = json_decode(urldecode($_POST['order_data']), 1);
print_r($arr);
По большому счету, на стороне сервера, вполне хватило бы и id-шников товаров, по которым можно получить всю инфу из БД, но дабы лишний раз не мучить мускул, можно отправить все данные.
Гость 31.01.2016 15:56
Здравствуйте, я очень дубовый в таких вещах, но корзина очень нужна, скажите пожалуйста, как сделать что бы в таблице возле количества товара был плюсик и минус, то есть можно было бы прибавлять и убавлять колл - во определённого товара, спасибо большое заранее, про локальное хранилище помогло, спасибо
Гость 31.01.2016 16:13
Вы меня простите, для меня сверх сложно понять что куда вставлять и в какой последовательности, получалось только парсить таблицу, если Вам не сложно, не могли бы Вы показать готовый код скрипта со всеми плюшками, с меня соточка яндекс денег для хостинга Вашего замечательного ресурса, и всяческие рекомендации для друзей)).
Гость 01.02.2016 08:59
Помогите пожалуйста, мне на почту это приходит:
Order Data : %7B%221%22%3A%5B%22%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%3Cspan%20class%3D%5C%22tovar%5C%22%20id%3D%5C%22wb_uid2%5C%22%3E%20dfgfdgdfgfdgfdgd%3C%2Fspan%3E%22%2C%2234534%22%2C4%5D%2C%228%22%3A%5B%22%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%3Cspan%20id%3D%5C%22wb_uid0%5C%22%3E%20dfgfdgdfgfdgfdgd%3C%2Fspan%3E%22%2C%2234534%22%2C2%5D%7D
Incode 01.02.2016 11:33
не могли бы Вы показать готовый код скрипта со всеми плюшками
Я собираюсь сделать более полноценное решение для тех, кто хочет сразу использовать корзину в своих проектах, НО это будет не сегодня и не завтра (может ближе к концу недели), т.к. я сейчас очень занят и для ускорения процесса, делать я его буду с использованием jQuery.
мне на почту это приходит
Похоже, что вы отправляете на почту не обработанные данные. Их нужно, как минимум, декодировать и в идеале привести к удобочитаемому виду, например, табличному виду. Примитивный пример:
<?php
// $str - данные, которые приходят с клиента (ваша строка выше)
$arr = json_decode(urldecode($str),1); // Декодировали строку и преобразовали в массив
$tbl = '<table border=1>
<tr>
	<th>ID</th>
	<th>Наименование</th>
	<th>Цена</th>
	<th>Кол-во</th>
</tr>';
foreach($arr as $id => $item_data) {
	$tbl .= '<tr>';
	$tbl .= '<td>' . $id . '</td>';
	foreach($item_data as $val) {
		$tbl .= '<td>' . $val . '</td>';
	}
	$tbl .= '</tr>';
}
$tbl .= '</table>';
// $tbl добавляем в тело письма. 
Гость 02.02.2016 10:27
Спасибо большое, из локального хранилища расшифрованная строка теперь приходит на почту, но в таблицу ни как не оборачивается зараза. вот в принципе код страницы, ничё не пойму, почему не работает? (я новичёк полный в этом деле, не смейтесь плиз))):
Показать код
Дайте Ваш ЯД кошель, будем хоть кто сколько может на поддержку проекта скидывать.
Incode 02.02.2016 11:47
не смейтесь плиз
Да тут не до смеха... Во-первых, на время отладки, я советую включать вывод ошибок. Во-вторых, в примере (добавление скрытого поля с данными), я показывал что нужно кодировать данные из LS, а не декодировать. То есть, encodeURIComponent, а не decodeURIComponent. В-третьих, при обработке данных, нет переменной $str и вместо неё, должна быть подставлена реальная переменная. В вашем случае - $_POST['order_data'].
Это то, что бросилось сразу в глаза. Более глубоко в ваш код не вникал.
Дайте Ваш ЯД кошель
Спасибо, но это лишнее.
Гость 02.02.2016 23:37
Ребята, кто как и я испытывал затруднения с отправкой данных таблицы в форму, вот простое реение,это:
<script type="text/javascript">
function sendValueInInput()
{
    a = document.getElementById('TextArea');
    b = document.getElementById('cart_content');
    a.value = b.innerText;
}
</script>
Вставляете в head страницы, а это : onclick="sendValueInInput()" вписываете в тег кнопки отправки. Естественно нужно создать в форме скрытую TextArea (Текстовую область) с таким же ID как и в скрипте (в моём примере TextArea). И всё.
Гость 06.02.2016 16:24
Добрый день, скажите пожалуйста, а над корзиной jQuery которую Вы обещали, Вы не работали ещё? Я думаю все были бы очень Вам признательны.
Incode 06.02.2016 18:15
над корзиной jQuery которую Вы обещали, Вы не работали ещё?
В общих чертах уже сделал. Если успею, то завтра выложу в разделе jQuery
Serzhi 06.02.2016 20:26
Помогите пожалуйста вопрос вот в чем.
Необходимо создать карзину катораю формирует запрос в фомате json передает их на сервер методом
HTTP POST.
для передачи используеться метод "CreateOrder"

Пример запроса

{ «token»: «541c4a59bdad87039c7ae8df6af14785сaf7526f01a613f5bd894d615ee811e5», «qnit_id»: 1, «method»: «CreateOrder», «params»: { «name»: «Иванов Иван», «phone»: «79123211232», «external_id»: «2643», "products": [ { «id»: 1000587, «quantity»: 1, }, { «id»: 3044, «quantity»: 2 } ] } }
Incode 06.02.2016 20:54
@Serzhi, данные в localStorage уже находятся в формате JSON. Поэтому можете просто добавить их в объект с другими данными.
Incode 06.02.2016 21:09
@Serzhi, Вот примерно, как должно выглядеть у вас:
var obj = {
  token: 'some_token',
  qnit_id: 1,
  method: 'CreateOrder',
  params: {
    name: 'Иванов Иван',
    phone: '79123211232',
    external_id: 2643,
    products: getCartData() // данные заказываемых товаров
  }
};
Это готовый объект со всеми вашими данными. Если нужно его преобразовать в JSON-строку:
var json = JSON.stringify(obj);
Данные, естественно, подставляете свои, функция getCartData() описана в статье.
Serzhi 11.02.2016 21:15
Подскажите как методом HTTP POST этот объект передать на адрес: http://adres.ru/api. Возможно ли сделать так чтобы при нажати кнопки в корзине и на почту отправлялась письмо и этот объект отправлялся на сервер
Incode 11.02.2016 23:58
@Serzhi, тут, как говорится, одно из двух: то ли вы не понимаете принципы работы, то ли я не понимаю, что именно вы хотите. Давайте начнем с того, что я пару дней назад выложил простенький плагин, который передает данные на сервер и отправляет данные на почту. Возможно, что вам он подойдет и вопросы отпадут сами собой. Если же нет, то продолжим обсуждение, но только вы более конкретно объясните суть задачи.
Гость 19.02.2016 08:53
Добрый день - Огромное спасибо за скрипт корзины, давно искал такой вариант исполнения.
Возможно ли прикрутить выпадающий список для кнопки добавить в корзину?
Или вместо списка выбор цвета.
Спасибо.
Incode 19.02.2016 09:44
прикрутить выпадающий список для кнопки
Если вам надо передавать на сервер какие-то дополнительные данные, то их можно без проблем добавить к запросу, обратившись к определенному элементу и достав из него нужную информацию. Простенький пример, я вам набросал, НО в нём не учитывается то, что человек может выбрать два и более товара одного типа, но с разными цветами. Для этого нужны более глобальные изменения в коде.
Гость 28.02.2016 10:01
C пустым заказом разобрался. Если дернуть /php/handler.php придет пустой заказ. Как это поправить?
Incode 28.02.2016 11:08
Если дернуть /php/handler.php придет пустой заказ. Как это поправить?
Не дергать его (ваш К.О.). Если серьёзно, то нужно проверять и фильтровать данные перед тем, как их использовать. Для проверки на "пустоту", достаточно функции empty()
Гость 24.03.2016 08:25
А как реализовать например такую вещь. Когда кликаем добавить в корзину, справа появляется кнопка удалить товар из корзины плюс она работает и по нажатию удаляет и пропадает
Incode 24.03.2016 12:36
кликаем добавить в корзину, справа появляется кнопка удалить товар
Вся работа корзины крутится вокруг объекта. Если вам так будет проще, то этот объект в то же PHP, был бы ассоциативным массивом, где ключами выступали бы id товаров. Кнопка удаления из корзины, может иметь в каком-нибудь атрибуте (я ратую за data-атрибуты) этот id. По клику на эту кнопку, получаем id и удаляем соответствующий элемент из объекта (Оператор delete), который хранится в LocalStorage, Cookie или другом хранилище.
Абстрактный пример:
// Объект с даннми корзины
var obj = {
  10: 'Item 10',
  20: 'Item 20',
  50: 'Item 50'
};
// Полученный ID
var id = 20;
// Удаление
delete obj[id];
// Проверим в консоли
console.log(obj); // Осталось два элемента со свойствами 10 и 50
После удаления, прячете кнопку, пересчитываете кол-во товаров в корзине и производите любые другие действия. которые считаете нужными.
Гость 29.03.2016 15:40
totalItems += '<table>';


Наверное, </table>?
Гость 29.03.2016 21:29
Замечательный пример!
Спасибо, автор.

Incode, пытаюсь колонку с ценой выравнять по правому краю, но что-то костыли слишком корявые получаются :-)
У вас есть идеи, как это сделать?
Гость 29.03.2016 22:28
function openCart(e){
	var cartData = getCartData(), // вытаскиваем все данные корзины
			totalItems = '',
			totalCount = 0,
			totalSum = 0;
	// если что-то в корзине уже есть, начинаем формировать данные для вывода
	if(cartData !== null){
		totalItems = '<table class="shopping_list"><tr><th>Наименование</th><th>Цена</th><th>Кол-во</th><th></th></tr>';
		for(var items in cartData){
			totalItems += '<tr>';
			for(var i = 0; i < cartData[items].length; i++){
			
			if ( i == 1 ) {
				totalItems += '<td align="right">' + cartData[items][i] + '</td>';
				} else {
				totalItems += '<td>' + cartData[items][i] + '</td>';
 				};
			}
			totalSum += cartData[items][1] * cartData[items][2];
			totalCount += cartData[items][2];
			totalItems += '<td><span class="del_item" data-id="'+ items +'">X</span></td>';
			totalItems += '</tr>';
		}
		totalItems += '<tr><td><strong>Итого</strong></td><td align="right">$ <span id="total_sum">'+ totalSum +'</span></td><td><span id="total_count">'+ totalCount +'</span> шт.</td><td></td></tr>';
		totalItems += '</table>';
		cartCont.innerHTML = totalItems;
	} else {
		// если в корзине пусто, то сигнализируем об этом
		cartCont.innerHTML = 'В корзине пусто!';
	}
	return false;
}
Страница 4 из 6  
Ваш комментарий:
X