JavaScript: Select With Filtration

Solution

 
function escapeRegExp(string){
	return string.replace(/([.*+?^${}()|\[\]\/\\])/g, "\\$1");
}
 
function hideSelect(selectObj, bHide)
{
	selectObj.disabled = bHide;
	selectObj.style.display = bHide ? "none" : "block";
}
 
function filterSelect(fullSelect, filterInput) {
	filterSelect = fullSelect.cloneNode(false); 	// Make a shallow copy
	hideSelect(filterSelect, true);
	fullSelect.parentNode.insertBefore(filterSelect, fullSelect);
	var filterValue = "";
 
	setInterval(function(){
		if (filterInput.value == filterValue)
			return;	
 
		filterValue = filterInput.value;
 
		var isFilterMode = !!filterValue;
		hideSelect(fullSelect, isFilterMode);
		hideSelect(filterSelect, !isFilterMode);
 
		if (!isFilterMode) 
			return;
 
		try {
			var reFilter = new RegExp("<option [^>]*>[^<]*?" + escapeRegExp(filterValue) + "[^<]*?<\/option>", "ig");
			var matchedOptions = fullSelect.innerHTML.match(reFilter);
		  	filterSelect.innerHTML = matchedOptions ? matchedOptions.join("") : "";
		}
		catch (e) {
			filterSelect.innerHTML = "<option>error</option>";
		}
 
	 }, 500);
}

Explanation

A SELECT tag with hundreds options deserves some filtration. Let’s have an extra INPUT to filter the SELECT options by label while the user is typing in. The problem: in Internet Explorer, changing 1000+ SELECT options on the fly is extremely slow; you may use this test in different browsers to make sure and get better familiar with the issue. The most surprising thing is that adding numerous options takes less time than their subsequent deletion. The proposed idea was the best I could have found, but in my case (20,000+ items) it was still slow (seconds).
My solution is in adding an extra SELECT to show only the filtered options. When a user opens a page, he sees a “full” SELECT. Then the user types a letter into the filter INPUT, the “full” SELECT disappears, while the new “filtered” SELECT gets shown and keeps working out the next filtration characters. As the filtration input gets empty, the “filtered” SELECT gets invisible, while the “full” one returns to the screen. This trick saves time on the 2 slowest actions: starting filtration on the full set and restoring that full set.
This implementation is cross-browser and uses only pure JavaScript without modern poorly supported features. No jQuery or other JavaScript extensions required, that is, no extra overhead.

Notes on implementation

1. Usage: call filterSelect(fullSelect, filterInput) , where both parameters are JavaScript objects, the SELECT with all the options and the input for filtration strings, respectively.
2. Disabling a hidden SELECT is required to submit only the visible one. Both SELECT-s bear the same name, so no need to update the form’s destination code.
3. The setInterval function works out all the possible ways of entering the filtration criteria: typing, pasting, clearing… Another purpose of this function is to skip extra work at quick typing.
4. To minimize page redrawing, the deletion of the old stuff and adding the newly filtered items get performed at one step: updating the innerHTML, which is prepared via the regular expression.
5. Although I escape the user input for the regular expression, nobody knows what users can enter; the try/catch block guards against improper characters, you are free to customize error handling.

Happy coding!