В народ... Код (Text): <!-- AJAX Implementation by Vladimir S. Bredihin <vb dog php dot ru> Based on Subsys_JsHttpRequest_Php: PHP backend for JavaScript DHTML loader. (C) 2005 Dmitry Koterov, http://forum.dklab.ru/users/DmitryKoterov/ Adaptation by 440hz < 440hz dog php dot ru> 2006 (c) Expi-Web --> <div id="pu_search" class="pu_search"></div> <div id='pu_search_description' class="pu_search_decription"></div> <input type=text style="width:100%; padding-left: 3px" maxlength="200" name=QS class=in id="query" autocomplete="off"> Javascript, с учетом что количество блоков может быть увеличено... Код (Text): /** * Vladimir S. Bredihin <vb dog php.ru> 2006-08-27 * * 2006-08-29 - 440hz - adapting design colors and height block * 2006-08-31 - vb - Version 1.1. Added description, onkeydown, * debug onclick hidden. * 2006-09-08 - vb - Version 1.2. Optimize scripts. Debug debug onclick for browser Opera * */ var pu_search; var obj_id = 'pu_search'; var objs = new Array (); var ajax = '/ajax/pu_search.php'; var timeout = null; var timeout_descr = null; function show_help() { var query = '' + document.getElementById('query').value; var req = new Subsys_JsHttpRequest_Js(); req.onreadystatechange = function() { if (req.readyState == 4) { if (req.responseJS) { // comment: name pu_search == popup_search. var data = ''; var tmp = objs[0].row_from + objs[0].row_limit; var count_items = req.responseJS.fnc_names.length; var obj = document.getElementById(obj_id); objs[0].clear(); var started = false; for (var i = 0; i < count_items; i++) { objs[0].add(req.responseJS.fnc_names[i], req.responseJS.fnc_names[i], req.responseJS.fnc_descriptions[i]); if (!started && tmp <= i) {objs[0].show();started = true;} } if (!started) objs[0].show (); } } } if (query!='') { req.caching = true; req.open('POST', ajax, true); req.send({ q: query }); } else objs[0].hide(); clearTimeout(timeout); } function load_active_description (){ var req = new Subsys_JsHttpRequest_Js(); req.onreadystatechange = function() { if (req.readyState == 4) { if (req.responseJS) { objs[0].items[objs[0].active].description = req.responseJS.fnc_description; clearTimeout(timeout_descr); objs[0].items[objs[0].active].descr_canload = 0; puShowDescription0(); } } } req.caching = true; req.open('POST', ajax, true); req.send({ l: objs[0].items[objs[0].active].label }); } function puLoadUp() { if (timeout) clearTimeout(timeout); timeout = setTimeout(show_help, 1000); } function puSubmit(){ if (document.getElementById(obj_id).style.display == 'block') return false; else return true; } onload = function(){ objs[0] = new puClass (0, 'pu_search', 'query'); obj0 = document.getElementById('query'); obj0.onkeyup= function(Key){ if (Key) event = Key; if (event.keyCode == 13) { var tmp = obj0.value; //special for IE var visible = objs[0].isShowed(); if (visible) puSelected (0,-1); puHide(0); if (!visible) obj0.value = tmp; //special for IE } else if (event.keyCode == 27) puHide (0); else if (event.keyCode == 38 || event.keyCode == 40) puTimeoutDescription0(); else if (event.keyCode != 38 && event.keyCode != 40 && event.keyCode != 37 && event.keyCode != 39 //arrows && event.keyCode != 9 && event.keyCode != 16 && event.keyCode != 17 && event.keyCode != 18 //shift,ctrl,alt,tab && event.keyCode != 35 && event.keyCode != 36) //end, home { objs[0].row_from = 0; objs[0].row_limit = 15; puLoadUp(); } } obj0.onkeydown = function (Key){ if (Key) event = Key; if (event.keyCode == 38) { puPrev(0, -1); } else if (event.keyCode == 40) { puNext(0, -1); } if (timeout_descr) clearTimeout(timeout_descr); } } onclick = function (){ var i for ( i = 0; i < objs.length; i++) objs[i].hide(); } puItemClass = function (id_obj, label, value, description, number){ var active; var label; var value; var number; var id_obj; var li_style; var description; var descr_canload; this.id_obj = id_obj; this.label = label; this.value = value; if (description && description != 'Y') this.description = description; else this.descr_canload = 1; this.number = number; this.active = 0; this.li_style =''; this.show = function (){ var noactive; if (this.active == 0) noactive = "this.className=\"pu_noactive\""; return "<li" +" onclick=\"puSelected('"+this.id_obj+"','"+this.number+"')\"" +" onmouseout='"+noactive+"' " +" onmouseover='this.className=\"pu_active\"' " +" class='"+this.li_style+"'>"+this.label+"</li>"; } this.deactivate = function(){ this.active = 0; this.li_style = "pu_noactive"; } this.activate = function(){ this.active = 1; this.li_style = "pu_active"; } } puClass = function (obj_number, id_select, id_input){ var active; var obj_number; var items; var id_select; var id_input; var row_limit; var row_from; var pause; this.active = -1; this.obj_number = obj_number; this.items = new Array (); this.id_input = id_input; this.id_select = id_select; this.add = function (label, value, description){ this.items[this.items.length] = new puItemClass (this.obj_number, label, value, description, this.items.length); if (this.active < 0) { this.active = this.items.length-1; this.items[this.active].activate(); } } this.clear = function() { this.items.length = 0; this.active=-1; } this.show = function() { document.getElementById(this.id_select).innerHTML = ''; if (this.items.length > 0) { count_items = this.items.length; var limit = this.row_limit; if (limit > count_items) limit = count_items; if (this.row_from > 0 && count_items-this.row_from < limit) this.row_from = count_items - limit; var inner = '<ul>'; for (var i = this.row_from; i < limit+this.row_from; i++) inner += this.items[i].show(); inner += '</ul>'; document.getElementById(this.id_select).innerHTML = inner; document.getElementById(this.id_select).style.display='block'; this.showDescription(); } else this.hide(); } this.hide = function(){ this.hideDescription(); document.getElementById(this.id_select).style.display='none'; } this.isShowed = function (){ if (document.getElementById(this.id_select).style.display=='block') return true; else return false; } this.showDescription = function(){ if (this.items[this.active].description) { document.getElementById(this.id_select+'_description').innerHTML = this.items[this.active].description; document.getElementById(this.id_select+'_description').style.top = document.getElementById(this.id_select).offsetTop+document.getElementById(this.id_select).offsetHeight; document.getElementById(this.id_select+'_description').style.display='block'; } } this.hideDescription = function(){ document.getElementById(this.id_select+'_description').style.display='none'; } this.selected = function (number){ if (number < 0) number = this.active; document.getElementById(this.id_input).value = this.items[number].value; } this.next = function (number){ if (number < 0) { if (this.active>=0 && this.items.length > this.active+1) { this.hideDescription(); this.items[this.active].deactivate() this.active++; this.items[this.active].activate(); if (this.row_from + this.row_limit <= this.active) this.row_from = this.active-this.row_limit+1; this.show(); } } } this.prev = function (number){ if (number < 0) { if (this.active>0) { this.hideDescription(); this.items[this.active].deactivate() this.active--; this.items[this.active].activate(); if (this.row_from > this.active) this.row_from = this.active; this.show(); } } } } function puSelected (obj_number, number){ objs[obj_number].selected(number); } function puNext (obj_number, number){ objs[obj_number].next(number); } function puPrev (obj_number, number){ objs[obj_number].prev(number); } function puHide (obj_number){ objs[obj_number].hide(); } function puShowDescription0 (){ objs[0].showDescription(); } function puTimeoutDescription0(){ if (timeout_descr) clearTimeout(timeout_descr); if (objs[0].active>=0 && objs[0].items && objs[0].items[objs[0].active].descr_canload == 1) { timeout_descr = setTimeout(load_active_description, 1000); } } PHP: <?php /** * Vladimir S. Bredihin vb <vb@php.ru> 2006-08-26 * /* 29.08.2006 - 440hz адаптировано под текущую систему 1.08.2006 - vb адаптировано под версию 1.1. (теперь помимо имени забирается описание) 8.09.2006 - vb Если количество записей запроса больше 20 (запрос не точный), то пользователю возвращаются описания только по наиболее точно написанным функциям. Добавлена обработка запроса на одно описание. */ include_once('start.inc'); require_once('./subsys_ajax.php'); $JsHttpRequest =& new Subsys_JsHttpRequest_Php("utf-8"); if (isset ($_REQUEST['q'])) { $query = addslashes($_REQUEST['q']); $type = 'all'; } if (isset ($_REQUEST['l'])) { $query = addslashes($_REQUEST['l']); $type = 'onedescr'; } $result = array (); if(!empty($query)) { $result = array ('names'=>array (), 'descriptions'=>array ()); switch($query) { case 'жопа': $result['names'][] = ''; $result['names'][] = 'не ... такое не ищем © php.ru'; $result['names'][] = ''; break; default: if ($type == 'all') { $desc_limit = strlen($query)+2; //Если данных очень много то будем выводить информацию только //по тем функциям, которые написаны/написаны без нескольких символов $query = str_replace ('_', '\_', $query); //Экранируем _, чтобы mysql искал этот символ $where = "name LIKE '{$query}%'"; } else //onedescr $where = "name = '{$query}' LIMIT 0,1"; $objs = $OOPSGlobal['SES']->db->QueryObjects("SELECT name, description FROM php_manual_link_search WHERE {$where}"); if ( count ($objs) < 20 ) $alldesc = true; else $alldesc = false; foreach($objs as $obj) { $result['names'][] = $obj->name; if ($alldesc || strlen($obj->name) <= $desc_limit) $result['descriptions'][] = $obj->description; elseif ($obj->description) $result['descriptions'][] = 'Y'; else $result['descriptions'][] = null; } break; } } // возврат if ($type == 'all') { $_RESULT = array( 'fnc_names' =>$result['names'], 'fnc_descriptions'=>$result['descriptions'] ); } else //onedescr { $_RESULT = array( 'fnc_description'=>@$result['descriptions'][0] ); } exit(); ?> Если у кого-то есть желание доработать, велкАм