codex23

mini-cart source code

Jun 10th, 2025 (edited)
271
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
JavaScript 14.14 KB | Source Code | 0 0
  1. /* mini_cart_actions.js */
  2. export default class MiniCartActions {
  3.     'use strict';
  4.  
  5.     constructor(uiBody) {
  6.        
  7.         // New debug code added: Add delay to ensure DOM is ready
  8.         if (document.readyState === 'loading') {
  9.             document.addEventListener('DOMContentLoaded', () => {
  10.                 this.initialize(uiBody);
  11.             });
  12.         } else {
  13.             this.initialize(uiBody);
  14.         }
  15.        
  16.         //Properties
  17.         this.body = document.querySelector(uiBody);
  18.         if (!this.body) {
  19.             console.error(`Element with selector '${uiBody}' not found.`);
  20.         }
  21.        
  22.         this.sidebar = this.body.querySelector('.sidebar');
  23.         this.shopping_menu = this.body.querySelector('.shopping-menu-wrapper .shopping-menu');
  24.        
  25.         //mini cart and mini cart icon
  26.         //this.mini_cart = this.findMiniCart();
  27.         //this.cart_icon = this.findCartIcon();
  28.        
  29.         //mini cart
  30.         const mobile_mini_cart = this.sidebar.querySelector('.actionsWrapper .widget_shopping_cart_content');
  31.         const default_mini_cart = this.shopping_menu.querySelector('.widget_shopping_cart_content');
  32.         this.mini_cart = mobile_mini_cart
  33.         ? mobile_mini_cart
  34.         : default_mini_cart;
  35.        
  36.         //mini cart icon
  37.         const mobile_cart_icon = this.sidebar.querySelector('.actionsWrapper .shop-icons #cart-icon');
  38.         const default_cart_icon = this.shopping_menu.querySelector('.shop-icons #cart-icon');
  39.         this.cart_icon = mobile_cart_icon
  40.         ? mobile_cart_icon
  41.         : default_cart_icon;
  42.        
  43.         this.cart_items = this.shopping_menu.querySelectorAll('.woocommerce-mini-cart-item');
  44.        
  45.         //mini cart counter
  46.         this.mini_cart_counter = this.findMiniCartCounter();
  47.        
  48.         this.p_add_to_cart = this.shopping_menu.querySelectorAll('.product-actions .add_to_cart_button');
  49.        
  50.         //close mini cart
  51.         this.close_mini_cart = this.mini_cart.querySelector('#close-container');
  52.        
  53.         console.log('mini-cart classlist (constructor): ', this.mini_cart);
  54.         console.log('cart_icon classlist (constructor): ', this.cart_icon);
  55.        
  56.         //Methods
  57.         this.miniCartActivator();
  58.        
  59.     }
  60.    
  61.     initialize(uiBody) {
  62.         console.log('Initializing MiniCartActions...');
  63.        
  64.         //Properties
  65.         this.body = document.querySelector(uiBody);
  66.         console.log('Body element:', this.body);
  67.        
  68.         if (!this.body) {
  69.             console.error(`Element with selector '${uiBody}' not found.`);
  70.             return;
  71.         }
  72.        
  73.         this.sidebar = this.body.querySelector('.sidebar');
  74.         this.shopping_menu = this.body.querySelector('.shopping-menu-wrapper .shopping-menu');
  75.        
  76.         console.log('Sidebar:', this.sidebar);
  77.         console.log('Shopping menu:', this.shopping_menu);
  78.        
  79.         //mini cart and mini cart icon
  80.         this.mini_cart = this.findMiniCart();
  81.         this.cart_icon = this.findCartIcon();
  82.        
  83.         console.log('Mini cart found:', this.mini_cart);
  84.         console.log('Cart icon found:', this.cart_icon);
  85.        
  86.         if (!this.mini_cart) {
  87.             console.error('Mini cart not found! Cannot proceed.');
  88.             return;
  89.         }
  90.        
  91.         if (!this.cart_icon) {
  92.             console.error('Cart icon not found! Cannot proceed.');
  93.             return;
  94.         }
  95.        
  96.         this.cart_items = this.shopping_menu?.querySelectorAll('.woocommerce-mini-cart-item') || [];
  97.         this.mini_cart_counter = this.findMiniCartCounter();
  98.         this.p_add_to_cart = this.shopping_menu?.querySelectorAll('.product-actions .add_to_cart_button') || [];
  99.         this.close_mini_cart = this.mini_cart?.querySelector('#close-container');
  100.        
  101.         console.log('Elements found:', {
  102.             cart_items: this.cart_items.length,
  103.             mini_cart_counter: this.mini_cart_counter,
  104.             p_add_to_cart: this.p_add_to_cart.length,
  105.             close_mini_cart: this.close_mini_cart
  106.         });
  107.        
  108.         // Test stateSetter directly without event listeners first
  109.         this.testStateSetter();
  110.        
  111.         // Initialize the mini cart activator
  112.         this.miniCartActivator();
  113.     }
  114.    
  115.     findMiniCart() {
  116.         /**
  117.         * Finds the mini-cart container element in the DOM.
  118.         * Searches first in the mobile sidebar's actionsWrapper, then falls back
  119.         * to the desktop shopping-menu container.
  120.         *
  121.         * @returns {HTMLElement|null} The mini-cart container element (.widget_shopping_cart_content)
  122.         *                            or null if not found in either location
  123.         */
  124.        
  125.         let miniCart = null;
  126.        
  127.         if (this.sidebar) {
  128.             miniCart = this.sidebar.querySelector('.actionsWrapper .widget_shopping_cart_content');
  129.         }
  130.        
  131.         if (!miniCart && this.shopping_menu) {
  132.             miniCart = this.shopping_menu.querySelector('.widget_shopping_cart_content');
  133.         }
  134.         console.log('findMiniCart(): ', miniCart);
  135.         return miniCart;
  136.     }
  137.    
  138.     findCartIcon() {
  139.         /**
  140.         * Finds the cart icon element in the DOM.
  141.         * Searches first in the mobile sidebar's actionsWrapper, then falls back
  142.         * to the desktop shopping-menu container.
  143.         *
  144.         * @returns {HTMLElement|null} The cart icon element (#cart-icon)
  145.         *                            or null if not found in either location
  146.         */
  147.        
  148.         let cartIcon = null;
  149.         console.log('findCartIcon() (sidebar): ', cartIcon);
  150.         if (this.sidebar) {
  151.             cartIcon = this.sidebar.querySelector('.actionsWrapper .shop-icons #cart-icon');
  152.            
  153.         }
  154.        
  155.         if (!cartIcon && this.shopping_menu) {
  156.             cartIcon = this.shopping_menu.querySelector('.shop-icons #cart-icon');
  157.            
  158.         }
  159.         console.log('findCartIcon() (shopping_menu): ', cartIcon);
  160.         return cartIcon;
  161.     }
  162.    
  163.     findMiniCartCounter() {
  164.         /**
  165.         * Finds the mini-cart counter element in the DOM.
  166.         * Searches first in the mobile sidebar's actionsWrapper, then falls back
  167.         * to the desktop shopping-menu container.
  168.         *
  169.         * @returns {HTMLElement|null} The mini-cart counter element (.mini_cart_counter)
  170.         *                            or null if not found in either location
  171.         */
  172.        
  173.         let counter = null;
  174.        
  175.         if (this.sidebar) {
  176.             counter = this.sidebar.querySelector('.actionsWrapper .mini_cart_counter');
  177.         }
  178.        
  179.         if (!counter && this.shopping_menu) {
  180.             counter = this.shopping_menu.querySelector('.mini_cart_counter');
  181.         }
  182.         console.log('findMiniCartCounter(): ', counter);
  183.         return counter;
  184.     }
  185.    
  186.     initializeContainer(container) {
  187.         /**
  188.         * Initializes the mini-cart to its default inactive state.
  189.         * Removes any 'active' class and adds 'inactive' class to ensure
  190.         * the mini-cart starts hidden when the page loads or reloads.
  191.         *
  192.         * @returns {void}
  193.         */
  194.         console.log('=== initializeMiniCart ===');
  195.         if (!this.mini_cart) {
  196.             console.error('Cannot initialize: mini_cart is null');
  197.             return;
  198.         }
  199.        
  200.         // Clear all existing state classes first
  201.         this.mini_cart.classList.remove('cart_active', 'cart_inactive');
  202.        
  203.         // Force DOM update
  204.         this.mini_cart.offsetHeight;
  205.        
  206.         // Add inactive class
  207.         this.mini_cart.classList.add('cart_inactive');
  208.        
  209.         /* More debugging added/ */
  210.         console.log('Mini cart initialized with classes:', this.mini_cart.className);
  211.         console.log('Mini cart computed style display:', getComputedStyle(this.mini_cart).display);
  212.         return this.mini_cart.classList; // Missing return statemenet added.
  213.     }
  214.    
  215.     stateSetter(element, off, on) {
  216.         /**
  217.         * stateSetter
  218.         * Toggles the class state of a given element by removing an "off" state and adding an "on" state.
  219.         *
  220.         * @param {HTMLElement} element - The element to update.
  221.         * @param {string} off - The class name representing the "off" state.
  222.         * @param {string} on - The class name representing the "on" state.
  223.         * @returns {HTMLElement} - The updated element.
  224.         */
  225.        
  226.         /* Debug code added: Breaking points */
  227.        
  228.         // Log before changes
  229.         console.log('Classes before change:', element.className);
  230.        
  231.         // Remove old class
  232.         element.classList.remove(off);
  233.         console.log('After removing', off, ':', element.className);
  234.        
  235.         // Add new class
  236.         element.classList.add(on);
  237.         console.log('After adding', on, ':', element.className);
  238.        
  239.         // Force DOM reflow
  240.         const height = element.offsetHeight;
  241.         console.log('Forced reflow, offsetHeight:', height);
  242.        
  243.         // Log computed styles
  244.         const computedStyle = getComputedStyle(element);
  245.         console.log('Computed display:', computedStyle.display);
  246.         console.log('Computed visibility:', computedStyle.visibility);
  247.         console.log('Computed opacity:', computedStyle.opacity);
  248.         return element;
  249.     }
  250.    
  251.     stateControl(trigger, element) {
  252.         /**
  253.         * stateControl
  254.         * Binds a click event listener to the trigger element. When clicked, it toggles
  255.         * the state (active/inactive) of the target element.
  256.         *
  257.         * @param {HTMLElement} trigger - The element that triggers the state change.
  258.         * @param {HTMLElement} element - The target element whose state is toggled.
  259.         */
  260.         console.log('stateControl() trigger: ', trigger);
  261.         console.log('stateControl() element: ', element);
  262.         trigger.addEventListener('click', () => {
  263.             if (element.classList.contains('cart_inactive')) {
  264.                 this.stateSetter(element, 'cart_inactive', 'cart_active');
  265.                
  266.                 return element;
  267.             } else if(element.classList.contains('cart_active')) {
  268.                 this.stateSetter(element, 'cart_active', 'cart_inactive');
  269.                
  270.                 return element;
  271.             } else {
  272.                 return;
  273.             }
  274.         });
  275.     }
  276.        
  277.     closeMiniCart(container, closeToggle) {
  278.         if (!container || !closeToggle) {
  279.             console.error('closeMiniCart: Missing container or closeToggle');
  280.             return;
  281.         }
  282.        
  283.         console.log('Setting up close mini cart listener');
  284.        
  285.         closeToggle.addEventListener('click', (event) => {
  286.             event.preventDefault();
  287.             event.stopPropagation();
  288.             console.log('Close button clicked');
  289.             this.stateSetter(container, 'cart_active', 'cart_inactive');
  290.         });
  291.        
  292.         return container;
  293.     }
  294.    
  295.     appendElem(trigger, element) {
  296.         /**
  297.         * appendElem
  298.         * Appends a given element to the trigger element.
  299.         *
  300.         * @param {HTMLElement} trigger - The parent element where the element should be appended.
  301.         * @param {HTMLElement} element - The element to append.
  302.         * @returns {HTMLElement} - The appended element.
  303.         */
  304.         if (!trigger || !element) {
  305.             console.error('appendElem: Missing trigger or element');
  306.             return null;
  307.         }
  308.        
  309.         const appended = trigger.appendChild(element);
  310.        
  311.         return appended;
  312.     }
  313.    
  314.    
  315.     activateCounter() {
  316.         /**
  317.         * activateCounter
  318.         * Updates the mini-cart counter state from inactive to active.
  319.         *
  320.         * @returns {HTMLElement|undefined} - The updated mini cart counter, if updated.
  321.         */
  322.         if (!this.mini_cart_counter) {
  323.             return;
  324.         }
  325.        
  326.         if (this.mini_cart_counter.classList.contains('counter_inactive')) {
  327.             this.stateSetter(this.mini_cart_counter, 'counter_inactive', 'counter_active');
  328.             return this.mini_cart_counter;
  329.         }
  330.     }
  331.    
  332.     buttonHandler(buttons) {
  333.         /**
  334.         * buttonHandler
  335.         * Attaches a click event listener to each add-to-cart button, triggering the counter activation.
  336.         *
  337.         * @param {NodeList|Array} buttons - A collection of add-to-cart button elements.
  338.         * @returns {NodeList|Array} - The collection of buttons with event listeners attached.
  339.         */
  340.         if (!buttons || buttons.length === 0) {
  341.             return buttons;
  342.         }
  343.        
  344.         buttons.forEach(button => {
  345.             button.addEventListener('click', () => {
  346.                 this.activateCounter(); // Fixed: was missing 'this.'
  347.             });
  348.         });
  349.        
  350.         return buttons;
  351.     }
  352.    
  353.    
  354.     closeWhenOutside(entity) {
  355.         /**
  356.         * closeWhenOutside
  357.         * Binds a click event listener to an external entity (like the document) that closes the mini-cart
  358.         * if the click occurs outside the cart icon and mini cart.
  359.         *
  360.         * @param {HTMLElement} entity - The element to listen for clicks (typically document).
  361.         */
  362.         if (!entity) {
  363.             console.error('closeWhenOutside: No entity provided');
  364.             return;
  365.         }
  366.        
  367.         if (!this.mini_cart || !this.cart_icon) {
  368.             console.error('closeWhenOutside: Required elements not found');
  369.             return;
  370.         }
  371.    
  372.         console.log('Setting up outside click handler');
  373.        
  374.         entity.addEventListener('click', (event) => {
  375.             // Only close if mini cart is currently active
  376.             if (this.mini_cart.classList.contains('cart_active')) {
  377.                 const clickedInsideCart = this.mini_cart.contains(event.target);
  378.                 const clickedInsideIcon = this.cart_icon.contains(event.target);
  379.                
  380.                 if (!clickedInsideCart && !clickedInsideIcon) {
  381.                     console.log('Clicked outside, closing mini cart');
  382.                     this.stateSetter(this.mini_cart, 'cart_active', 'cart_inactive');
  383.                 }
  384.             }
  385.         });
  386.     }
  387.    
  388.     miniCartActivator() {
  389.         /**
  390.         * miniCartActivator
  391.         * Ties all mini-cart related behaviors together:
  392.         * cartState - Binds state control to the cart icon for toggling the mini-cart.
  393.         * buttonState - Attaches event listeners to add-to-cart buttons.
  394.         * closeWhenOutside - Sets up the event listener to close the mini-cart when clicking outside.
  395.         * appendMenu - Appends the mini-cart counter to the shopping menu.
  396.         *
  397.         * @returns {object} - An object containing the activated actions.
  398.         */
  399.        
  400.         console.log('this is miniCartActivator (mini_cart_actions)');
  401.         console.log('this is close_mini_cart from miniCartActivator: ', this.close_mini_cart);
  402.        
  403.         if(this.mini_cart) {
  404.             // Initialize state as inactive.
  405.             this.initializeContainer(this.mini_cart);
  406.            
  407.             // Set the mini-cart state accordingly.
  408.             this.stateControl(this.cart_icon, this.mini_cart);
  409.            
  410.             // Set the mini-cart state as inactive when clicking outside it.
  411.             this.closeWhenOutside('body');
  412.         }
  413.        
  414.         if(this.shopping_menu || this.sidebar) {
  415.             // Increase the cart item count when clicking on the add-to-cart button.
  416.             this.buttonHandler(this.p_add_to_cart, this.mini_cart_counter);
  417.            
  418.             // Append the cart counter to the shopping menu.
  419.             this.appendElem(this.shopping_menu, this.mini_cart_counter);   
  420.     }  
  421.    
  422.    
  423.    
  424.  
  425. }
  426.  
  427. /* index.js */
  428.  
  429. import MiniCartActions from './/modules/mini_cart_actions.js';
  430. const miniCartActions = new MiniCartActions('body');
Advertisement
Add Comment
Please, Sign In to add comment