Search code examples
javascriptinternet-explorercross-browserlegacy

IE11 Object doesn't support property or method 'indexOf' (but it's not an object and works in other browsers)


I have a script that iterates through values passed in an array and checks whether those items are natively supported by a browser. This works fine in Chrome, Edge, FireFox and Safari, but IE11 and older (the browsers that really need this to work) throws an error. The array values are strings (as can be seen below) but for some reason, IE11 is treating them as objects, which means when I try to check for a . in the string, IE is erroring out.

I have tried converting the array item toString, but that just results in object object or object undefined.

I have setup a CodePen which contains the full set of code: https://codepen.io/willstocks_tech/pen/YBEzLW?editors=1012

The version that can be used in IE is: https://s.codepen.io/willstocks_tech/debug/YBEzLW/mWAoNxdogGvr (although this might expire!)

//https://github.com/willstocks-tech/dynamically-polyfill-features-for-a-script/releases/
dynamicPolyfill(
	['IntersectionObserver', 'IntersectionObserverEntry', 'Object.assign', 'Array.copyWithin']
	,'https://cdn.jsdelivr.net/npm/[email protected]/dist/quicklink.umd.js' 
	,'quicklink()' 
)

function dynamicPolyfill(tocheck, scriptToPolyfill, functionToRunonLoad) {
	if(typeof Promise !== "undefined"){
		var checkPromises = [];
		if(Array.isArray(tocheck)) {
			tocheck.forEach(
				function(tocheck){
					checkPromises.push(checking(tocheck))
				}
			)
		} else {
			checkPromises.push(checking(tocheck))
		}
		checkPromises = checkPromises.filter(function(p){return p !== undefined}).join(' ');
		loadPolyfill(checkPromises, scriptToPolyfill, functionToRunonLoad)
	} else {
		promiFill(tocheck, scriptToPolyfill, functionToRunonLoad);
	}
}

function promiFill(tocheck, scriptToPolyfill, functionToRunonLoad){
		var promPolyfill = document.createElement('script'); 
		promPolyfill.src = ('https://polyfill.io/v3/polyfill.min.js?features=Promise'); 
		document.body.appendChild(promPolyfill); 
		promPolyfill.onerror = function(response) { 
			console.error("Polyfilling promise failed", response); 
		} 
		promPolyfill.onload = function(tocheck, scriptToPolyfill, functionToRunonLoad) {
			dynamicPolyfill(tocheck, scriptToPolyfill, functionToRunonLoad);
		}
}

function checking(check){
	var splitChars = '.';
	if ((check in window) != true || (check in this) != true) {
		if (check.indexOf(splitChars) >= 1) {
			var split = check.split('.');
			var firstWord = window[split[0]];
			var lastWord = new Object(split[split.length - 1]);
			if (lastWord in firstWord != true && lastWord in firstWord.prototype != true) {
				return check;
			}
		} else {
			return check;
		}
	}
}

function loadPolyfill(url, scriptToPolyfill, functionToRunonLoad) {
	if(url !== "") {
		var polyfill = document.createElement('script'); 
		polyfill.src = ('https://polyfill.io/v3/polyfill.min.js?features='+encodeURIComponent(url)); 
		document.body.appendChild(polyfill); 
		polyfill.onerror = function(response) { 
			console.error("Loading the polyfill(s) failed!", response); 
		} 
		polyfill.onload = function() {
			loadMyScript(scriptToPolyfill, functionToRunonLoad)
		}
	} else {
	    loadMyScript(scriptToPolyfill, functionToRunonLoad)
	}
}

function loadMyScript(url, functionToRunonLoad) {
	if(Array.isArray(url)) {
		var promises = [];
		url.forEach(
			function(url){
				promises.push(nonblankURL(url));
			}
		);
		Promise.all(promises)
			.then( 
				function() {
					initialiseMyScript(functionToRunonLoad)
				}
		).catch(function(error){return error})
	} else if (!Array.isArray(url) && url !== null && url !== '') {
		nonblankURL(url)
			.then( 
				function() {
					initialiseMyScript(functionToRunonLoad)
				}
		).catch(function(error){return error}) 
	} else {
		initialiseMyScript(functionToRunonLoad) 
	}
}

function nonblankURL(uri){
	return new Promise( 
		function(resolve, reject) { 
			var thescript = document.createElement('script');
			thescript.src = encodeURI(uri);
			document.body.appendChild(thescript);
			thescript.onerror = function(response) {
				return reject("Loading the script failed!", response);
			} 
			thescript.onload = function() {
				return resolve(uri);
			} 
		}
	)

}

function initialiseMyScript(functionToRunonLoad) {
	if(Array.isArray(functionToRunonLoad)) {
		functionToRunonLoad.forEach(
			function(functionToRunonLoad){
				initScript(functionToRunonLoad)
			}
		)
	}	else {	
		initScript(functionToRunonLoad)
	}
	function initScript(fn) {
		try {
			window[fn];
		} catch(err) {
			console.error('There was an error: ', err, err.name, err.stack);
		}
	}
}

All other browsers seem to handle this fine, but IE11 and older throw the following error: SCRIPT438: Object doesn't support property or method 'indexOf'

I can't work out for the life of me how to get this to work in IE, but also in all other browsers at the same time!


Solution

  • You can see in the IE11 debugger that tocheck is an Event object at that point (which obviously doesn't have indexOf). That's because of this code:

    promPolyfill.onload = function(tocheck, scriptToPolyfill, functionToRunonLoad) {
        dynamicPolyfill(tocheck, scriptToPolyfill, functionToRunonLoad);
    }
    

    The first and only argument a DOM event handler receives is an event object, but you're accepting it as tocheck and then calling dynamicPolyfill with that event.

    You may have meant:

    promPolyfill.onload = function() {
        dynamicPolyfill(tocheck, scriptToPolyfill, functionToRunonLoad);
    };
    

    or similar. In any case, the issue is calling indexOf on an Event object.