miércoles, 8 de junio de 2011

Menú desplegable con AJAX

Vamos a mostrar un interesante menú desplegable hecho con la técnica AJAX, y como construirlo paso a paso. Debo advertir que quizás se requiere de conocimientos básicos de programación y en especial, algo de programación en ambiente Web.
Necesitaremos los siguientes elementos:
  1. Base de datos: Usaremos MySQL como SMBD y, para este ejemplo tendremos una tabla cargada con un listado de ciudades
  2. Un script PHP que capture los datos de la base de datos según el parámetro enviado.
  3. Una Página Receptora de los resultados, en la que colocaremos la zona para mostrarlos. Además esta misma enviará la solicitud de los datos que necesita mostrar.
Veamos como será el resultado esperado. Solo comience a escribir un nombre en el campo de texto indicado. Al escribir las 3 primeras letras aparecerá un listado de los nombre en los que se encuentren estas. Por favor, intente escribir "mar..."

Nombre

Id
Documento


Paso 1. Empezamos construyendo la página donde se desencadenan todos los eventos. En nuestro ejemplo usaremos lo siguiente:
<html>
  <head>
    <title>Menu desplegable con AJAX</title>
  </head>
  <body>
    <div style="float:left;width:80px;">Nombre</div>
      <div style="float:left; width:220px;">
        <input type="text" style="width:200px;" name="nom" id="nom" value="" />
      </div>
      <div style="float:left; width:130px;">
        Id <input type="text" style="width:90px;" name="id" id="id" value="" />
      </div>
      <div style="float:left; width:190px;">
         Documento <input type="text" style="width:90px;" name="cid" id="cid" value="" />
      </div>
  </body>
</html>

Paso 2. Desarrollamos los elementos básicos para que AJAX funcione. Primero la función para crear instancias del objeto XMLHTTPRequest. En el post AJAX para principiantes - 2 encontraremos mas detalles acerca de este tema.
function nuevoAjax(){
var ajax;
  if (window.XMLHttpRequest) { // codigo para IE7+, Firefox, Chrome, Opera, Safari
    ajax=new XMLHttpRequest();
  } else { // codigo para IE6, IE5
    ajax=new ActiveXObject("Microsoft.XMLHTTP");
  }
  return ajax;
}

Paso 3. Crearemos el archivo php que se conectará a la base de datos para sacar de ella lo que necesitamos. A este archivo lo llamaremos array_listado.php, luego lo veremos mencionado en el proceso de captura de datos con Ajax en el paso 4.
<?php
$hostname = "localhost";
$database = "my_database";
$username = "my_username";
$password = "my_password";
$conexion = mysql_pconnect($hostname, $username, $password) or trigger_error(mysql_error(), E_USER_ERROR);

mysql_select_db($database, $conexion);
$sql_query = "SELECT * FROM clientes";
$cliData = mysql_query($sql_query, $conexion) or die(mysql_error());
$cliCount = mysql_num_rows($cliData);
  if ($cliCount>0) {
      $cli = mysql_fetch_assoc($cliData);
      $k = 0;
      do {
          echo $cli['id']."~".$cli['cli_nombre']."~".$cli['cli_cid']."--\n";
      } while ($cli = mysql_fetch_assoc($cliData));
  }
?>

Paso 4. Ahora crearemos una función con la que cargaremos en nuestra página un listado de nombre, id y documentos (para nuestro ejemplo).
var listado = new Array();
function cargar_array_listado() {
  var ajaxL = nuevoAjax();
  ajaxL.open("GET", "array_listado.php", true);
  ajaxL.onreadystatechange=function() {
    if (ajaxL.readyState==4) {
       var tabla = ajaxL.responseText;
       var registros = tabla.split("--");
       for (var u in registro) {
          datos = registro[u].split("~");
          listado[u]= new Object();
          listado[u]['id']=datos[0];
          listado[u]['nom']=datos[1];
          listado[u]['cid']=datos[2];
       }
     }
  }
  ajaxL.send(null);
}
Podemos darnos cuenta de que:

  • A través de la función open del objeto Ajax, ejecutamos en el servidor el archivo array_listado.php
  • Usando el método responseText del objeto Ajax, capturamos la respuesta del servidor como texto plano. Este contenido será una serie de registros separados por el doble guion "--", donde cada dato estará separado por el símbolo "~". Obviamente podemos usar el método responseXML, para lo cual deberíamos haber configurado nuestra respuesta del archivo array_listado.php como datos en formato XML.
  • Considerando que tenemos una respuesta en formato de texto plano, hacemos uso de la función split de JavaScript para obtener cada dato y construir la matriz de datos que necesitamos.

Paso 5. Agregaremos algunas funciones que nos ayudarán a que nuestra escritura en el campo de texto de como resultado que se despliegue un listado con los nombres que coincidan en cualquier parte con nuestra escritura.
function teclear(evt,campo){
    if (campo.value.length == 0) {
      if ((evt.keyCode == 9)||(evt.keyCode == 13)) {
      clear();
      }
    } else if ((campo.value.length > 0)&&(campo.value.length < 3)) {
       document.getElementById('selector').innerHTML = "";
       if ((evt.keyCode == 9)||(evt.keyCode == 13)) { // tab o enter
          clear();
       }
    } else {
       if ((evt.keyCode > 47)&&(evt.keyCode < 91)){ // cualquier letra o numero
          autoCompletar(campo);
       }
       if ((evt.keyCode == 37)||(evt.keyCode == 38)) { // flecha hacia arriba o hacia atras
          if (document.getElementById('sel').selectedIndex > 0) {
             document.getElementById('sel').selectedIndex = (document.getElementById('sel').selectedIndex)-1;
             campo.value =     document.getElementById('sel').options[document.getElementById('sel').selectedIndex].text
          }
       }
       if ((evt.keyCode == 39)||(evt.keyCode == 40)) { // flecha hacia abajo o hacia delante
            if (document.getElementById('sel').selectedIndex < (document.getElementById('sel').length-1)) {
                document.getElementById('sel').selectedIndex = (document.getElementById('sel').selectedIndex)+1;
                campo.value = document.getElementById('sel').options[document.getElementById('sel').selectedIndex].text
            }
        }
        if ((evt.keyCode == 9)||(evt.keyCode == 13)) { // tab o enter
            if (document.getElementById('sel').selectedIndex != -1) {
                var idsel = document.getElementById('sel').options[document.getElementById('sel').selectedIndex].value;
                document.getElementById('id').value = idsel;
                look(idsel);
                document.getElementById('selector').innerHTML = "";
                document.getElementById('cid').focus();
            } else {
                 document.getElementById('selector').innerHTML = "";
                 clear();
            }
        }
    }
    return false;
}

function autoCompletar(field) {
// crear selector
    var lista = "";
    var cuent = 0;
    var ubica = 0;
    var selecto = "";
    for (var k=0; k < listado.length; k++) {
        ubica = listado[k].name.toUpperCase().indexOf(field.value.toUpperCase());
        if ((ubica == 0)||(listado[k].name.toUpperCase().charCodeAt(ubica-1)==32)) {
           selecto += "<option value='" + listado[k].code + "' >" + listado[k].name + "</option>\n";
           cuent++;
        }
     }
     lista = "<select style=\"width:200px;\" name=\"sel\" id=\"sel\" onfocus = \"document.getElementById('nom').focus()\" onclick = \"document.getElementById('nom').value = this.options[this.selectedIndex].text\">\n" + selecto + "</select>\n";

// mostrar selector
    if (cuent == 0) {
        document.getElementById('selector').innerHTML = "";
        document.getElementById('re_id').value = 0;
    } else {
        document.getElementById('selector').innerHTML = lista;
        document.getElementById('sel').setAttribute('size',cuent+1);
        document.getElementById('sel').selectedIndex = -1;
    }
}

function clear() {
  document.getElementById('id').value = 0;
  document.getElementById('cid').value = '';
  document.getElementById('nom').value = '';
  document.getElementById('nom').focus();
}

function look(id) {
    for (var k=0; k<listado.length; k++) {
        if (listado[k].code == id){
            document.getElementById('cid').value = listado[k].cid;
            document.getElementById('nom').value = listado[k].name;
        }
    }
}

Paso 6. Finalmente, agregamos un par de detalles al campo de texto nombre, para que se desencadenen las funciones programadas para los eventos onFocus y onKeyUp

<input id="nom" name="nom" onfocus="cargar_array_listado();" onkeyup="teclear(event,this);" style="width: 200px;" type="text" value="" />

onFocus: Al recibir el enfoque, la matriz de datos se recargará con la lista actualizada de clientes, por lo que en un entorno multiusuario se podrán tener siempre los datos actualizados.
onKeyUp: Al soltar la pulsación de una tecla, se desencadenará la función que despliega el listado de nombres coincidentes. Igualmente en esta misma función se encuentran inmersos los procesos de selección de la lista y carga de datos del nombre seleccionado.

Por favor, no olviden comentar sus ideas para mejorar este artilugio.

2 comentarios:

  1. Muy buen aporte lo voy a intentar aplicar a un menú de categorías con subcategorías cargadas dinámicamente y te cuento.

    Gracias

    ResponderEliminar
  2. Come se ha de llamar cada archivo con su codigo, ejemplo cual es HTML cual es PHP etc

    ResponderEliminar