1(function ($, elementorFrontend, elementorModules) { 2 'use strict'; 3 var _sticky = elementorModules.frontend.handlers.Base.extend({ 4 currentConfig: {}, 5 debouncedReactivate: null, 6 bindEvents() { 7 elementorFrontend.addListenerOnce(this.getUniqueHandlerID() + 'sticky', 'resize', this.reactivateOnResize); 8 }, 9 unbindEvents() { 10 elementorFrontend.removeListeners(this.getUniqueHandlerID() + 'sticky', 'resize', this.reactivateOnResize); 11 }, 12 isStickyInstanceActive() { 13 return undefined !== this.$element.data('sticky'); 14 }, 15 /** 16 * Get the current active setting value for a responsive control. 17 * 18 * @param {string} setting 19 * @return {any} - Setting value. 20 */ 21 getResponsiveSetting(setting) { 22 const elementSettings = this.getElementSettings(); 23 return elementorFrontend.getCurrentDeviceSetting(elementSettings, setting); 24 }, 25 /** 26 * Return an array of settings names for responsive control (e.g. `settings`, `setting_tablet`, `setting_mobile` ). 27 * 28 * @param {string} setting 29 * @return {string[]} - List of settings. 30 */ 31 getResponsiveSettingList(setting) { 32 const breakpoints = Object.keys(elementorFrontend.config.responsive.activeBreakpoints); 33 return ['', ...breakpoints].map(suffix => { 34 return suffix ? `${setting}_${suffix}` : setting; 35 }); 36 }, 37 getConfig() { 38 const elementSettings = this.getElementSettings(), 39 stickyOptions = { 40 to: elementSettings.sticky, 41 offset: this.getResponsiveSetting('sticky_offset'), 42 effectsOffset: this.getResponsiveSetting('sticky_effects_offset'), 43 classes: { 44 sticky: 'elementor-sticky', 45 stickyActive: 'elementor-sticky--active elementor-section--handles-inside', 46 stickyEffects: 'elementor-sticky--effects', 47 spacer: 'elementor-sticky__spacer' 48 }, 49 isRTL: elementorFrontend.config.is_rtl, 50 // In edit mode, since the preview is an iframe, the scrollbar is on the left. The scrollbar width is 51 // compensated for in this case. 52 handleScrollbarWidth: elementorFrontend.isEditMode() 53 }, 54 $wpAdminBar = elementorFrontend.elements.$wpAdminBar, 55 isParentContainer = this.isContainerElement(this.$element[0]) && !this.isContainerElement(this.$element[0].parentElement); 56 if ($wpAdminBar.length && 'top' === elementSettings.sticky && 'fixed' === $wpAdminBar.css('position')) { 57 stickyOptions.offset += $wpAdminBar.height(); 58 } 59 60 // The `stickyOptions.parent` value should only be applied to inner elements, and not to top level containers. 61 if (elementSettings.sticky_parent && !isParentContainer) { 62 // TODO: The e-container classes should be removed in the next update. 63 stickyOptions.parent = '.e-container, .e-container__inner, .e-con, .e-con-inner, .elementor-widget-wrap'; 64 } 65 return stickyOptions; 66 }, 67 activate() { 68 this.currentConfig = this.getConfig(); 69 this.$element.sticky(this.currentConfig); 70 }, 71 deactivate() { 72 if (!this.isStickyInstanceActive()) { 73 return; 74 } 75 this.$element.sticky('destroy'); 76 }, 77 run(refresh) { 78 if (!this.getElementSettings('sticky')) { 79 this.deactivate(); 80 return; 81 } 82 var currentDeviceMode = elementorFrontend.getCurrentDeviceMode(), 83 activeDevices = this.getElementSettings('sticky_on'); 84 if (-1 !== activeDevices.indexOf(currentDeviceMode)) { 85 if (true === refresh) { 86 this.reactivate(); 87 } else if (!this.isStickyInstanceActive()) { 88 this.activate(); 89 } 90 } else { 91 this.deactivate(); 92 } 93 }, 94 /** 95 * Reactivate the sticky instance on resize only if the new sticky config is different from the current active one, 96 * in order to avoid re-initializing the sticky when not needed, and avoid layout shifts. 97 * The config can be different between devices, so this need to be checked on each screen resize to make sure that 98 * the current screen size uses the appropriate Sticky config. 99 * 100 * @return {void} 101 */ 102 reactivateOnResize() { 103 clearTimeout(this.debouncedReactivate); 104 this.debouncedReactivate = setTimeout(() => { 105 const config = this.getConfig(), 106 isDifferentConfig = JSON.stringify(config) !== JSON.stringify(this.currentConfig); 107 if (isDifferentConfig) { 108 this.run(true); 109 } 110 }, 300); 111 }, 112 reactivate() { 113 this.deactivate(); 114 this.activate(); 115 },
116 onElementChange(settingKey) { 117 if (-1 !== ['sticky', 'sticky_on'].indexOf(settingKey)) { 118 this.run(true); 119 } 120 121 // Settings that trigger a re-activation when changed. 122 const settings = [...this.getResponsiveSettingList('sticky_offset'), ...this.getResponsiveSettingList('sticky_effects_offset'), 'sticky_parent']; 123 if (-1 !== settings.indexOf(settingKey)) { 124 this.reactivate(); 125 } 126 }, 127 /** 128 * Listen to device mode changes and re-initialize the sticky. 129 * 130 * @return {void} 131 */ 132 onDeviceModeChange() { 133 // Wait for the call stack to be empty. 134 // The `run` function requests the current device mode from the CSS so it's not ready immediately. 135 // (need to wait for the `deviceMode` event to change the CSS). 136 // See `elementorFrontend.getCurrentDeviceMode()` for reference. 137 setTimeout(() => this.run(true)); 138 }, 139 onInit() { 140 elementorModules.frontend.handlers.Base.prototype.onInit.apply(this, arguments); 141 if (elementorFrontend.isEditMode()) { 142 elementor.listenTo(elementor.channels.deviceMode, 'change', () => this.onDeviceModeChange()); 143 } 144 this.run(); 145 }, 146 onDestroy() { 147 elementorModules.frontend.handlers.Base.prototype.onDestroy.apply(this, arguments); 148 this.deactivate(); 149 }, 150 /** 151 * 152 * @param {HTMLElement|null|undefined} element 153 * @return {boolean} Is the passed element a container. 154 */ 155 isContainerElement(element) { 156 const containerClasses = [ 157 // TODO: The e-container classes should be removed in the next update. 158 'e-container', 'e-container__inner', 'e-con', 'e-con-inner']; 159 return containerClasses.some(containerClass => { 160 return element?.classList.contains(containerClass); 161 }); 162 } 163 }); 164 165 166 167 $( window ).on( 'elementor/frontend/init', () => { 168 const addHandler = ( $element ) => { 169 elementorFrontend.elementsHandler.addHandler( _sticky, { 170 $element, 171 } ); 172 }; 173 174 elementorFrontend.hooks.addAction( 'frontend/element_ready/section', addHandler ); 175 elementorFrontend.hooks.addAction( 'frontend/element_ready/container', addHandler ); 176 elementorFrontend.hooks.addAction( 'frontend/element_ready/widget', addHandler ); 177 } ); 178 179}(jQuery, window.elementorFrontend, window.elementorModules));
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.