Crear un carrito de compras Javascript del lado del cliente

 

 

 

  • Clase magistral de tipografía, con Elliot Jay Stocks
  • Accesibilidad para diseñadores, con Stéphanie Walter

  • Índice
    1. Almacenamiento de sesiones: un recordatorio rápido
    2. Consideraciones de Seguridad
    3. Nuestro proyecto de muestra: Bodega
    4. Estructura HTML
      1. índice.html
      2. carrito.html
      3. pago.html
      4. pedido.html
    5. Código JavaScript
      1. Estructura del objeto
      2. Propiedades del objeto
      3. Métodos de objetos
      4. Métodos privados (ayudantes)

    En esta serie de artículos, Gabriele Romanato cubrirá en profundidad una implementación práctica del almacenamiento de sesiones mediante la creación de un carrito de compras de comercio electrónico completo con el sessionStorageobjeto y jQuery. Recuerde que, en estos artículos, no encontrará una nueva técnica para reemplazar las técnicas existentes del lado del servidor, sino simplemente una prueba del concepto de almacenamiento de sesiones.

     

    El almacenamiento de sesiones es una nueva característica introducida por la especificación " Almacenamiento web " del W3C. Es compatible con Internet Explorer 8+, Firefox, Chrome, Safari y Opera Desktop (para obtener una lista completa, consulte "¿ Puedo usarlo ?"). En esta serie de artículos, cubriremos en profundidad una implementación práctica del almacenamiento de sesiones mediante la creación de un carrito de compras de comercio electrónico completo con el sessionStorageobjeto y jQuery.

    Tenga en cuenta que, en estos artículos, no voy a proponer una nueva técnica para reemplazar las técnicas existentes del lado del servidor, sino simplemente una prueba del concepto de almacenamiento de sesiones.

    Almacenamiento de sesiones: un recordatorio rápido

    Usamos sesiones para almacenar datos y compartirlos en varias páginas. Por lo general, un usuario elegiría un producto y guardaríamos el nombre del producto junto con la cantidad y el precio elegidos.

    Luego, el usuario rellenaría un formulario con su información personal y lo guardaríamos en la sesión actual antes de finalizar el proceso, que suele ser la página de pago y la posterior redirección a la pasarela de pago (por ejemplo, PayPal). ).

    ¿Cómo se construyen los carritos de compras? PHP, por ejemplo, utiliza frecuentemente matrices asociativas para crear la estructura básica de un carrito de compras. Las matrices asociativas permiten a los desarrolladores web PHP mantener estructurados y organizados los datos de la sesión.

    Las sesiones de JavaScript funcionan de manera diferente. Generalmente, una sesión caduca cuando el usuario cierra su navegador (pero tenga en cuenta que el concepto de “cerrar un navegador” no está claro en dispositivos móviles). Cuando caduca una sesión, se eliminan todos los datos almacenados en el almacenamiento de sesión de un navegador web. No es necesario inicializar explícitamente una sesión porque en JavaScript una sesión toma la forma de un sessionStorageobjeto global y siempre está presente. Depende de nosotros escribir datos en la sesión actual.

    Los datos de la sesión vienen en forma de pares clave-valor y el valor de cada clave puede contener solo cadenas. Para escribir datos, podemos usar el sessionStorage.setItem( name, value )método:

    sessionStorage.setItem( "total", 120 );

    En este caso, la clave nombrada totalahora contiene el valor 120como una cadena, aunque hemos usado un número entero en nuestra llamada al .setItem()método. Este valor estará disponible hasta que caduque la sesión, a menos que usemos sessionStorage.removeItem( “total” )para eliminar la clave nombrada o llamemos sessionStorage.clear()para eliminar por completo todas las claves y valores del almacenamiento de la sesión.

    Tenga en cuenta que cuando una clave no existe en el almacenamiento de la sesión, su valor siempre es null. Luego, cuando eliminamos una clave del almacenamiento de la sesión e intentamos nuevamente obtener su valor, simplemente obtendremos null.

    Como habrás adivinado, nuestra clave ahora siempre está disponible, incluso mientras el usuario navega por las páginas de nuestro sitio web. Para obtener su valor simplemente escribimos lo siguiente:

    var total = sessionStorage.getItem( "total" );console.log( total ); // '120', a string

    También podemos actualizar su valor usando sessionStorage.setItem()nuevamente con un nuevo valor:

    var total = parseInt( sessionStorage.getItem( "total" ) );var quantity = 2;var updatedTotal = total * quantity;sessionStorage.setItem( "total", updatedTotal ); // '240', a string

    Ahora, la clave nombrada totaltiene un valor de 240con nuestra última actualización. ¿Por qué llamamos parseInt()? Esta es una técnica sencilla para convertir una cadena numérica en un número verdadero, asegurando que nuestro cálculo será consistente. Recuerde que todos los valores en el almacenamiento de la sesión son cadenas y nuestros cálculos deben realizarse únicamente entre números.

     

    ¡Pero espera! ¿Qué pasa con los objetos? Los objetos se pueden almacenar en el almacenamiento de sesión convirtiéndolos primero en cadenas JSON (con JSON.stringify()) y luego nuevamente en objetos JavaScript (con JSON.parse()):

    var cart = { item: "Product 1", price: 35.50, qty: 2};var jsonStr = JSON.stringify( cart );sessionStorage.setItem( "cart", jsonStr );// now the cart is {"item":"Product 1","price":35.50,"qty":2}var cartValue = sessionStorage.getItem( "cart" );var cartObj = JSON.parse( cartValue );// original object

    Para actualizar nuestro objeto, simplemente lo ampliamos y luego repetimos el procedimiento anterior.

    Consideraciones de Seguridad

    La seguridad es importante. Si leemos las notas de seguridad de la especificación del W3C, sabremos de los riesgos de seguridad incluso de una tecnología del lado del cliente como el almacenamiento web.

    El documento técnico del Equipo de Preparación para Emergencias Informáticas de EE. UU. sobre seguridad de sitios web (PDF) establece claramente:

    "Cada organización comunitaria, corporación, empresa o agencia gubernamental depende de un sitio web externo para proporcionar información sobre ellos mismos, anunciar un evento o vender un producto o servicio. En consecuencia, los sitios web públicos suelen ser los vectores de ataque más específicos para actividad maliciosa."

    Incluso si una sesión del navegador finaliza cuando el navegador está cerrado, aún pueden producirse ataques maliciosos, especialmente si el navegador se ha visto comprometido por ciertos exploits. Además, los sitios web comprometidos a menudo pueden utilizarse para difundir malware dirigido a navegadores concretos.

    Por este motivo, asegúrese de que su sitio web sea seguro antes de confiar en cualquier técnica para almacenar datos en el navegador. Mantener un sitio web seguro está más allá del alcance de este artículo, pero simplemente siguiendo las mejores prácticas de seguridad, debería poder beneficiarse del almacenamiento web sin preocuparse demasiado por sus implicaciones de seguridad.

    Nuestro proyecto de muestra: Bodega

    Nuestro proyecto de muestra es una tienda online que vende vino. Es un sitio web de comercio electrónico sencillo cuya única complicación está en cómo se calculan los gastos de envío.

    En definitiva, los vinos se venden en paquetes de seis botellas. Esto significa que la cantidad total de botellas vendidas debe ser siempre múltiplo de seis. Los gastos de envío se calculan, entonces, en función de la cantidad total de botellas vendidas.

    Nuestra tienda dependerá de PayPal, por lo que tendremos que crear una cuenta comercial en PayPal Sandbox para probar nuestro código.

    El usuario podrá agregar y quitar productos de su carrito de compras, actualizar el carrito, cambiar la cantidad de cada producto y vaciar el carrito. Deben completar un formulario con su información de contacto, especificando si su dirección de facturación es la misma que su dirección de envío.

     

    Antes de ser redirigido a PayPal, el usuario verá una página de resumen con sus datos personales, su carrito y el precio total del carrito más los gastos de envío.

    Después de completar su compra, el usuario deberá ser redirigido nuevamente a nuestro sitio web. Este es el único paso del proceso que no podemos manejar solo con JavaScript . PayPal enviará varios datos a través de una solicitud HTTP que debe procesarse con un lenguaje del lado del servidor (como PHP). Si necesita más información para comenzar con este tipo de procesamiento, consulte el tutorial de PayPal.

    Estructura HTML

    Nuestro proyecto se compone de los siguientes apartados:

    • index.htmlContiene el listado desde el que el usuario podrá añadir productos a su carrito de compra, especificando la cantidad de cada producto.
    • cart.htmlEsta es la página del carrito de compras donde los usuarios pueden actualizar o vaciar su carrito. Alternativamente, pueden volver a la página principal para continuar comprando o pasar a la página de pago.
    • checkout.htmlEn esta página, los usuarios completan un formulario con su información personal, específicamente, sus direcciones de facturación y envío.
    • order.htmlEsta página contiene un breve resumen del pedido del usuario más el formulario de PayPal. Una vez que un usuario envía el formulario, será redirigido a la página de inicio de PayPal.

    Repasaremos el marcado de este proyecto en las siguientes secciones.

    índice.html

    Los componentes principales de esta página son los formularios que permiten al usuario agregar productos a su carrito de compras.

    div data-name="Wine #1" data-price="5" h3Wine #1/h3 peuro; 5/p form action="cart.html" method="post" div label for="qty-1"Quantity/label input type="text" name="qty-1" value="1" / /div pinput type="submit" value="Add to cart" //p /form/div

    Se puede acceder a los atributos de datos utilizados aquí para almacenar nombres y precios de productos a través de jQuery utilizando los métodos .data() y $.data() .

    carrito.html

    Nuestra página de carrito de compras se compone de tres componentes: una tabla con la información del producto, un elemento que muestra el subtotal y una lista de acciones del carrito.

    form action="cart.html" method="post" table thead tr th scope="col"Item/th th scope="col"Qty/th th scope="col"Price/th /tr /thead tbody/tbody /table p strongSub Total/strong: span/span /p ul li input type="submit" name="update" value="Update Cart" / /li li input type="submit" name="delete" value="Empty Cart" / /li li a href="index.html"Continue Shopping/a /li li a href="checkout.html"Go To Checkout/a /li /ul/form

    La tabla contenida en esta página está vacía y la llenaremos con datos a través de JavaScript. El elemento que muestra el subtotal funciona simplemente como marcador de posición para JavaScript. Las dos primeras acciones, "Actualizar carrito" y "Vaciar carrito", serán manejadas por JavaScript, mientras que las dos últimas acciones son simplemente enlaces a la página de lista del producto y a la página de pago, respectivamente.

     

    pago.html

    Esta página tiene cuatro componentes:

    • una tabla que muestra los artículos pedidos (la misma tabla que se muestra anteriormente en la sección del carrito de compras), más el precio final y los gastos de envío;
    • un formulario en el que el usuario deberá rellenar sus datos de facturación;
    • un formulario con información de envío;
    • una casilla de verificación para permitir al usuario especificar que sus detalles de facturación son los mismos que sus detalles de envío.
    table thead tr th scope="col"Item/th th scope="col"Qty/th th scope="col"Price/th /tr /thead tbody /tbody/tablediv p strongShipping/strong: span/span /p p strongTotal/strong: span/span /p/divform action="order.html" method="post" h2Your Details/h2 fieldset legendBilling/legend !-- Name, Email, City, Address, ZIP Code, Country (select box) --div label for="name"Name/label input type="text" name="name" data-type="string" data-message="This field may not be empty" //divdiv label for="email"Email/label input type="text" name="email" data-type="expression" data-message="Not a valid email address" //divdiv label for="city"City/label input type="text" name="city" data-type="string" data-message="This field may not be empty" //divdiv label for="address"Address/label input type="text" name="address" data-type="string" data-message="This field may not be empty" //divdiv label for="zip"ZIP Code/label input type="text" name="zip" data-type="string" data-message="This field may not be empty" //divdiv label for="country"Country/label select name="country" data-type="string" data-message="This field may not be empty" option value=""Select/option option value="US"USA/option option value="IT"Italy/option /select/div/fieldsetdivSame as Billing input type="checkbox" value=""//divfieldsetlegendShipping/legend !-- Same fields as billing --/fieldsetpinput type="submit" value="Submit" //p/form

    Los atributos de datos se utilizan aquí para la validación. El data-typeatributo especifica el tipo de datos que estamos validando y data-messagecontiene el mensaje de error que se mostrará en caso de falla.

    No utilicé la validación de correo electrónico integrada en los navegadores web sólo por simplicidad, pero puedes usarla si quieres. Todo sobre Apple, Mac e Iphone

    pedido.html

    Esta página final contiene un breve resumen del pedido del usuario, sus detalles y el formulario de PayPal.

    h1Your Order/h1table thead tr th scope="col"Item/th th scope="col"Qty/th th scope="col"Price/th /tr /thead tbody /tbody/tablediv p strongShipping/strong: span/span /p p strongTotal/strong: span/span /p/divdiv h2Your Data/h2 div/div/divform action="" method="post" input type="hidden" name="cmd" value="_cart" / input type="hidden" name="upload" value="1" / input type="hidden" name="business" value="" / input type="hidden" name="currency_code" value="" / input type="submit" value="Pay with PayPal" //form

    El formulario de PayPal y otros elementos de esta página están inicialmente vacíos, excepto aquellos campos que no necesitan generarse dinámicamente.

     

    Código JavaScript

    El diseño CSS de este proyecto no tendrá ninguna influencia real en el objetivo que queremos lograr. Incluso si deshabilitamos CSS por completo, el proyecto continuaría funcionando gracias a la fuerte relación entre la estructura del HTML y el comportamiento de JavaScript.

    Usaremos un enfoque orientado a objetos debido a la complejidad de nuestros objetivos. Nuestro objeto se basará en un patrón de construcción simple y utilizará métodos públicos y privados.

    Estructura del objeto

    Nuestro objeto tiene una estructura muy simple. La función constructora inicializa el elemento de nivel superior que envuelve toda la estructura de nuestro DOM e invoca el método de inicialización.

    (function( $ ) { $.Shop = function( element ) { this.$element = $( element ); // top-level element this.init(); }; $.Shop.prototype = { init: function() { // initializes properties and methods } }; $(function() { var shop = new $.Shop( "#site" ); // object's instance });})( jQuery );

    La instancia del objeto se crea cuando el DOM está listo. Podemos probar que todo ha funcionado bien de la siguiente manera:

    $(function() { var shop = new $.Shop( "#site" ); console.log( shop.$element );});

    Esto genera lo siguiente:

    x.fn.x.init[1] 0: div#site context: document length: 1 selector: "#site"

    Ahora que sabemos que se ha creado una instancia de nuestro objeto correctamente, podemos definir sus propiedades.

    Propiedades del objeto

    Las propiedades de nuestro objeto se dividen en dos categorías: primero, las propiedades para manejar cálculos, formularios y validación, y segundo, las referencias a elementos HTML.

    $.Shop.prototype = { init: function() { // Properties this.cartPrefix = "winery-"; // prefix string to be prepended to the cart's name in session storage this.cartName = this.cartPrefix + "cart"; // cart's name in session storage this.shippingRates = this.cartPrefix + "shipping-rates"; // shipping rates key in session storage this.total = this.cartPrefix + "total"; // total key in the session storage this.storage = sessionStorage; // shortcut to sessionStorage object this.$formAddToCart = this.$element.find( "form.add-to-cart" ); // forms for adding items to the cart this.$formCart = this.$element.find( "#shopping-cart" ); // Shopping cart form this.$checkoutCart = this.$element.find( "#checkout-cart" ); // checkout form cart this.$checkoutOrderForm = this.$element.find( "#checkout-order-form" ); // checkout user details form this.$shipping = this.$element.find( "#sshipping" ); // element that displays the shipping rates this.$subTotal = this.$element.find( "#stotal" ); // element that displays the subtotal charges this.$shoppingCartActions = this.$element.find( "#shopping-cart-actions" ); // cart actions links this.$updateCartBtn = this.$shoppingCartActions.find( "#update-cart" ); // update cart button this.$emptyCartBtn = this.$shoppingCartActions.find( "#empty-cart" ); // empty cart button this.$userDetails = this.$element.find( "#user-details-content" ); // element that displays the user's information this.$paypalForm = this.$element.find( "#paypal-form" ); // PayPal form this.currency = "euro;"; // HTML entity of the currency to be displayed in layout this.currencyString = "€"; // currency symbol as text string this.paypalCurrency = "EUR"; // PayPal's currency code this.paypalBusinessEmail = "[email protected]"; // your PayPal Business account email address this.paypalURL = "https://www.sandbox.paypal.com/cgi-bin/webscr"; // URL of the PayPal form // object containing patterns for form validation this.requiredFields = { expression: { value: /^([w-.]+)@((?:[w]+.)+)([a-z]){2,4}$/ }, str: { value: "" } }; // public methods invocation }};

    Repasemos estas propiedades una por una.

     

    Almacenamiento y otras propiedades:

    • cartPrefixUn prefijo que se antepondrá a la clave del nombre del carrito en el almacenamiento de la sesión.
    • cartNameLa clave del nombre del carrito en el almacenamiento de la sesión (combina la cartPrefixcadena con la cartcadena)
    • shippingRatesLa clave de tarifa de envío en el almacenamiento de sesiones
    • totalLa clave del total en el almacenamiento de sesiones.
    • storageAcceso directo al sessionStorageobjeto.
    • currencyUna entidad HTML utilizada para mostrar la moneda actual en el diseño.
    • currencyStringEl símbolo de moneda actual utilizado en el texto del elemento.
    • paypalCurrencyCódigo de texto de moneda de PayPal
    • paypalBusinessEmailLa dirección de correo electrónico de su cuenta PayPal Business
    • paypalURLLa URL del formulario de PayPal (por defecto es la URL de PayPal Sandbox)
    • requiredFieldsUn objeto que contiene los patrones y reglas para la validación de formularios.

    Referencias a elementos:

    • $formAddToCartLos formularios para añadir productos al carrito de la compra
    • $formCartEl formulario del carrito de compras
    • $checkoutCartEl formulario del carrito de compras de la caja
    • $checkoutOrderFormEl formulario de pago donde los usuarios ingresan su información personal.
    • $shippingEl elemento que contiene y muestra las tarifas de envío.
    • $subTotalEl elemento que contiene y muestra los cargos totales.
    • $shoppingCartActionsLos elementos que contienen las acciones relacionadas con el carrito de compras.
    • $updateCartBtnEl botón para actualizar el carrito de compras.
    • $emptyCartBtnEl botón para vaciar el carrito.
    • $userDetailsEl elemento que contiene y muestra la información ingresada por el usuario.
    • $paypalFormformulario de paypal

    Todos los elementos tienen el prefijo del $signo, lo que significa que son objetos jQuery. Pero no todos estos elementos están disponibles en todas las páginas . Para comprobar si existe un elemento jQuery, simplemente pruebe su lengthpropiedad:

     

    if( $element.length ) { // the element exists}

    Otro enfoque, que no se utiliza en nuestro proyecto, es agregar una ID o clase particular al bodyelemento y realizar acciones de forma condicional:

    var $body = $( "body" ), page = $body.attr( "id" ); switch( page ) { case "product-list": // actions for handling products break; case "shopping-cart": // actions for handling the shopping cart break; case "checkout": // actions for handling the checkout's page break; default: break; }

    Métodos de objetos

    Las acciones de nuestro código tienen lugar en los métodos de nuestro objeto, los cuales, a su vez, se pueden dividir en métodos públicos y privados. Los métodos privados operan en segundo plano, por así decirlo, y ayudan a los métodos públicos a realizar sus tareas. Estos métodos tienen el prefijo un guión bajo y nunca se utilizan directamente.

    Mientras tanto, los métodos públicos operan directamente sobre los elementos y datos de la página y no tienen prefijos. Ya hemos visto el init()método, que simplemente inicializa propiedades y otros métodos públicos en la función constructora del objeto. Los otros métodos se explicarán a continuación.

    Métodos privados (ayudantes)

    El primer método privado, _emptyCart()simplemente vacía el almacenamiento de la sesión actual en el navegador:

    $.Shop.prototype = { // empties session storage _emptyCart: function() { this.storage.clear(); }};

    Para formatear un número con un número determinado de decimales, implementamos el _formatNumber()método:

    /* Format a number by decimal places * @param num Number the number to be formatted * @param places Number the decimal places * @returns n Number the formatted number*/_formatNumber: function( num, places ) { var n = num.toFixed( places ); return n;}

    Este método utiliza el método toFixed() del Numberobjeto de JavaScript. Su función en nuestro proyecto es formatear adecuadamente los precios.

    Debido a que no todos los precios de nuestras páginas están contenidos en atributos de datos , necesitamos un método especializado para extraer la porción numérica de una cadena de los nodos de texto. Este método se llama _extractPrice():

    /* Extract the numeric portion from a string * @param element Object the jQuery element that contains the relevant string * @returns price String the numeric string */_extractPrice: function( element ) { var self = this; var text = element.text(); var price = text.replace( self.currencyString, "" ).replace( " ", "" ); return price;}

    Arriba, selfhay una referencia al $.Shopobjeto, y la necesitaremos cada vez que queramos acceder a una propiedad o método de nuestro objeto sin preocuparnos mucho por el alcance.

     

    Puede proteger este método agregando una rutina adicional que elimine todos los espacios en blanco finales:

    var text = $.trim( element.text() );

    Tenga en cuenta que el método $.trim() de jQuery elimina todas las líneas, espacios (incluidos los espacios que no se separan) y tabulaciones nuevas desde el principio y el final de una cadena. Si estos caracteres de espacio en blanco aparecen en medio de una cadena, se conservan.

    Entonces, necesitamos dos métodos para convertir cadenas en números y números en cadenas . Esto es necesario para realizar cálculos y mostrar los resultados en nuestras páginas.

    /* Converts a numeric string into a number * @param numStr String the numeric string to be converted * @returns num Number the number, or false if the string cannot be converted */_convertString: function( numStr ) { var num; if( /^[-+]?[0-9]+.[0-9]+$/.test( numStr ) ) { num = parseFloat( numStr ); } else if( /^d+$/.test( numStr ) ) { num = parseInt( numStr ); } else { num = Number( numStr ); } if( !isNaN( num ) ) { return num; } else { console.warn( numStr + " cannot be converted into a number" ); return false; }},/* Converts a number to a string * @param n Number the number to be converted * @returns str String the string returned */_convertNumber: function( n ) { var str = n.toString(); return str;}

    Arriba, _convertString()ejecuta las siguientes pruebas:

    1. ¿La cadena tiene formato decimal? Si es así, utiliza la función parseFloat() .
    2. ¿La cadena tiene un formato de número entero? Si es así, utiliza la función parseInt() .
    3. Si no se puede detectar el formato de la cadena, utiliza el constructor Number() .
    4. Si el resultado es un número (probado con la función isNaN() ), devuelve el número. De lo contrario, envía una advertencia a la consola de JavaScript y devuelve false.

    Por el contrario, _convertNumber()simplemente invoca el método toString() para convertir un número en una cadena.

    El siguiente paso es definir dos métodos para convertir un objeto JavaScript en una cadena JSON y una cadena JSON nuevamente en un objeto JavaScript:

    /* Converts a JSON string to a JavaScript object * @param str String the JSON string * @returns obj Object the JavaScript object */_toJSONObject: function( str ) { var obj = JSON.parse( str ); return obj;},/* Converts a JavaScript object to a JSON string * @param obj Object the JavaScript object * @returns str String the JSON string */_toJSONString: function( obj ) { var str = JSON.stringify( obj ); return str;}

    El primer método hace uso del JSON.parse()método, mientras que el último invoca el JSON.stringify()método (consulte el artículo de Mozilla Developer Network sobre " Uso de JSON nativo ").

    ¿Por qué necesitamos estos métodos? Porque nuestro carrito también almacenará la información relacionada con cada producto utilizando el siguiente formato de datos (espacios añadidos para legibilidad):

    Llave Valor
    winery-cart { "items": [ { "product": "Wine #1", "qty": 5, "price": 5 } ] }

    La winery-cartclave contiene una cadena JSON que representa una matriz de objetos (es decir, items) en la que cada objeto muestra la información relevante sobre un producto agregado por el usuario, es decir, el nombre del producto, la cantidad y el precio.

    Es bastante obvio que ahora también necesitamos un método especializado para agregar elementos a esta clave particular en el almacenamiento de sesiones:

    /* Add an object to the cart as a JSON string * @param values Object the object to be added to the cart * @returns void */_addToCart: function( values ) { var cart = this.storage.getItem( this.cartName ); var cartObject = this._toJSONObject( cart ); var cartCopy = cartObject; var items = cartCopy.items; items.push( values ); this.storage.setItem( this.cartName, this._toJSONString( cartCopy ) );}

    Este método obtiene la clave del carrito del almacenamiento de la sesión, la convierte en un objeto JavaScript y agrega un nuevo objeto como una cadena JSON a la matriz del carrito. El objeto recién agregado tiene el siguiente formato:

    this._addToCart({ product: "Test", qty: 1, price: 2});

    Ahora, nue






    Tal vez te puede interesar:

    1. ¿Deberían abrirse los enlaces en ventanas nuevas?
    2. 24 excelentes tutoriales de AJAX
    3. 70 técnicas nuevas y útiles de AJAX y JavaScript
    4. Más de 45 excelentes recursos y repositorios de fragmentos de código

    Crear un carrito de compras Javascript del lado del cliente

    Crear un carrito de compras Javascript del lado del cliente

    Clase magistral de tipografía, con Elliot Jay Stocks Accesibilidad para diseñadores, con Stéphanie Walter Índice

    programar

    es

    https://pseint.es/static/images/programar-crear-un-carrito-de-compras-javascript-del-lado-del-cliente-839-0.jpg

    2024-04-04

     

    Crear un carrito de compras Javascript del lado del cliente
    Crear un carrito de compras Javascript del lado del cliente

    Si crees que alguno de los contenidos (texto, imagenes o multimedia) en esta página infringe tus derechos relativos a propiedad intelectual, marcas registradas o cualquier otro de tus derechos, por favor ponte en contacto con nosotros en el mail [email protected] y retiraremos este contenido inmediatamente

     

     

    Top 20