YUI: autocomplete

Что такое AJAX, наверное, уже знают все. (кто не знает, идет гуглить), А что такое YUI я рассказывал в прошлой статье

Итак, автокомплит это технология, по которой данные взятые, например из аджакса (это совершенно не обязательно) предлагаются пользователю для быстрого заполнения полей формы. Например, пользователь пишет в поле «город» несколько букв — «Сан» ему будут предложены варианты «Санкт-Петербург», «Сан-Франциско» и «Сан Ремо» и пользователь быстро может выбрать мышкой или с клавиатуры то что ему нравится.

Проблема один — AJAX. Надо писать frontend (javascript) приложение, для клиента основанное на классе JsHttpRequest и backend (например, на php) приложение с запросом к БД

Проблема два нарисовать красивый выпадающий список с навигацией с клавиатуры. Проблема собственно у ослика (как обычно) потому что он не обрабатывает нажатие стрелочек в отличие от всех других браузеров.

Кстати, как и в прошлой статье, я отнюдь не говорю что это либа это панацея всех проблем! например, она Grupo очень тяжелая. Но раз я уже начал писать, а вы читать… продолжим

подключаем либу


<script type="text/javascript" src="lib/yahoo/yahoo/yahoo.js"></script>
<script type="text/javascript" src="lib/yahoo/dom/dom.js"></script>
<script type="text/javascript" src="lib/yahoo/event/event.js"></script>
<script type="text/javascript" src="lib/yahoo/connection/connection.js"></script>
<script type="text/javascript" src="lib/yahoo/animation/animation.js"></script>
<script type="text/javascript" src="lib/yahoo/autocomplete/autocomplete.js"></script>

и css


#ysearchmod {position:relative;padding:1em;}
#firstname_root {z-index:3000;} /* for IE z-index of absolute divs inside relative divs issue */
#secondname_root {z-index:2000;} /* for IE z-index of absolute divs inside relative divs issue */
#lastname_root {z-index:1000;} /* for IE z-index  Branding  of absolute divs inside relative divs issue */
.yui-ac-content {position:absolute;width:100%;border:1px  cheap mlb jerseys  solid #404040;background:#fff;overflow:hidden;z-index:9050;}
.ysearchquery {position:absolute;right:10px;color:#808080;z-index:10;}
.yui-ac-shadow {position:absolute;margin:.3em;width:100%;background:#a0a0a0;z-index:9049;}
ul {padding:5px 0;width:100%;}
li {padding:0 5px;cursor:default;white-space:nowrap;}
li.yui-ac-highlight {background:#a0a0a0;}
li.yui-ac-prehighlight {background:pink;}

Для тех кто заметил что этот css сильно отличается от предлагаемого YAHOO объясняю — так как они описывают каждый элемент уже при трех полях на ajaxe css станет пухлым и дохлым поэтому я вынес его в JavaScript

И еще на заметку я буду предлагать заполнять ФИО

Последний штрих рисуем HTML


<div id="firstname_root"><input type="text" name="firstname" id="firstname" value=""><div id="firstname_conteiner"></div><div>
<div id="secondname_root"><input type="text" name="secondname" id="secondname" value=""><div id="secondname_conteiner"><div id="firstname_conteiner"></div><div>
<div id="lastname_root"><input type="text" name="lastname" id="lastname" value=""><div id="firstname_conteiner"><div id="lastname_conteiner"></div><div>

А теперь в тегах <_script>…<_/script> описываем загрузчик, он же frontend приложение


// определяем именное пространство
YAHOO.namespace("AutoComplete");
/*
Грубо говоря если вы определите YAHOO.namespace("XXX");
то следующая строчка будет выглядеть так
YAHOO.XXX = function(input){
*/

// конструктор
YAHOO.AutoComplete = function(input){
var oACDS;
var oAutoComp;
var oDiv = null;

	// CSS, вот сюда то я и вынес все излишнее свойства
	if (oDiv=document.getElementById(input))
	{
		var rDiv = document.getElementById(input+'_root');
		var cDiv = document.getElementById(input+'_conteiner');
		oDiv.style.position = "absolute";
		oDiv.style.width = "100%";
		rDiv.style.height = "2em";
		rDiv.style.position = "relative";
		rDiv.style.margin_bottom = "1.5em";
		rDiv.style.width = "100%";
		cDiv.style.position = "absolute";
		cDiv.style.top = "1.7em";
		cDiv.style.width = "100%";
	}


    // заполняем первый объект oACDS, он у нас  ???????  отвечает за AJAX
    // адресс backend'a с параметрами
    oACDS = new YAHOO.widget.DS_XHR("/backend.php", ["n", "t"]);
    // вид ответа - простой текст - по  cheap mlb jerseys  одному варианту на строку
    oACDS.responseType = YAHOO.widget.DS_XHR.TYPE_FLAT;
    // кеширование
   oACDS.queryMatchSubset = true;
    // время кеширования 60 сек
    oACDS.maxCacheEntries = 60;

    // этот параметр отвечает за дополнительные параметры в  cheap jerseys  url, который запрашивается
    //тоесть вместо /backend.php данные будут посланы на /backend.php?active=имя_поля
    oACDS.scriptQueryAppend = '&active=' + input;

  wholesale nba jerseys     //заполняем второй объект отвечающий за autocomplete, ему по большому счету нужно только имя поля в
    //котором должен работать  PHP  скрипт
    oAutoComp = new YAHOO.widget.AutoComplete(input,input+'_conteiner', oACDS);
    // разделитель (по умолчанию дописывает в конце выбранного варианта ';')
    oAutoComp.delimChar = "";
    // задержка в секундах до отправки запроса
    oAutoComp.queryDelay = 0;
    // минимальная длина строки перед поиском
    oAutoComp.minQueryLength=1;
    // имя класса для выделенного варианта ответа
     wholesale jerseys  oAutoComp.prehighlightClassName = "yui-ac-prehighlight";
    // отключает автокомплит в браузере чтоб он не мешал
    oAutoComp.allowBrowserAutocomplete = false;
    // можно еще поизвращаться с анимацией, но это вы сделаете сами

};
//ну и собственно стартуем скрипт по разу на каждое поле
YAHOO.AutoComplete('firstname')
Теперь backend надо написать, да?!

// я полностью писать не буду, просто объясню. Данные из аджакса приходят в кодировке UCS-2BE поэтому 
// ее надо перекодировать в win-1251

// таблица символов
$unicode_to_cp1251_tbl = array(
	0x0402 => "x80",	0x0403 => "x81",	0x201A => "x82",	0x0453 => "x83",
	0x201E => "x84",	0x2026 => "x85",	0x2020 => "x86",	0x2021 => "x87",
	0x20AC => "x88",	0x2030 => "x89",	0x0409 => "x8A",	0x2039 => "x8B",
	0x040A => "x8C",	0x040C => "x8D",	0x040B => "x8E",	0x040F => "x8F",
	0x0452 => "x90",	0x2018 => "x91",	0x2019 => "x92",	0x201C => "x93",
	0x201D => "x94",	0x2022 => "x95",	0x2013 => "x96",	0x2014 => "x97",
	0x2122 => "x99",	0x0459 => "x9A",	0x203A => "x9B",	0x045A => "x9C",
	0x045C => "x9D",	0x045B => "x9E",	0x045F => "x9F",	0x00A0 => "xA0",
	0x040E => "xA1",	0x045E => "xA2",	0x0408 => "xA3",	0x00A4 => "xA4",
	0x0490 => "xA5",	0x00A6 => "xA6",	0x00A7 => "xA7",	0x0401 => "xA8",
	0x00A9 => "xA9",	0x0404 => "xAA",	0x00AB => "xAB",	0x00AC => "xAC",
	0x00AD => "xAD",	0x00AE => "xAE",	0x0407 => "xAF",	0x00B0 => "xB0",
	0x00B1 => "xB1",	0x0406 => "xB2",	0x0456 => "xB3",	0x0491 => "xB4",
	0x00B5 => "xB5",	0x00B6 => "xB6",	0x00B7 => "xB7",	0x0451 => "xB8",
	0x2116 => "xB9",	0x0454 => "xBA",	0x00BB => "xBB",	0x0458 => "xBC",
	0x0405 => "xBD",	0x0455 => "xBE",	0x0457 => "xBF",	0x0410 => "xC0",
	0x0411 => "xC1",	0x0412 =>  чайников!  "xC2",	0x0413 => "xC3",	0x0414 => "xC4",
	0x0415 => "xC5",	0x0416 => "xC6",	0x0417 => "xC7",	0x0418 => "xC8",
	0x0419 => "xC9",	0x041A => "xCA",	0x041B => "xCB",	0x041C => "xCC",
	0x041D => "xCD",	0x041E => "xCE",	0x041F => "xCF",	0x0420 => "xD0",
	0x0421 => "xD1",	0x0422 => "xD2",	0x0423 => "xD3",	0x0424 => "xD4",
	0x0425 => "xD5",	0x0426 => "xD6",	0x0427 => "xD7",	0x0428 => "xD8",
	0x0429 => "xD9",	0x042A => "xDA",	0x042B => "xDB",	0x042C => "xDC",
	0x042D => "xDD",	0x042E => "xDE",	0x042F => "xDF",	0x0430 => "xE0",
	0x0431 => "xE1",	0x0432 => "xE2",	0x0433 => "xE3",	0x0434 => "xE4",
	0x0435 => "xE5",	0x0436 => "xE6",	0x0437 => "xE7",	0x0438 => "xE8",
	0x0439 => "xE9",	0x043A => "xEA",	0x043B => "xEB",	0x043C => "xEC",
	0x043D => "xED",	0x043E => "xEE",	0x043F => "xEF",	0x0440 => "xF0",
	0x0441 => "xF1",	0x0442 => "xF2",	0x0443 => "xF3",	0x0444 => "xF4",
	0x0445 => "xF5",	0x0446 => "xF6",	0x0447 => "xF7",	0x0448 => "xF8",
	0x0449 => "xF9",	0x044A => "xFA",	0x044B => "xFB",	0x044C => "xFC",
	0x044D => "xFD",	0x044E => "xFE",	0x044F => "xFF",
);

// функция поиска по таблице
function utf8_to_cp1251($s) {
	$tbl = $GLOBALS['unicode_to_cp1251_tbl'];
	$uc = 0;
	$bits = 0;
	$r = "";
	for($i = 0, $l = strlen($s); $i < $l; $i++)
	{
		$c = $s{$i};
		$b = ord($c);
		if($b & 0x80) 
		{
			if($b & 0x40)
			{
				if($b & 0x20) 
				{
					$uc = ($b & 0x0F) << 12;
					$bits =  calendar  12;
				} else {
					$uc = ($b & 0x1F) << 6;
					$bits = 6;
				}
			} else {
				$bits -= 6;
				if($bits)
				{
					$uc |= ($b & 0x3F) << $bits;
				} else {
					$uc |= $b & 0x3F;
					if($cc = @$tbl[$uc]) 
					{
						$r .= $cc;
					} else {
						$r .= '?';
					}
				}
			}
		} else {
			$r .= $c;
		}
	}
	return $r;
}

// применяем функцию ко всем входящим данным
foreach ($_REQUEST as $key => $value){
    $_REQUEST[$key] = utf8_to_cp1251($value);
}

// из всех данный нужно только 2
$like = $_REQUEST['query'];
$field = $_REQUEST['active'];

// соединяемся с БД
mysql_connect('localhost','user','pass');
mysql_select_db('namedatabase');

//Ну и собственно спрашиваем есть ли похожие имена
$sql = mysql_query("SELECT ".$field." AS suggestion FROM table_name WHERE ".$field." LIKE '$like%' ORDER BY ".$field."LIMIT 50");

// выводим списком ответ
while($result = mysql_fetch_assoc($sql)){
    echo $result['suggestion']."n";
}

Вот, пожалуй, и все. Кстати очень тяжелая (много килобайт весит) либа. Subsys от Котерова намного легче и практичнее, но не имеет автокомплита. Попытки втиснуть его туда насильно увенчались успехом только для FF1.5 и Opera8.0 , IE ругался