Создаем корзину покупателя на чистом 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

146 комментариев

Страница 5 из 6  
Гость 29.08.2015 13:32
@Incode, а как сделать так?
http://hkar.ru/Dqo4
Гость 29.08.2015 15:50
и как добавить кнопку удалить для каждого товара?
Incode 30.08.2015 08:48
и как добавить кнопку удалить
Я еще как бы в отпуске, поэтому пишу "на коленке" )) В общем, вот пример по вашему вопросу. Может не очень опрятно, т.к. на скорую руку, но работает.
Гость 30.08.2015 11:23
@Incode, спасибо, приятного отдыха)
Гость 11.09.2015 18:44
Уважаемый создатель сего чуда, примите мои искренние признание и благодарность за такой замечательный урок. У вас в новом примере с удалением товара из корзины не работает кнопка удалить в IE. Можно как то это решить ?
Incode 12.09.2015 00:41
не работает кнопка удалить в IE
Да, совсем забыл про IE. Добавьте в код такой полифилл для работы метода .matches():
(function (e) {
    e.matches || (e.matches = e.matchesSelector || function (selector) {
        var matches = document.querySelectorAll(selector),
            i = -1;
        while (matches[++i] && matches[i] !== this) {}
        return !!matches[i];
    });
})(Element.prototype);

создатель сего чуда
Перебор. Тут до чуда, как до Луны раком ;)
Гость 12.09.2015 01:42
Все добавил, заработало, спасибо вам огромное.
Гость 03.01.2016 14:46
Спасибо за урок, примите искренную благодарность.
Но можно сделать так, чтобы корзина открывалась сразу после добавлени товара, тоесть я нажал на кнопочку "Добавить товар", он добавился и корзина сразу открыта со всеми товарами.
Incode 03.01.2016 16:49
чтобы корзина открывалась сразу после добавлени товара
Да, можно. В функции addToCart() сделайте изменение в условии:
// Обновляем данные в LocalStorageif(!setCartData(cartData)){    d.getElementById('checkout').click(); // вызываем клик на кнопке "Оформить заказ"    this.disabled = false; // разблокируем кнопку после обновления LS}
Гость 05.01.2016 11:26
Доброго дня! Спасибо за урок, есть вопросик.
Возможно сделать так, чтобы каждый элемент выводился отдельно.
То есть например, чтобы цена выводилась под тегом "<span>" а название товара под "<a>".
Заранее спасибо!
Incode 11.01.2016 23:07
сделать так, чтобы каждый элемент выводился отдельно
Конечно можно. Но для того, чтоб подкорректировать код, нужно видеть точную html-разметку. Можете создать пример разметки на jsfiddle.net и попробуем решить проблемку.
Гость 16.01.2016 00:35
А что лучше всего делать с товаром в корзине? Посылать себе на мейл? Можно код?
Incode 16.01.2016 09:57
А что лучше всего делать с товаром в корзине?
Как-то странновато вопрос задан. Пользователю лучше купить его, а вам - продать (ваш К.О.) Обычно, после того, как товар собран в корзину, схема проста:
1. Пользователь оформляет заказ, указывая свои контактные данные и отправляет на обработку.
2. Данные передаются на сервер (в моих проектах, я использую для этого ajax)
3. На сервере вы их можете записать в БД, можете отправить на email, а можете сделать и то, и другое.
4. Дальше, пользователь ждёт, потирая ручки, когда вы ему этот заказ доставите, а вы потираете ручки, в предвкушении миллионного дохода :)
Можно код?
Какой код вы ждёте? Отправки на email? Так в PHP есть стандартная функция mail(). Можно воспользоваться готовыми и универсальными решениями, как, например, PHPMailer
Гость 18.01.2016 12:41
Добрый день!
Спасибо за урок. Не могли бы вы подсказать способ подсчета общей стоимости всех позиций
Incode 18.01.2016 15:27
Спасибо за урок.
Слово "урок" - это очень громко, т.к. я просто делюсь каким-то своим опытом и не более того.
подсчета общей стоимости всех позиций
В комментариях, я уже как бы отвечал на подобный вопрос. Вот ссылка на пример, в исходном коде страницы сможете увидеть/скопировать JS-код и остальное.
Гость 22.01.2016 14:53
Спасибо, отличное решение!
А как можно сделать чтоб в отдельном диве на странице выводилось
у вас 100500 товаров на сумму 100500
перейти в корзину.
Т.е чтоб выводить корзину на каждой странице.
Incode 22.01.2016 18:07
выводить корзину на каждой странице
localStorage, как и cookie, будет доступен на каждой странице. Поэтому проблем нет, чтоб получать данные, когда это понадобится. Собственно, можно организовать какой-нибудь label, где будет отображаться количество товаров в корзине, а при клике на него - будет открываться форма для оформления товара.
P.S. Сейчас у меня очень большой проект и руки не доходят, чтоб сделать полноценную корзину, как меня неоднократно просили. Если ничего не изменится, то в начале февраля, попробую реализовать. Если вам не к спеху, то подождите... Впрочем, ничего не мешает потом изменить существующий вариант, на новый.
Гость 23.01.2016 00:12
Ребят, помогите маленькому в этом вопросе. У нас есть таблица которая строится из значений которые мы выводим, но как это после отправить в почтовом сообщении или для онлайн оплаты? Заранее спасибо!)
Incode 23.01.2016 07:43
как это после отправить в почтовом сообщении или для онлайн оплаты?
Ваш вопрос очень абстрактный и на часть его, я уже отвечал выше: для отправки на почту, есть стандартная функция PHP mail() или можно использовать класс PHPMailer. А для оплаты онлайн, можно использовать API, предоставляемые банками или сервисы вроде Robokassa, Интеркасса, LiqPay и т.д.
Гость 23.01.2016 11:06
Ваш вопрос очень абстрактный и на часть его, я уже отвечал выше: для отправки на почту, есть стандартная функция PHP mail() или можно использовать класс PHPMailer. А для оплаты онлайн, можно использовать API, предоставляемые банками или сервисы вроде Robokassa, Интеркасса, LiqPay и т.д.

Ну а можно хоть какой-то пример для рассмотрения? Мне нужно прикрутить форму яндекса, насколько я знаю, там идет своя форма с данными. Какие значения стоит передавать икаким образом, вот это я не понимаю?
Incode 23.01.2016 17:53
нужно прикрутить форму яндекса, насколько я знаю, там идет своя форма с данными.
Я понятия не имею о формах Яндекса, т.к. никогда их не использовал и вряд ли что-то смогу подсказать в этом направлении.
Гость 23.01.2016 19:59
Я понятия не имею о формах Яндекса, т.к. никогда их не использовал и вряд ли что-то смогу подсказать в этом направлении.

хорошо, тогда немного по другому спрошу. Как передать значение в форму html
<form action="mail.php">
<input type="товар" name="товар" placeholder="Здесь будет выбранные объекты">
<input type="цена" name="цена" placeholder="а здесь сумма">
<button type="submit">Отправить</button>
</form>
вот такого вида можно сделать?
Incode 24.01.2016 09:23
Как передать значение в форму html
А зачем передавать передавать значения в форму? Вы данные отправляете обычным способом или с помощью Ajax? Если Ajax, то всё предельно просто:
// по клику на какую-то кнопку с id = send_order
document.getElementById('send_order').addEventListener('click', function() {
    // добавили все данные какой-то формы, (!)если нужно
    // если не нужно, то просто: var formData = new FormData();
    var formData = new FormData(document.querySelector('form'));
    // достали заказываемые товары и добавили к остальным данным
    formData.append('order', localStorage.getItem('cart'));
    // отправляем данные на сервер
    var xhr = new XMLHttpRequest();
    xhr.open('POST', 'mail.php', true);
    xhr.send(formData);
});

На сервере:
<?php
// преобразовали json-строку в массив
$order = json_decode($_POST['order'], 1);
// и делаем с ним всё, что нам угодно
print_r( $order );
Гость 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-шников товаров, по которым можно получить всю инфу из БД, но дабы лишний раз не мучить мускул, можно отправить все данные.
Страница 5 из 6  
Ваш комментарий:
X