1

Тема: JS: Editable ComboBox

С праздником, мужики!

Обычно в поле с комбобоксом можно вводить данные. Всеми это воспринимается как само собой разумеющееся.
Реализация комбобокса для браузеров (с помощью <SELECT> и <OPTION>) не подразумевает ввод текста вручную.
Ну а если оно надо? - Тогда каждый идет своим путем. Вариантов в инете - предостаточно. Вот и я предлагаю свой.

Маленький код на чистом JS без всяких фреймворков, графических элементов и новомодных изысков HTM5.
По сути к обычному объекту SELECT добавлены несколько дополнительных свойств.

Создается комбобокс командой

new ComboBox(idCell, 'idCB', '150px'); 

idCell - id элемента куда он будет вставляться;
'idCB' - строка, содержащая id будущего комбобокса;
'150px' - строка, содержащая ширину будущего комбобокса (если данный параметр отсутствует, то комбобокс займет 100% ширины родителя);

Помимо привычных свойств и методов, присущих любому элементу SELECT, в наш комбобокс добавлены еще четыре:
setText('mytext') - установка текста в поле ввода (того, что можно ввести вручную);
getText() - получение текста из поля ввода;
addOptions(array) - добавление новых пунктов в комбобокс (Добавляются только если такие пункты отсутствуют);
getOptions() - возвращает массив, содержащий список пунктов комбобокса.

Несколько замечаний:
1. Элемент в который подразумевается вставлять данный комбобокс должен иметь ширину, большую чем ширина комбобокса.
2. Данный комобокс создавался исключительно для IE (точнее - для HTA) и во всех IE6..IE11 смотрится вполне прилично. Попробовал в Chrom и Firefox - код четко работает и там, вот только дизайн - значительно хуже, но полагаю, желающие смогут это поправить.

Теперь, собственно, сам код с наглядным примером его использования:

+ открыть спойлер
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta http-equiv=MSThemeCompatible content=yes>
<style type="text/css">
	* {font:10pt verdana;}
	table {border-collapse:collapse;}
	table, td {border:1px solid green;}
	td {padding:4px 6px;}
</style>
<script type="text/javascript">
	// ComboBox с возможностью ручного ввода
	function oComboBox (oContainer, id, width) {
		var oSelect = document.createElement('SELECT');
		oSelect.id = id;
		oSelect.style.width = width || '100%';
		oSelect.style.position = 'absolute';
		var oInput = document.createElement('INPUT');
		oInput.style.position = 'relative';
		oContainer.appendChild(oSelect);
		oContainer.appendChild(oInput);
		oSelect.style.top = oInput.offsetTop + 'px';
		oSelect.style.clip = 'rect(auto auto auto ' + (oSelect.offsetWidth - 20) + 'px)';
		oInput.style.width = oSelect.offsetWidth - 19 + 'px';
		oSelect.onchange = function(){oInput.value=this.options[this.selectedIndex].text; oInput.select();};

		// Установка текста в поле ввода
		oSelect.setText = function(text) {
			oSelect.selectedIndex = -1;
			for (var j=0; j<oSelect.options.length; j++) {
				if (oSelect.options[j].text.toUpperCase() == text.toUpperCase()) oSelect.selectedIndex = j;
			}
			oInput.value = text;
		};

		// Получение текста в поле ввода
		oSelect.getText = function() {return oInput.value;};

		// Добавление новых пунктов в ComboBox (только если такие отсутствуют)
		oSelect.addOptions = function (aOptions) {
			function isOptionExist(text) {
				for (var j=0; j<oSelect.options.length; j++) {
					if (oSelect.options[j].text.toUpperCase() == text.toUpperCase()) return true;
				}
			}
			for (var i=0; i<aOptions.length; i++) {
				if (!isOptionExist(aOptions[i])) {
					var oOption = document.createElement("option");
					oOption.text = aOptions[i];
					oSelect.options.add(oOption);
					oSelect.selectedIndex = oSelect.options.length-1;
					oInput.value = oSelect.options[oSelect.options.length-1].text;
				}
			}
		};

		// Возвращает массив, содержащий список пунктов ComboBox
		oSelect.getOptions = function () {
			var aOptions = [];
			for (var j=0; j<oSelect.options.length; j++) {
				aOptions.push(oSelect.options[j].text);
			}
			return aOptions;
		};

	}

	function Sample() {
		var o = new oComboBox(idCell, 'idCB', '150px');
		idCB.addOptions(['RED', 'GREEN', 'BLUE']);
		idCB.addOptions(['yellow',  'red', 'orange']);
		idCB.setText('Blue');
	}

</script>
<title>Editable ComboBox</title>
</head>
<body onload="Sample();">
<table>
	<tr>
		<td id="idCell" style="width:200px;"></td>
		<td>new methods</td>
		<td>
			<input type="button" value="setText(text)" onclick="idCB.setText('crimson')">
			<input type="button" value="getText()" onclick="alert(idCB.getText())">
			<input type="button" value="addOptions(array)" onclick="idCB.addOptions(['Violet','Silver'])">
			<input type="button" value="getOptions()" onclick="alert(idCB.getOptions())">
		</td>
	</tr>
	<tr>
		<td>&nbsp;</td>
		<td>built-in methods</td>
		<td>
			<input type="button" value="selectedIndex" onclick="alert(idCB.selectedIndex)">
			<input type="button" value="outerHTML" onclick="alert(idCB.outerHTML)">
			<input type="button" value="change option text" onclick="idCB.options[idCB.selectedIndex].text='New Text';">
		</td>
	</tr>
</table>
</body>
</html>

Буду рад услышать любые конструктивные замечания и предложения (для того и выкладываю).