• Skip to main content
  • Skip to footer

recoveryArea

Content Management for Everyone!

  • About me
  • Blog

How To Detect User-Defined Window Globals with JavaScript (Enhanced)

19 September 2025 By Pascal Louwes

During A/B testing and conversion optimization work, third-party scripts often pollute the global window scope. This can cause conflicts, memory leaks, or unexpected behavior in your experiments. Here’s an enhanced approach to quickly identify what’s been added to the global namespace.

The Problem

When running experimentation platforms like GrowthBook, Optimizely, or Convert, you need to ensure script conflicts don’t impact your conversion metrics. The original iframe-comparison approach works, but needs better error handling and performance considerations for production environments.

Enhanced Implementation

/**
 * Enhanced Window Globals Detective
 * Identifies user-defined globals with performance tracking and error handling
 * @param {Object} options - Configuration options
 * @returns {Object} Analysis results with categorized globals
 */
const detectWindowGlobals = async (options = {}) => {
	const {
		includeNative = false,
		excludePatterns = [],
		performanceMode = true
	} = options;

	const startTime = performance.now();
	const results = {
		timing: {},
		summary: {},
		globals: {
			functions: {},
			objects: {},
			arrays: {},
			primitives: {},
			undefined: {},
			symbols: {}
		},
		excluded: [],
		errors: []
	};

	try {
		// Get baseline window properties using clean iframe
		const iframe = document.createElement('iframe');
		iframe.style.cssText = 'position:absolute;left:-9999px;top:-9999px;width:1px;height:1px;';
		
		// Defensive iframe creation
		if (!document.body) {
			throw new Error('Document body not available');
		}

		document.body.appendChild(iframe);
		
		// Wait for iframe to initialize properly
		await new Promise(resolve => {
			if (iframe.contentWindow) {
				resolve();
			} else {
				iframe.onload = resolve;
			}
		});

		const cleanWindow = iframe.contentWindow;
		if (!cleanWindow) {
			throw new Error('Failed to access iframe window');
		}

		// Compare current window against baseline
		const currentProps = Object.getOwnPropertyNames(window);
		const baselineProps = Object.getOwnPropertyNames(cleanWindow);
		
		// Filter out baseline properties and excluded patterns
		const customProps = currentProps.filter(prop => 
			!baselineProps.includes(prop) && 
			!excludePatterns.some(pattern => 
				new RegExp(pattern).test(prop)
			)
		);

		// Categorize properties with error handling for inaccessible properties
		customProps.forEach(prop => {
			try {
				const value = window[prop];
				const type = typeof value;

				if (value === null) {
					results.globals.primitives[prop] = null;
				} else if (value === undefined) {
					results.globals.undefined[prop] = undefined;
				} else if (type === 'symbol') {
					results.globals.symbols[prop] = value.toString();
				} else if (type === 'function') {
					results.globals.functions[prop] = {
						name: value.name || 'anonymous',
						length: value.length,
						source: performanceMode ? '[Function]' : value.toString().slice(0, 100) + '...'
					};
				} else if (type === 'object') {
					if (Array.isArray(value)) {
						results.globals.arrays[prop] = {
							length: value.length,
							preview: performanceMode ? '[Array]' : value.slice(0, 3)
						};
					} else {
						const keys = Object.keys(value);
						results.globals.objects[prop] = {
							constructor: value.constructor?.name || 'Unknown',
							keys: performanceMode ? keys.length : keys.slice(0, 5),
							preview: performanceMode ? '[Object]' : value
						};
					}
				} else {
					results.globals.primitives[prop] = value;
				}
			} catch (error) {
				results.errors.push({ property: prop, error: error.message });
				results.excluded.push(prop);
			}
		});

		// Generate summary statistics
		results.summary = {
			totalWindowProps: currentProps.length,
			baselineProps: baselineProps.length,
			customProps: customProps.length,
			functions: Object.keys(results.globals.functions).length,
			objects: Object.keys(results.globals.objects).length,
			arrays: Object.keys(results.globals.arrays).length,
			primitives: Object.keys(results.globals.primitives).length,
			errors: results.errors.length
		};

		// Clean up iframe
		iframe.remove();

	} catch (error) {
		results.errors.push({ global: true, error: error.message });
	}

	results.timing.total = performance.now() - startTime;
	return results;
};

// Usage examples
(async () => {
	// Basic usage
	const analysis = await detectWindowGlobals();
	console.log('Custom globals detected:', analysis);

	// With exclusion patterns (useful for known safe scripts)
	const filtered = await detectWindowGlobals({
		excludePatterns: ['^gtag', '^ga', '^dataLayer', '^_gaq']
	});
	console.log('Filtered analysis:', filtered);

	// Performance mode off for detailed inspection
	const detailed = await detectWindowGlobals({ performanceMode: false });
	console.log('Detailed analysis:', detailed);
})();

Key Improvements

Async/Await Implementation: Properly handles iframe initialization instead of assuming immediate availability.

Error Boundaries: Won’t crash when encountering inaccessible properties (common with certain third-party scripts).

Performance Tracking: Measures execution time to identify potential performance impacts.

Memory Efficiency: Performance mode prevents serialization of large objects that could slow down analysis.

Pattern Exclusion: Filter out known safe globals (analytics scripts, etc.) to focus on potential problems.

Categorized Output: Groups results by type for easier debugging and analysis.

Real-World Application

This enhanced version is particularly useful when:

  • Debugging script conflicts in A/B testing platforms
  • Auditing third-party script impact on page performance
  • Identifying memory leaks during long-running experiments
  • Ensuring clean experimentation environments across different page types

The categorized output makes it easy to spot problematic patterns—like multiple conflicting analytics libraries or memory-intensive objects that could impact conversion tracking accuracy.

Happy debugging! 🔍

Filed Under: JavaScript

Pascal Louwes

Me
Creative, resourceful and innovative Front End Developer / User Experience Optimizer. I love to make stuff work, perform and convert better.

Buzzwords
JavaScript, jQuery, User Experience & Conversion Optimization, Web Strategy, Interaction Design, Accessibility, Wordpress Design, Development & Optimization. check out my full profile

Footer

Office

recoveryArea
Nijverheidstraat 11-1
7511 JM Enschede
The Netherlands

KvK: 65594940

Mission

Building a more elegant, accessible internet through precise experimentation and performance engineering. Moving toward an empathic, sustainable future where technology genuinely serves people.

Read my full profile

Get in touch

  • GitHub
  • LinkedIn
  • Twitter

Copyright © 2026 · From recoveryArea with