Como hacer un Buscador con Ajax

Como hacer un Buscador con Ajax

En este post ahora trataré de explicar como crear un buscador utlizando AJAX:

Mucho de esto, fue código que implementé hace mucho tiempo, y aunque existen mejores o mas sencillas formas de hacerlo utilizando algun framework, trataré de explicar la forma en que trabajé cuando me inicié en esto de AJAX

Antes de continuar, aquí pueden ver el resultado final o descargar el ejemplo completo:

Antes de iniciar, describiré brevemente los archivos con los cuales consiste el buscador:

  • /buscador.php Este archivo es la estructura de todo el buscador.
  • /busqueda.php Este archivo es lo mismo que buscador.php con la diferencia de que le quitamos todo, menos lo que está entre el div resultados: <HTML>...<div id="resultados">...</div></BODY></HTML>. Esto con la finalidad de llamar este archivo para obtener las búsquedas y mostrarlas con AJAX sin recargar todo el sitio nuevamente.
  • /buscador.js Aquí están incluídos las funciones básicas para el funcionamiento del buscador.
  • /buscador.css Le da una bonita apariencia el buscador.
  • /include/funciones.php Aquí sólo guardamos: sql_quote para el filtrado de las consultas que se necesiten ejecutar.
  • /include/pagination.class.php Aquí descripción
  • /config.php Aquí la configuración básica para el funcionamiento del buscador. Host, usuario, password, base de datos.

Ahora, trataré de describir a detalle la finalidad de cada archivo:

buscador.php

Primero que nada incluimos los archivos necesarios para su funcionamiento. Más adelante pondré la explicación más a detalle de cada uno.

  1. <?
  2. require('config.php');
  3. require('include/conexion.php');
  4. require('include/funciones.php');
  5. require('include/pagination.class.php');

Determino cuantos elementos se mostrarán por página y en que página estoy inicialmente (o por defecto). Estos dos datos son para ir limitando la consulta SQL que traerá los resultados de la búsqueda y en este otro artículo del Blog explico bien como paginar las consultas SQL, ya que internamente también estaré utilizando la clase para paginación de resultados.

  1. $items = 10;
  2. $page = 1;
  3.  
  4. if(isset($_GET['page']) and is_numeric($_GET['page']) and $page = $_GET['page'])
  5.         $limit = " LIMIT ".(($page-1)*$items).",$items";
  6.     else
  7.         $limit = " LIMIT $items";

En esta parte verifico si existe la variable q. La variable q estará presente en caso de que se haya introducido una búsqueda.

  1. if(isset($_GET['q']) and !eregi('^ *$',$_GET['q'])){
  2.         //Realizamos la búsqueda de q en los registros
  3.     }else{
  4.         //Mostramos los registros disponibles ya que no hay nada por buscar
  5.     }

Aquí lo que falta en el código del párrafo anterior: En caso de que si esté definida la variable q (para eso utilizo la función isset()), me aseguro que no esté formada por sólo espacios para asegurarme de que en realidad estoy buscando algo (para eso utilizo eregi() apoyándome de la expresión ^ *$ que significa algo así: "que inicie y termine con un espacio en blanco que se repita desde 0 a infinito veces")

  1. if(isset($_GET['q']) and !eregi('^ *$',$_GET['q'])){
  2.         $q = sql_quote($_GET['q']); //para ejecutar consulta
  3.         $busqueda = htmlentities($q); //para mostrar en pantalla
  4.  
  5.         $sqlStr = "SELECT * FROM preguntas WHERE pregunta LIKE '%$q%'";
  6.         $sqlStrAux = "SELECT count(*) as total FROM preguntas WHERE pregunta LIKE '%$q%'";
  7.     }else{
  8.         $sqlStr = "SELECT * FROM preguntas";
  9.         $sqlStrAux = "SELECT count(*) as total FROM preguntas";
  10.     }

La función sql_quote sirve para filtrar la cadena y evitar inyecciones de código SQL. Está almacenada en el archivo includes/funcions.php que también explicaré mas adelante.

En este paso (creo yo que sería el más complicado ya que estamos trabajando con consultas SQL) estoy haciendo 2 cosas:

  1. Creando una consulta que devuelva todos los registros que necesitamos (SELECT * FROM preguntas WHERE pregunta LIKE '%$q%').
  2. y creando ésta otra que únicamente devuelva la cantidad de registros ("SELECT count(*) as total FROM preguntas WHERE pregunta LIKE '%$q%').

No se que tan complicado pueda ser esta paso para todos, especialmente para ti Stan :) ,... pero cualquier duda por favor en los comentarios y las vamos resolviendo entre todos los que gusten participar.

Voy a suponer que la mayoría sabe como funciona esto de las consultas SQL y continuaré.


Ahora, en el paso que sigue, ejecutamos ambas consultas y seguido de eso estructuramos el HTML que formará el buscador:

  1. $aux = Mysql_Fetch_Assoc(mysql_query($sqlStrAux,$link));
  2. $query = mysql_query($sqlStr.$limit, $link);
  3. ?>

En el paso anterior, a la segunda consulta le concatené la variable que habíamos preparado para limitar la cantidad de registros ($limit) que traeríamos según la página en la que estuviéramos ($sqlStr.$limit).

En este paso, comenzamos a estructurar el HTML: Incluimos la hoja de estilo CSS del paginador, el estilo CSS para mostrar las tablas con los resultados, y el archivo include/buscador.js (otro de los importantes en el funcionamiento del buscador)

  1. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  2. <html xmlns="http://www.w3.org/1999/xhtml">
  3. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  4. <title>Buscador en AJAX</title>
  5. <link rel="stylesheet" href="pagination.css" media="screen">
  6. <link rel="stylesheet" href="style.css" media="screen">
  7. <script src="include/buscador.js" type="text/javascript" language="javascript"></script>
  8. </head>
  9.  

Creamos la caja de búsqueda:

  1. <form action="index.php" onsubmit="return buscar()">
  2.       <label>Buscar</label> <input type="text" id="q" name="q" value="<? if(isset($q)) echo $busqueda; ?>" onKeyUp="return buscar()">
  3.       <input type="submit" value="Buscar" id="boton">
  4.       <span id="loading"></span>
  5.     </form>

Este es el formulario para realizar la búsqueda, en el encontraremos tres puntos a destacar:

  1. onsubmit="return buscar()": Al enviar el formulario o dar clic en buscar, se ejecutará la función buscar().
  2. onKeyUp="return buscar()": El momento en que ejecutará la función buscar() será al quitar el dedo de la tecla (onKeyUp).
  3. <span id="loading"></span>: Ahí aparecerá la animación cargando cuando el contenido esté siendo cargado.

La función buscar() está almacenada en include/buscador.js


Por último ya mostrar los resultados de las consultas SQL ejecutadas.

Está parte está formada básicamente y a grandes rasgos por:

  1. <div id="resultados">
  2.        <!-- Aquí van los registros -->
  3.     </div>

Dentro <div id="resultados"> ... </div> aparecerán los resultados, y al momento de dar clic en buscar o ejecutar la funcion buscar() que está contenida dentro de include/buscador.js y es accionada por el formulario anterior, este div se actualizará con los nuevos resultados.

Tomando en cuenta que lo que está dentro del div anterior, es únicamente los resultados de búsqueda, y que estos se estarán mandando llamar cada que uno ejecute la búsqueda: Esto lo separé en el archivo busqueda.php (Este lo mandamos llamar cada que buscar() se ejecuta, y actualizamos el contenido del div con los que nos devuelva el archivo busqueda.php):

  1. echo"    <div id=resultados>
  2.    <p>";
  3.  
  4.         if($aux['total'] and isset($q)){
  5.                 echo "{$aux['total']} Resultado".($aux['total']>1?'s':'')." que coinciden con tu b&uacute;squeda \"<strong>$busqueda</strong>\".";
  6.             }elseif($aux['total'] and !isset($q)){
  7.                 echo "Total de registros: {$aux['total']}";
  8.             }elseif(!$aux['total'] and isset($q)){
  9.                 echo"No hay registros que coincidan con tu b&uacute;squeda \"<strong>$busqueda</strong>\"";
  10.             }
  11.    
  12.     echo"</p>";
  13.  
  14.         if($aux['total']>0){
  15.             $p = new pagination;
  16.             $p->Items($aux['total']);
  17.             $p->limit($items);
  18.             if(isset($q))
  19.                     $p->target("index.php?q=".urlencode($q));
  20.                 else
  21.                     $p->target("index.php");
  22.             $p->currentPage($page);
  23.             $p->show();
  24.             echo '<ul>';
  25.             while($row = mysql_fetch_assoc($query))
  26.                 echo "<li>".htmlentities($row['pregunta'])."</li>";
  27.             echo '</ul>';
  28.             $p->show();
  29.         }
  30. echo"
  31.    </div>
  32. </body>
  33. </html>";

Dentro de este último bloque de código validamos que haya resultados para despues de eso paginarlos con ayuda de la clase de paginación (y el artículo sobre como paginar) y por último un ciclo en el que vamos mostrando los resultados.

Un punto que posiblemente pueda causar duda en el código anterior: cuanto intento cambiar a donde apunta la paginación de manera que los enlaces apunten a algo así (En caso de haber una busqueda):

  • buscador.php?q=busqueda&page1
  • buscador.php?q=busqueda&page2
  • etc

A la busqueda le aplico la función urlencode, para que la búsqueda en caso de tener símbolos especiales me permita introducirlos como una URL. Aunque, recomiendo mejor leer las especificaciones que nos dan en la documentación de php: http://php.net/urlencode

  1. $p->target("index.php?q=".urlencode($q));
  2.        else
  3.     $p->target("index.php");

Cualquiera de todos los puntos anteriores puedo profundisarlo, sólo cuestion de dejarlo en los comentarios. (Uff, brb! voy por el 2do café.)

busqueda.php

Este archivo está formado básicamente por lo que está dentro de <div id="resultados"> ... </div> en buscador.php. Únicamente nos sirve para traer resultados mediante AJAX.

  1. require('config.php');
  2. require('include/conexion.php');
  3. require('include/funciones.php');
  4. require('include/pagination.class.php');
  5.  
  6. $items = 10;
  7. $page = 1;
  8.  
  9. if(isset($_GET['page']) and is_numeric($_GET['page']) and $page = $_GET['page'])
  10.         $limit = " LIMIT ".(($page-1)*$items).",$items";
  11.     else
  12.         $limit = " LIMIT $items";
  13.  
  14. if(isset($_GET['q']) and !eregi('^ *$',$_GET['q'])){
  15.         $q = sql_quote($_GET['q']); //para ejecutar consulta
  16.         $busqueda = htmlentities($q); //para mostrar en pantalla
  17.  
  18.         $sqlStr = "SELECT * FROM preguntas WHERE pregunta LIKE '%$q%'";
  19.         $sqlStrAux = "SELECT count(*) as total FROM preguntas WHERE pregunta LIKE '%$q%'";
  20.     }else{
  21.         $sqlStr = "SELECT * FROM preguntas";
  22.         $sqlStrAux = "SELECT count(*) as total FROM preguntas";
  23.     }
  24.  
  25. $aux = Mysql_Fetch_Assoc(mysql_query($sqlStrAux,$link));
  26. $query = mysql_query($sqlStr.$limit, $link);
  27.     echo "<p>";
  28.         if($aux['total'] and isset($busqueda)){
  29.                 echo "{$aux['total']} Resultado".($aux['total']>1?'s':'')." que coinciden con tu b&uacute;squeda \"<strong>$busqueda</strong>\".";
  30.             }elseif($aux['total'] and !isset($q)){
  31.                 echo "Total de registros: {$aux['total']}";
  32.             }elseif(!$aux['total'] and isset($q)){
  33.                 echo"No hay registros que coincidan con tu b&uacute;squeda \"<strong>$busqueda</strong>\"";
  34.             }
  35.  
  36.     echo "</p>";
  37.  
  38.         if($aux['total']>0){
  39.             $p = new pagination;
  40.             $p->Items($aux['total']);
  41.             $p->limit($items);
  42.             if(isset($q))
  43.                     $p->target("index.php?q=".urlencode($q));
  44.                 else
  45.                     $p->target("index.php");
  46.             $p->currentPage($page);
  47.             $p->show();
  48.             echo "\t<table class=\"registros\">\n";
  49.             echo "<tr class=\"titulos\"><td>Titulo</td></tr>\n";
  50.             $r=0;
  51.             while($row = mysql_fetch_assoc($query)){
  52.           echo "\t\t<tr class=\"row$r\"><td><a href=\"http://www.mis-algoritmos.com/?p={$row['id']}\" target=\"_blank\">".htmlentities($row['pregunta'])."</a></td></tr>\n";
  53.           if($r%2==0)++$r;else--$r;
  54.         }
  55.             echo "\t</table>\n";
  56.             $p->show();
  57.         }

include/buscador.js

En este archivo están almacenadas las dos funciones más importantes para permitirle la funcionalidad con AJAX al buscador:

  1. xmlhttp() que crea una instancia del objeto con el que trabajaré las funciones de AJAX. (Esta función la conseguí en este artículo de anieto2k que escribió hace ya algo de tiempo)
  2. Y buscar() que ejecuta la búsqueda y devuelve al resultado a buscador.php para mostrarlo y dar el efecto que buscamos.

Aquí la función xmlhttp() que únicamente crea el objeto para trabajar con AJAX

  1. function xmlhttp(){
  2.         var xmlhttp;
  3.         try{xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");}
  4.         catch(e){
  5.             try{xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");}
  6.             catch(e){
  7.                 try{xmlhttp = new XMLHttpRequest();}
  8.                 catch(e){
  9.                     xmlhttp = false;
  10.                 }
  11.             }
  12.         }
  13.         if (!xmlhttp)
  14.                 return null;
  15.             else
  16.                 return xmlhttp;
  17.     }

Y aquí la función buscar(). Esta es importante, así que me voy describiendo casi línea por línea.

  1. function buscar(){
  2.         var query = document.getElementById('q').value;
  3.         var A = document.getElementById('resultados');
  4.         var B = document.getElementById('loading');
  5.         var ajax = xmlhttp();
  6.  
  7.         ajax.onreadystatechange=function(){
  8.                 if(ajax.readyState==1){
  9.                         B.innerHTML = "<img src='images/loading.gif' alg='Loading...'>";
  10.                     }
  11.                 if(ajax.readyState==4){
  12.                         A.innerHTML = ajax.responseText;
  13.                         B.innerHTML = "";
  14.                     }
  15.             }
  16.  ajax.open("GET","busqueda.php?q="+encodeURIComponent(query),true);
  17.         ajax.send(null);
  18.         return false;
  19.     }

Creamos tres variables: A, B y ajax (El nombre no importa).

En la variable A y B estoy utilizando document.getElementById('elemento'), que nos permite acceder a las propiedades de cualquier elemento HTML que esté representado de alguna forma en el navegador. En la página de Mozilla está detallado con palabras más entendibles: http://developer.mozilla.org/en/docs/DOM:document.getElementById.

  1. var query = document.getElementById('q').value;
  2.         var A = document.getElementById('resultados');
  3.         var B = document.getElementById('loading');
  4.         var ajax = xmlhttp();

A = document.getElementById('resultados'); nos permitirá manipular el DIV resultados (<div id="resultados"> en donde sustituiremos los resultados que nos devuelva busqueda.php), y con B = document.getElementById('loading'); controlaremos el contenido de <span id="loading"></span>, en donde pondremos la imagen cargando cuando el objeto AJAX esté trabajando.


ajax.onreadystatechange=function(){ ... } permite estar a la escucha de cuando sucede un cambio en el objeto AJAX (Estos cambios está explicados aquí). Por el momento, sólo utilizaré dos de ellos:

  1. 1 (Cargando)
  2. Y 4 (Terminado)

Creo que esta información es suficiente para comprender que if(ajax.readyState==1){...}, debo mostrar la imagen cargando, y que si if(ajax.readyState==4){...}, entonces debo mostrar el resultado de las búsquedas.

Para poder hacer esto utilizaremos innerHTML, que nos permite obtener o cambiar el contenido de un elemento HTML. En la página de Mozilla está detallado con palabras más entendibles: http://developer.mozilla.org/en/docs/DOM:element.innerHTML.

Por lo que, con B.innerHTML = "<img src='images/loading.gif' alg='Loading...'>"; puedo alterar el contenido del <span id="loading"> y cambiarlo por la imagen cargando, y de igual forma al momento de terminar la obtención de los resultados borro lo que hay dentro de B(<span id="loading">) y en A (<div id="resultados">) cargo los resultados: A.innerHTML = ajax.responseText;

  1. ajax.onreadystatechange=function(){
  2.                 if(ajax.readyState==1){
  3.                         B.innerHTML = "<img src='images/loading.gif' alg='Loading...'>";
  4.                     }
  5.                 if(ajax.readyState==4){
  6.                         A.innerHTML = ajax.responseText;
  7.                         B.innerHTML = "";
  8.                     }
  9.             }
------------------

Espero que este tutorial, les hay servidor de algo y valga la pena la desveladota que me aventé... mañana mi examen de Ecuaciones Diferenciales y no estudié nada por estar escribiendo esto =S

Y bueno, ya para terminar: cualquier comentario o sugerencia que permita reforzar o documentar un poco mas este tutorial, es bien recibido :D ...

Igual y si notan muchas inconsistencias en mi explicación o horrores ortográficos pues me echan un grito en los comentarios /puf que yo ya me voy a dormir! u_U

There are 132 comments. for this entry.