javascript

Autocompletado AJAX con PHP y MySQL

Resumen: mostrar cómo autocompletar un campo con valores de una tabla de una base de datos MySQL; trayendo los datos con AJAX.

En este post te mostraré cómo sugerir nombres conforme el usuario escribe; trayendo los datos de MySQL con una consulta LIKE y usando fetch para traer los datos por AJAX en tiempo real.

La librería

Para lograr esto vamos a usar la librería awesomplete; cuya introducción ya vimos anteriormente. Si no la has visto, mírala para que veas cómo descargarla e incluirla.

Programando lado del servidor

En el servidor simplemente mostraremos un arreglo de datos que servirán para llenar la lista. Vamos a recibir un parámetro GET en la URL para la búsqueda.

Voy a usar el archivo de base de datos del tutorial de PHP con MySQL:

<?php
$contraseña = "";
$usuario = "root";
$nombre_base_de_datos = "mascotas";
try {
    $bd = new PDO('mysql:host=localhost;dbname=' . $nombre_base_de_datos, $usuario, $contraseña);
    $bd->query("set names utf8;");
    $bd->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
    $bd->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    $bd->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ);
    return $bd;
} catch (Exception $e) {
    echo "Ocurrió algo con la base de datos: " . $e->getMessage();
    return null;
}

Ahora veamos el archivo que filtra los datos:

<?php
// Si no hay búsqueda, mostrar un arreglo vacío y salir
if (empty($_GET["busqueda"])) {
    echo "[]";
    exit;
}
$bd = include_once "bd.php";
$busqueda = $_GET["busqueda"];
$sentencia = $bd->prepare("select * from mascotas where nombre like ?");
$sentencia->execute(["%$busqueda%"]);
$mascotas = $sentencia->fetchAll(PDO::FETCH_OBJ);
echo json_encode($mascotas);

Lo que haremos será buscar en una base de datos de mascotas; si quieres puedes insertarlas con lo siguiente:

-- MySQL dump 10.16  Distrib 10.1.31-MariaDB, for Win32 (AMD64)
--
-- Host: localhost    Database: mascotas
-- ------------------------------------------------------
-- Server version       10.1.31-MariaDB

/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;

--
-- Table structure for table `mascotas`
--

DROP TABLE IF EXISTS `mascotas`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `mascotas` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `nombre` varchar(255) NOT NULL,
  `raza` varchar(255) NOT NULL,
  `edad` tinyint(4) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Dumping data for table `mascotas`
--

LOCK TABLES `mascotas` WRITE;
/*!40000 ALTER TABLE `mascotas` DISABLE KEYS */;
INSERT INTO `mascotas` VALUES (1,'Maggie','Chihuaha',10),(2,'Panque','Ninguna',1),(3,'Guayaba','Ninguna',2),(4,'Cocoa','Ninguna',2);
/*!40000 ALTER TABLE `mascotas` ENABLE KEYS */;
UNLOCK TABLES;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;

/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;

-- Dump completed on 2019-12-03 15:46:00

Al consultar con, por ejemplo, la letra a tenemos esto:

¿Por qué no filtrar los datos del lado del cliente?

Aunque el autocompletado ya filtra los datos, siempre es importante tener un límite de datos. Imagina si fueran millones de registros, sería mejor buscar los que coinciden en el servidor, que buscarlos del lado del cliente.

La vista HTML

En el HTML de nuestro input para autocompletar importamos los estilos, el script de awesomplete y nuestro propio script para el funcionamiento:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Autocompletado con AJAX y PHP</title>
    <link rel="stylesheet" href="awesomplete.base.css">
    <link rel="stylesheet" href="awesomplete.theme.css">
</head>
<body>
    <h1>Probando Autocompletado</h1>
    <input type="text" id="nombre" autocomplete="off" placeholder="Escribe un nombre">
    <script src="awesomplete.min.js"></script>
    <script src="script.js"></script>
</body>
</html>

Lo único destacable es que el input tiene el id nombre y el autocomplete desactivado.

El script

Finalmente veamos el script de JavaScript. Primero obtenemos una referencia al input con querySelector, y creamos un objeto de tipo Awesomplete; le pasamos el input como primer argumento, y como segundo argumento un objeto de configuraciones.

En el objeto de configuraciones indicamos las sugerencias, que son un arreglo vacío al principio. También indicamos el mínimo de caracteres a 1.

document.addEventListener("DOMContentLoaded", () => {
    // El elemento que tendrá el autocompletado
    const $inputNombre = document.querySelector("#nombre");



    let ac = new Awesomplete($inputNombre, {
        list: [], // Por defecto es una lista vacía, hasta que se comienza a escribir
        minChars: 1, // Cuántos caracteres escribir para autocompletar
    });

    // Esta función filtra los datos y refresca el autocompletado
    const refrescarLista = () => {
        let valorDelInput = $inputNombre.value;
        if (!valorDelInput) return; // Detener si no hay valor

        // Buscar nombres de la base de datos con PHP
        fetch("./nombres.php?busqueda=" + valorDelInput)
            .then(r => r.json())
            .then(mascotas => {
                // Mapeamos, ya que se requiere label y value
                ac.list = mascotas.map(mascota => ({
                    label: mascota.nombre, // Lo que aparece al buscar
                    value: mascota.id, // El valor que se pone en el input
                }));
            });
    };

    // Agregar un listener para cuando se cambie el contenido; en el mismo se refresca la lista
    $inputNombre.addEventListener("input", () => {
        refrescarLista();
    });

});

En la línea 13 defino la función que refresca la lista, la cual obtendrá el valor del input (es decir, lo que el usuario haya escrito); si hay algo escrito entonces se realiza una petición con fetch y tendremos el arreglo de MySQL que mostré anteriormente.

Después de eso, asignamos el arreglo a ac.list (recuerda que ac es un objeto de tipo Awesomplete) pero antes de eso lo mapeamos, pues Awesomplete requiere un arreglo plano, o un arreglo de objetos con la clave label (que es lo que se muestra) y value, que es lo que se coloca en el input al seleccionar una opción.

Finalmente agregamos un listener al input, para que cuando el usuario escriba se refresque la lista. Al final obtenemos el siguiente resultado:

Si te fijas en la consola, se hace la petición y se refresca la lista con los datos que traiga el servidor.

Conclusión y notas finales

Por cierto, si quieres el evento cuando se selecciona el valor del autocompletado puedes agregar un listener al input. El evento que dice que se ha seleccionado (ya sea con el mouse o usando el teclado) se llama awesomplete-selectcomplete.

$inputNombre.addEventListener("awesomplete-selectcomplete", function() {
    console.log("Se ha seleccionado un elemento de la lista");
});

Si quisieras que cuando se haga la selección, en lugar del id se pusiera el nombre, al mapear el arreglo coloca el nombre en el value.

Enlaces de interés

He colocado el código completo en GitHub. Puedes verlo aquí.

También puedes ver más sobre los eventos de awesomplete en su página oficial.

Estoy aquí para ayudarte 🤝💻


Estoy aquí para ayudarte en todo lo que necesites. Si requieres alguna modificación en lo presentado en este post, deseas asistencia con tu tarea, proyecto o precisas desarrollar un software a medida, no dudes en contactarme. Estoy comprometido a brindarte el apoyo necesario para que logres tus objetivos. Mi correo es parzibyte(arroba)gmail.com, estoy como@parzibyte en Telegram o en mi página de contacto

No te pierdas ninguno de mis posts 🚀🔔

Suscríbete a mi canal de Telegram para recibir una notificación cuando escriba un nuevo tutorial de programación.
parzibyte

Programador freelancer listo para trabajar contigo. Aplicaciones web, móviles y de escritorio. PHP, Java, Go, Python, JavaScript, Kotlin y más :) https://parzibyte.me/blog/software-creado-por-parzibyte/

Ver comentarios

  • Hola, espero me puedas responder ya que es un hilo bastante viejo, pero estoy haciendo un sistema de ventas y este autocompletar me viene perfecto para buscar los productos en el input, solo queria preguntar que deberia modificar para que al seleccionar uno de los datos el formulario me envie los datos al seleccionar o hacer click en este?

    Gracias de antemano.

  • Comparto una alternativa al archivo que filtra los datos, esperando tus comentarios y opinión sobre dicha solución, de ser posible:

    include('conexion.php');

    $conn = OpenCon();
    $busqueda = $_GET["nombre"];
    $miArreglo = array();
    $sql = "SELECT * FROM mascotas where nombre like '%$busqueda%' ";
    if (mysqli_query($conn, $sql)) {
    $result = mysqli_query($conn, $sql);
    while($row = $result->fetch_assoc()) {
    $miarreglo[] = $row;
    }
    echo json_encode($miArreglo);
    } else {
    echo "Error: " . $sql . "" . mysqli_error($conn);
    }
    CloseCon($conn);
    }

    conexion.php ------

    function OpenCon(){
    $dbhost = "localhost";
    $dbuser = "root";
    $dbpass = "";
    $db = "nombredb";
    $conn = new mysqli($dbhost, $dbuser, $dbpass,$db) or die("Falló: %s\n". $conn -> error);
    return $conn;
    }
    function closeCon($conn){
    $conn->close();
    }

    Muchas gracias por compartir tu conocimiento.
    ¡Saludos!

Entradas recientes

Creador de credenciales web – Aplicación gratuita

Hoy te voy a presentar un creador de credenciales que acabo de programar y que…

1 semana hace

Desplegar PWA creada con Vue 3, Vite y SQLite3 en Apache

Ya te enseñé cómo convertir una aplicación web de Vue 3 en una PWA. Al…

2 semanas hace

Arquitectura para wasm con Go, Vue 3, Pinia y Vite

En este artículo voy a documentar la arquitectura que yo utilizo al trabajar con WebAssembly…

2 semanas hace

Vue 3 y Vite: crear PWA (Progressive Web App)

En un artículo anterior te enseñé a crear un PWA. Al final, cualquier aplicación que…

2 semanas hace

Errores de Comlink y algunas soluciones

Al usar Comlink para trabajar con los workers usando JavaScript me han aparecido algunos errores…

2 semanas hace

Esperar promesa para inicializar Store de Pinia con Vue 3

En este artículo te voy a enseñar cómo usar un "top level await" esperando a…

2 semanas hace

Esta web usa cookies.