PluginProbe
Optimole – Optimize Images | Convert WebP & AVIF | CDN & Lazy Load | Image Optimization / 4.2.7
Optimole – Optimize Images | Convert WebP & AVIF | CDN & Lazy Load | Image Optimization v4.2.7
4.2.13 4.2.12 4.2.11 4.2.10 4.2.9 4.2.8 4.2.7 4.2.6 4.2.5 2.5.5 2.5.6 2.5.7 3.0.0 3.0.1 3.1.0 3.1.1 3.1.2 3.1.3 3.10.0 3.11.0 3.11.1 3.11.2 3.11.3 3.12.0 3.12.1 All 134 releases
optimole-wp / assets / js / modules / dom-utils.js

dom-utils.js in Optimole – Optimize Images | Convert WebP & AVIF | CDN & Lazy Load | Image Optimization 4.2.7, at assets/js/modules/dom-utils.js

182 lines 5.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * Optimole DOM Utilities Module
3 * Provides DOM manipulation and element detection utilities
4 */
5
6 /**
7 * DOM utilities for element manipulation and detection
8 */
9 export const optmlDomUtils = {
10 /**
11 * Utility function for debouncing
12 * @param {Function} fn - Function to debounce
13 * @param {number} delay - Delay in milliseconds
14 * @returns {Function} Debounced function
15 */
16 debounce: function(fn, delay) {
17 let timer;
18 return function() {
19 clearTimeout(timer);
20 timer = setTimeout(() => fn.apply(this, arguments), delay);
21 };
22 },
23
24 /**
25 * Generate a unique selector for an element
26 * @param {Element} element - DOM element
27 * @returns {string} Unique CSS selector
28 */
29 getUniqueSelector: function(element) {
30 if (!element || element === document.body) return 'body';
31
32 // Use ID if available - fastest path
33 if (element.id) {
34 return `#${element.id}`;
35 }
36
37 const tag = element.tagName.toLowerCase();
38
39 // Optimize class name processing
40 let className = '';
41 if (element.className && typeof element.className === 'string') {
42 // Only process if needed
43 if (element.className.includes('optml-bg-lazyloaded')) {
44 className = '.' + element.className.trim()
45 .split(/\s+/)
46 .filter(cls => cls !== 'optml-bg-lazyloaded')
47 .join('.');
48 } else {
49 // Avoid unnecessary split/filter/join when no filtering needed
50 className = '.' + element.className.trim().replace(/\s+/g, '.');
51 }
52 }
53
54 // Get parent selector - but limit recursion depth for performance
55 const parentElement = element.parentElement;
56 if (!parentElement || parentElement === document.body) {
57 return `body > ${tag}${className}`;
58 }
59
60 // Optimize sibling calculation - only do this work if necessary
61 let nthTypeSelector = '';
62 const siblings = parentElement.children;
63 let siblingCount = 0;
64 let position = 0;
65
66 for (let i = 0; i < siblings.length; i++) {
67 if (siblings[i].tagName === element.tagName) {
68 siblingCount++;
69 if (siblings[i] === element) {
70 position = siblingCount;
71 }
72 }
73 }
74
75 if (siblingCount > 1) {
76 nthTypeSelector = `:nth-of-type(${position})`;
77 }
78
79 // Limit recursion depth to avoid performance issues with deeply nested DOM
80 // Use a simpler parent selector if we're already several levels deep
81 const parentSelector = parentElement.id ?
82 `#${parentElement.id}` :
83 this.getUniqueSelector(parentElement);
84
85 return `${parentSelector} > ${tag}${className}${nthTypeSelector}`;
86 },
87
88 /**
89 * Check if an element has a background image
90 * @param {Element} element - DOM element to check
91 * @param {boolean} returnUrl - Whether to return the URL or just boolean
92 * @returns {boolean|string} True/false or background image URL
93 */
94 hasBackgroundImage: function(element, returnUrl = false) {
95 // Use getComputedStyle for accurate results, but only once per element
96 const style = window.getComputedStyle(element);
97 const bgImage = style.backgroundImage;
98
99 // Check if the background image is a URL (not 'none')
100 return (bgImage && bgImage !== 'none' && bgImage.includes('url(')) ?
101 (returnUrl ? bgImage : true) : false;
102 },
103
104 /**
105 * Extract URLs from background-image CSS property
106 * @param {string} bgImage - Background image CSS value
107 * @returns {Array|null} Array of URLs or null
108 */
109 extractUrlsFromBgImage: function(bgImage) {
110 if (!bgImage) return null;
111
112 const urls = [];
113 const regex = /url\(['"]?(.*?)['"]?\)/g;
114 let match;
115
116 while ((match = regex.exec(bgImage)) !== null) {
117 if (match[1]) urls.push(match[1]);
118 }
119
120 return urls.length > 0 ? urls : null;
121 },
122
123 /**
124 * Check viewport and page visibility conditions
125 * @returns {Object} Object with validity checks
126 */
127 checkPageConditions: function() {
128 return {
129 hasValidViewport: window.innerWidth > 0 && window.innerHeight > 0,
130 isVisible: document.visibilityState !== 'hidden' || document.prerendering,
131 isComplete: document.readyState === 'complete'
132 };
133 },
134
135 /**
136 * Wait for page to be fully loaded
137 * @returns {Promise} Promise that resolves when page is loaded
138 */
139 waitForPageLoad: function() {
140 if (document.readyState === 'complete') {
141 return Promise.resolve();
142 }
143
144 return new Promise(resolve => {
145 window.addEventListener('load', resolve, { once: true });
146 });
147 },
148
149 /**
150 * Wait for browser idle time
151 * @returns {Promise} Promise that resolves during idle time
152 */
153 waitForIdleTime: function() {
154 return new Promise(resolve => {
155 if (typeof requestIdleCallback === 'function') {
156 requestIdleCallback(resolve);
157 } else {
158 setTimeout(resolve, 200);
159 }
160 });
161 },
162
163 /**
164 * Wait for images in viewport to load
165 * @param {number} delay - Additional delay in milliseconds (default: 1000)
166 * @returns {Promise} Promise that resolves after images are loaded
167 */
168 waitForViewportImages: function(delay = 1000) {
169 return new Promise(resolve => {
170 // Wait for page load first
171 if (document.readyState !== 'complete') {
172 window.addEventListener('load', () => {
173 setTimeout(resolve, delay);
174 }, { once: true });
175 } else {
176 // Page is already loaded, add delay for images to load
177 setTimeout(resolve, delay);
178 }
179 });
180 }
181 };
182