How to Create The Fly To Cart Feature In Magento 2

how to create fly to cart feature in magento 2

Quality improvements in eCommerce websites are essential to keep customers engaged in the online shopping experience. This post will introduce a new feature from Magento 2 to upgrade the current website: Fly To Cart.

Let’s begin!

What Is The Fly To Cart Feature?

Just as you might imagine from its name, this feature will have the product chosen flown into the cart. Thus, the step of adding things to the shopping cart is now done within one single screen.

In the past, whenever we added a new thing to the cart, the site would then send us to the shopping cart page which is rather time-consuming. With this feature, fewer steps and less time are required as there are fewer pages to switch and it is easier to track how many products are already in the cart. 

Also, consumers do not have to check the cart again to ensure whether the products have been included in the purchase or not.

This feature also makes the online shopping experience more interesting. For young people (18-25), they will probably be excited to test this function on the site.

Fly To Cart Feature Development Guide

Step 1

Override Magento_Catalog/js/catalog-add-to-cart.js file

create require js-config.js Vendor/Module/view/frontend/requirejs-config.js

var config = {
    map: {
        '*': {
            catalogAddToCart: 'Vendor_Module/js/catalog-add-to-cart'
        }
    }
};

Step 2

Add this content to Vendor_Module/js/catalog-add-to-cart.js

define([
    'jquery',
    'mage/translate',
    'underscore',
    'Magento_Catalog/js/product/view/product-ids-resolver',
    'jquery-ui-modules/widget'
], function ($, $t, _, idsResolver) {
    'use strict';

    $.widget('mage.catalogAddToCart', {
        options: {
            processStart: null,
            processStop: null,
            bindSubmit: true,
            minicartSelector: '[data-block="minicart"]',
            messagesSelector: '[data-placeholder="messages"]',
            productStatusSelector: '.stock.available',
            addToCartButtonSelector: '.action.tocart',
            addToCartButtonDisabledClass: 'disabled',
            addToCartButtonTextWhileAdding: '',
            addToCartButtonTextAdded: '',
            addToCartButtonTextDefault: '',
        },

        /** @inheritdoc */
        _create: function () {
            if (this.options.bindSubmit) {
                this._bindSubmit();
            }
        },

        /**
         * @private
         */
        _bindSubmit: function () {
            var self = this;

            if (this.element.data('catalog-addtocart-initialized')) {
                return;
            }

            this.element.data('catalog-addtocart-initialized', 1);
            this.element.on('submit', function (e) {
                e.preventDefault();
                self.submitForm($(this));
            });
        },

        /**
         * @private
         */
        _redirect: function (url) {
            var urlParts, locationParts, forceReload;

            urlParts = url.split('#');
            locationParts = window.location.href.split('#');
            forceReload = urlParts[0] === locationParts[0];

            window.location.assign(url);

            if (forceReload) {
                window.location.reload();
            }
        },

        /**
         * @return {Boolean}
         */
        isLoaderEnabled: function () {
            return this.options.processStart && this.options.processStop;
        },

        /**
         * Handler for the form 'submit' event
         *
         * @param {jQuery} form
         */
        submitForm: function (form) {
            this.ajaxSubmit(form);
        },

        /**
         * @param {jQuery} form
         */
        ajaxSubmit: function (form) {
            var self = this,
                productIds = idsResolver(form),
                formData;

            $(self.options.minicartSelector).trigger('contentLoading');
            self.disableAddToCartButton(form);
            formData = new FormData(form[0]);

            $.ajax({
                url: form.attr('action'),
                data: formData,
                type: 'post',
                dataType: 'json',
                cache: false,
                contentType: false,
                processData: false,

                /** @inheritdoc */
                beforeSend: function () {
                    if (self.isLoaderEnabled()) {
                        $('body').trigger(self.options.processStart);
                    }
                },

                /** @inheritdoc */
                success: function (res) {
                    var eventData, parameters;

                    $(document).trigger('ajax:addToCart', {
                        'sku': form.data().productSku,
                        'productIds': productIds,
                        'form': form,
                        'response': res
                    });

                    if (self.isLoaderEnabled()) {
                        $('body').trigger(self.options.processStop);
                    }

                    const miniCartElm = $(self.options.minicartSelector);
                    if (miniCartElm.length > 0) {
                        let productImage = form.closest('.product-item-info').find('img.product-image-photo').eq(0);
                        if (productImage.length === 0) {
                            productImage = form.closest('.column.main').find('.product.media img').eq(0);
                        }
                        if (productImage.length === 0) {
                            productImage = form.closest('tr.price-list-item').find('img.product-image-photo').eq(0);
                        }
                        if (productImage.length > 0) {
                            const productImageClone = productImage.clone()
                                .offset({
                                    top: productImage.offset().top,
                                    left: productImage.offset().left
                                })
                                .css({
                                    'opacity': '0.75',
                                    'position': 'absolute',
                                    'z-index': '9999',
                                    'width': 300,
                                    'height': 300
                                })
                                .appendTo($('body'));

                            productImageClone.removeClass();

                            productImageClone.animate({
                                'top': miniCartElm.offset().top + 10,
                                'left': miniCartElm.offset().left + 10,
                                'width': 0,
                                'height': 0
                            }, 700, 'easeInOutCubic', function () {
                                $(this).detach();
                            });

                            setTimeout(function () {
                                miniCartElm.find('.counter').effect('bounce', {
                                    times: 1
                                }, 200);
                            }, 700);
                        }
                    }

                    if (res.backUrl) {
                        eventData = {
                            'form': form,
                            'redirectParameters': []
                        };
                        // trigger global event, so other modules will be able add parameters to redirect url
                        $('body').trigger('catalogCategoryAddToCartRedirect', eventData);

                        if (eventData.redirectParameters.length > 0 &&
                            window.location.href.split(/[?#]/)[0] === res.backUrl
                        ) {
                            parameters = res.backUrl.split('#');
                            parameters.push(eventData.redirectParameters.join('&'));
                            res.backUrl = parameters.join('#');
                        }

                        self._redirect(res.backUrl);

                        return;
                    }

                    if (res.messages) {
                        $(self.options.messagesSelector).html(res.messages);
                    }

                    if (res.minicart) {
                        $(self.options.minicartSelector).replaceWith(res.minicart);
                        $(self.options.minicartSelector).trigger('contentUpdated');
                    }

                    if (res.product && res.product.statusText) {
                        $(self.options.productStatusSelector)
                            .removeClass('available')
                            .addClass('unavailable')
                            .find('span')
                            .html(res.product.statusText);
                    }
                    self.enableAddToCartButton(form);
                },

                /** @inheritdoc */
                error: function (res) {
                    $(document).trigger('ajax:addToCart:error', {
                        'sku': form.data().productSku,
                        'productIds': productIds,
                        'form': form,
                        'response': res
                    });
                },

                /** @inheritdoc */
                complete: function (res) {
                    if (res.state() === 'rejected') {
                        location.reload();
                    }
                }
            });
        },

        /**
         * @param {String} form
         */
        disableAddToCartButton: function (form) {
            var addToCartButtonTextWhileAdding = this.options.addToCartButtonTextWhileAdding || $t('Adding...'),
                addToCartButton = $(form).find(this.options.addToCartButtonSelector);

            addToCartButton.addClass(this.options.addToCartButtonDisabledClass);
            addToCartButton.find('span').text(addToCartButtonTextWhileAdding);
            addToCartButton.attr('title', addToCartButtonTextWhileAdding);
        },

        /**
         * @param {String} form
         */
        enableAddToCartButton: function (form) {
            var addToCartButtonTextAdded = this.options.addToCartButtonTextAdded || $t('Added'),
                self = this,
                addToCartButton = $(form).find(this.options.addToCartButtonSelector);

            addToCartButton.find('span').text(addToCartButtonTextAdded);
            addToCartButton.attr('title', addToCartButtonTextAdded);

            setTimeout(function () {
                var addToCartButtonTextDefault = self.options.addToCartButtonTextDefault || $t('Add to Cart');

                addToCartButton.removeClass(self.options.addToCartButtonDisabledClass);
                addToCartButton.find('span').text(addToCartButtonTextDefault);
                addToCartButton.attr('title', addToCartButtonTextDefault);
            }, 1000);
        }
    });

    return $.mage.catalogAddToCart;
});

You can see this is code for the fly to cart feature: 

const miniCartElm = $(self.options.minicartSelector);
if (miniCartElm.length > 0) {
    let productImage = form.closest('.product-item-info').find('img.product-image-photo').eq(0);
    if (productImage.length === 0) {
        productImage = form.closest('.column.main').find('.product.media img').eq(0);
    }
    if (productImage.length === 0) {
        productImage = form.closest('tr.price-list-item').find('img.product-image-photo').eq(0);
    }
    if (productImage.length > 0) {
        const productImageClone = productImage.clone()
            .offset({
                top: productImage.offset().top,
                left: productImage.offset().left
            })
            .css({
                'opacity': '0.75',
                'position': 'absolute',
                'z-index': '9999',
                'width': 300,
                'height': 300
            })
            .appendTo($('body'));

        productImageClone.removeClass();

        productImageClone.animate({
            'top': miniCartElm.offset().top + 10,
            'left': miniCartElm.offset().left + 10,
            'width': 0,
            'height': 0
        }, 700, 'easeInOutCubic', function () {
            $(this).detach();
        });

        setTimeout(function () {
            miniCartElm.find('.counter').effect('bounce', {
                times: 1
            }, 200);
        }, 700);
    }
}

Example Of The Fly To Cart Feature

If you still find it hard to imagine how this feature works, check out this website: Bnn/in.th. As you click the “Add to cart” button, the product will fly into the cart symbol on the page header.

A reminder to confirm the details of the order you have just made also appear so that you can see what you have put in the cart.

Wrapping Up

While this function sounds rather simple, its benefits to the website are hard to deny and they could enhance the shopping experience for online users.

If you are a Magento store owner and want to have the fly to cart feature on your site, contact us now.

If you are a Magento developer, more interesting tutorials are waiting for you to discover: