Búsqueda de sitios con tecnología de Google con jQuery

Con mucho, una de las funciones más solicitadas por los lectores de Tutorialzine es crear una búsqueda en todo el sitio. Una forma de hacerlo es construirlo usted mismo desde cero. Es decir, usar un lenguaje del lado del servidor como PHP y ejecutar consultas de búsqueda en su base de datos, mostrando los resultados al usuario.

Otra forma es utilizar los servicios de un motor de búsqueda que ya sabe todo sobre todos. Sí, lo has adivinado. En este tutorial, utilizamos la API de búsqueda AJAX de Google para crear un motor de búsqueda personalizado con el que puede buscar resultados web, imágenes, videos y noticias en su sitio.

El HTML

Comencemos con el marcado HTML. Del nuevo doctype HTML5, pasamos a definir el título del documento e incluir la hoja de estilo en la sección de encabezado de la página.

buscar.html

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Google Powered Site Search | Tutorialzine Demo</title>

<link rel="stylesheet" type="text/css" href="styles.css" />

</head>
<body>

<div id="page">

    <h1>Google Powered Site Search</h1>

    <form id="searchForm" method="post">
        <fieldset>

            <input id="s" type="text" />

            <input type="submit" value="Submit" id="submitButton" />

            <div id="searchInContainer">
                <input type="radio" name="check" value="site" id="searchSite" checked />
                <label for="searchSite" id="siteNameLabel">Search</label>

                <input type="radio" name="check" value="web" id="searchWeb" />
                <label for="searchWeb">Search The Web</label>
            </div>

            <ul class="icons">
                <li class="web" title="Web Search" data-searchType="web">Web</li>
                <li class="images" title="Image Search" data-searchType="images">Images</li>
                <li class="news" title="News Search" data-searchType="news">News</li>
                <li class="videos" title="Video Search" data-searchType="video">Videos</li>
            </ul>

        </fieldset>
    </form>

    <div id="resultsDiv"></div>

</div>

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script src="script.js"></script>
</body>
</html>

En la sección del cuerpo, tenemos el elemento contenedor principal:#page división El formulario dentro de él actúa no solo como un formulario de búsqueda, sino también como un contenedor. Tiene esquinas redondeadas CSS3 y se le ha aplicado un color de fondo más oscuro, lo que lo hace más fácil de distinguir del resto de la página.

Dentro del formulario está el cuadro de entrada de texto, después del cual viene el grupo de radio para buscar en el sitio actual/la web, y los cuatro íconos de tipo de búsqueda, organizados como una lista desordenada. Por último incluimos jQuery y nuestro scripts.js , que se analiza en el último paso de este tutorial.

El CSS

Los estilos CSS residen en styles.css . Aquí solo se incluyen las partes más interesantes.

estilos.css - Parte 1

#searchForm{
    /* The search form. */
    background-color:#4C5A65;
    padding:50px 50px 30px;
    margin:80px 0;
    position:relative;

    -moz-border-radius:16px;
    -webkit-border-radius:16px;
    border-radius:16px;
}

fieldset{
    border:none;
}

#s{
    /* The search text box. */

    border:none;
    color:#888888;
    background:url("img/searchBox.png") no-repeat;

    float:left;
    font-family:Arial,Helvetica,sans-serif;
    font-size:15px;
    height:36px;
    line-height:36px;
    margin-right:12px;
    outline:medium none;
    padding:0 0 0 35px;
    text-shadow:1px 1px 0 white;
    width:385px;
}

Como se mencionó anteriormente, las funciones del formulario no se limitan solo a enviar datos, sino también a actuar como un elemento contenedor regular. Esto mantiene el marcado en la página al mínimo, al mismo tiempo que proporciona una rica funcionalidad.

El cuadro de entrada de texto, #s , tiene un estilo con una imagen de fondo y relleno, para que el texto no cubra la lupa.

estilos.css - Parte 2

.icons{
    list-style:none;
    margin:10px 0 0 335px;
    height:19px;
    position:relative;
}

.icons li{
    background:url("img/icons.png") no-repeat;
    float:left;
    height:19px;
    text-indent:-999px;
    cursor:pointer;
    margin-right:5px;
}

/* Styling each icon */

li.web{ width:15px;}
li.web.active,
li.web:hover{ background-position:left bottom;}

li.images{ width:22px; background-position:-18px 0;}
li.images.active,
li.images:hover{ background-position:-18px bottom;}

li.news{ width:14px; background-position:-44px 0;}
li.news.active,
li.news:hover{ background-position:-44px bottom;}

li.videos{ width:17px; background-position:right 0;}
li.videos.active,
li.videos:hover{ background-position:right bottom;}

span.arrow{
    /* The little arrow that moves below the icons */

    width:11px;
    height:6px;
    margin:21px 0 0 5px;
    position:absolute;
    background:url('img/arrow.png') no-repeat;
    left:0;
}

/* The submit button */

#submitButton{
    background:url('img/buttons.png') no-repeat;
    width:83px;
    height:36px;
    text-indent:-9999px;
    overflow:hidden;
    text-transform:uppercase;
    border:none;
    cursor:pointer;
}

#submitButton:hover{
    background-position:left bottom;
}

En el fragmento anterior, puede ver que todos los íconos de tipo de búsqueda comparten una sola imagen de fondo. Se compensa con la posición de fondo, por lo que se muestra la parte correspondiente, tanto para el estado predeterminado como para el estado de desplazamiento.

La misma técnica se utiliza para el botón de envío. Su texto está oculto con un text-indent negativo y botones.png se muestra como fondo, con la parte superior de la imagen visible de forma predeterminada y la parte inferior al pasar el mouse sobre ella.

estilos.css - Parte 3

/* Web & news results */

.webResult{ text-shadow:1px 1px 0 #586a75;margin-bottom:50px;}
.webResult h2{
    background-color:#5D6F7B;
    font-size:18px;
    font-weight:normal;
    padding:8px 20px;

    /* Applying CSS3 rounded corners */
    -moz-border-radius:18px;
    -webkit-border-radius:18px;
    border-radius:18px;
}
.webResult h2 b{ color:#fff; }
.webResult h2 a{ color:#eee;border:none;}
.webResult p{ line-height:1.5;padding:15px 20px;}
.webResult p b{ color:white;}
.webResult > a{ margin-left:20px;}

/* Image & video search results */

.imageResult{
    float:left;
    height:170px;
    margin:0 0 20px 40px;
    text-align:center;
    width:150px;
}
.imageResult img{ display:block;border:none;}
.imageResult a.pic{
    border:1px solid #fff;
    outline:1px solid #777;
    display:block;
    margin:0 auto 15px;
}

/* The show more button */

#more{
    width:83px;
    height:24px;
    background:url('img/more.png') no-repeat;
    cursor:pointer;
    margin:40px auto;
}

#more:hover{
    background-position:left bottom;
}

En el último fragmento, diseñamos los resultados. Aunque mostramos cuatro tipos de resultados de búsqueda:web, noticias, imágenes y video, estos solo tienen el estilo de las dos clases anteriores:.webResult y .imageResult . Por último, diseñamos el #más botón, que se agrega dinámicamente a la página por jQuery dependiendo de los resultados devueltos por Google.

JQuery

Como se mencionó al principio, esta aplicación utiliza la API de búsqueda AJAX de Google. Google proporciona su propia biblioteca de JavaScript, pero si elige usarla, está limitado por su interfaz de usuario. Si bien es funcional, puede que no sea lo que desea ofrecer a sus visitantes. Es por eso que, en este tutorial, estamos usando la "versión desnuda" emitiendo llamadas JSONp con jQuery directamente a su API.

Antes de comenzar a analizar el código jQuery, echemos un vistazo a los datos que Google pone a nuestra disposición después de realizar una búsqueda con la API.

Resultado de muestra de la API de Google

{
    "GsearchResultClass": "GwebSearch",
    "unescapedUrl": "https://tutorialzine.com/2010/02/html5-css3-website-template/",
    "url": "https://tutorialzine.com/2010/02/html5-css3-website-template/",
    "visibleUrl": "tutorialzine.com",
    "cacheUrl": "http://www.google.com/search?q=cache:_NSLxH-cQMAJ:tutorialzine.com",
    "title": "Coding a <b>CSS3</b> & <b>HTML5</b> One-Page Website Template | Tutorialzine",
    "titleNoFormatting": "Coding a CSS3 & HTML5 One-Page Website Template | Tutorialzine",
    "content": "Feb 16, 2010 <b>...</b> Here we are using the new version of HTML.."
}

Una búsqueda realizada a través de su API arrojaría el mismo conjunto de resultados que normalmente obtendría directamente de su sitio. La diferencia es que aquí obtenemos una matriz de JavaScript poblada con objetos como el de arriba. Cada uno de estos objetos contiene el tipo de búsqueda, un título, una URL y el texto de la página que contiene los términos que estamos buscando.

Uso de GsearchResultClass propiedad, podemos determinar cómo mostrar la información, como verá en un momento. Esta aplicación de búsqueda solo admite búsquedas web, de imágenes, de noticias y de videos, pero puede ver una lista completa de los tipos de búsqueda disponibles en la documentación de búsqueda de AJAX de Google.

script.js - Parte 1

$(document).ready(function(){

    var config = {
        siteURL     : 'tutorialzine.com',   // Change this to your site
        searchSite  : true,
        type        : 'web',
        append      : false,
        perPage     : 8,            // A maximum of 8 is allowed by Google
        page        : 0             // The start page
    }

    // The small arrow that marks the active search icon:
    var arrow = $('<span>',{className:'arrow'}).appendTo('ul.icons');

    $('ul.icons li').click(function(){
        var el = $(this);

        if(el.hasClass('active')){
            // The icon is already active, exit
            return false;
        }

        el.siblings().removeClass('active');
        el.addClass('active');

        // Move the arrow below this icon
        arrow.stop().animate({
            left        : el.position().left,
            marginLeft  : (el.width()/2)-4
        });

        // Set the search type
        config.type = el.attr('data-searchType');
        $('#more').fadeOut();
    });

    // Adding the site domain as a label for the first radio button:
    $('#siteNameLabel').append(' '+config.siteURL);

    // Marking the Search tutorialzine.com radio as active:
    $('#searchSite').click();   

    // Marking the web search icon as active:
    $('li.web').click();

    // Focusing the input text box:
    $('#s').focus();

    $('#searchForm').submit(function(){
        googleSearch();
        return false;
    });

    $('#searchSite,#searchWeb').change(function(){
        // Listening for a click on one of the radio buttons.
        // config.searchSite is either true or false.

        config.searchSite = this.id == 'searchSite';
    });

La configuración El objeto contiene opciones de configuración generales, como la URL del sitio, una página de inicio (utilizada en la paginación) y el tipo de búsqueda predeterminado (una búsqueda web). Google solo nos permite seleccionar 8 resultados a la vez, lo cual es suficiente para búsquedas web, pero no tanto para imágenes. Esperemos que Google aumente este límite en el futuro.

Cuando se envía el formulario, jQuery llama a nuestro googleSearch() función, que puede ver a continuación.

script.js - Parte 2

  function googleSearch(settings){

        // If no parameters are supplied to the function,
        // it takes its defaults from the config object above:

        settings = $.extend({},config,settings);
        settings.term = settings.term || $('#s').val();

        if(settings.searchSite){
            // Using the Google site:example.com to limit the search to a
            // specific domain:
            settings.term = 'site:'+settings.siteURL+' '+settings.term;
        }

        // URL of Google's AJAX search API
        var apiURL = 'http://ajax.googleapis.com/ajax/services/search/'+settings.type+
                        '?v=1.0&callback=?';
        var resultsDiv = $('#resultsDiv');

        $.getJSON(apiURL,{
            q   : settings.term,
            rsz : settings.perPage,
            start   : settings.page*settings.perPage
        },function(r){

            var results = r.responseData.results;
            $('#more').remove();

            if(results.length){

                // If results were returned, add them to a pageContainer div,
                // after which append them to the #resultsDiv:

                var pageContainer = $('<div>',{className:'pageContainer'});

                for(var i=0;i<results.length;i++){
                    // Creating a new result object and firing its toString method:
                    pageContainer.append(new result(results[i]) + '');
                }

                if(!settings.append){
                    // This is executed when running a new search,
                    // instead of clicking on the More button:
                    resultsDiv.empty();
                }

                pageContainer.append('<div class="clear"></div>')
                             .hide().appendTo(resultsDiv)
                             .fadeIn('slow');

                var cursor = r.responseData.cursor;

                // Checking if there are more pages with results,
                // and deciding whether to show the More button:

                if( +cursor.estimatedResultCount > (settings.page+1)*settings.perPage){
                    $('<div>',{id:'more'}).appendTo(resultsDiv).click(function(){
                        googleSearch({append:true,page:settings.page+1});
                        $(this).fadeOut();
                    });
                }
            }
            else {

                // No results were found for this search.

                resultsDiv.empty();
                $('<p>',{
                    className   : 'notFound',
                    html        : 'No Results Were Found!'
                }).hide().appendTo(resultsDiv).fadeIn();
            }
        });
    }

La búsqueda de google() envía una solicitud JSONp a la API de Google, genera el marcado de los resultados y lo inserta en el #resultsDiv división Puede vaciar ese div de antemano (si estamos haciendo una nueva búsqueda) o agregar los resultados (esto sucede cuando hacemos clic en el botón "Más").

Ambas rutas siguen la misma lógica:un nuevo .pageContainer div se crea para cada conjunto de resultados (este div tiene un borde inferior, por lo que es más fácil distinguir una página de resultados de la siguiente) y un objeto del resultado class (puede ver esta clase a continuación), se inicializa y su marcado se agrega al pageContainer.

script.js - Parte 3

  function result(r){

        // This is class definition. Object of this class are created for
        // each result. The markup is generated by the .toString() method.

        var arr = [];

        // GsearchResultClass is passed by the google API
        switch(r.GsearchResultClass){

            case 'GwebSearch':
                arr = [
                    '<div class="webResult">',
                    '<h2><a href="',r.url,'">',r.title,'</a></h2>',
                    '<p>',r.content,'</p>',
                    '<a href="',r.url,'">',r.visibleUrl,'</a>',
                    '</div>'
                ];
            break;
            case 'GimageSearch':
                arr = [
                    '<div class="imageResult">',
                    '<a href="',r.url,'" title="',r.titleNoFormatting,
                    '" class="pic" style="width:',r.tbWidth,'px;height:',r.tbHeight,'px;">',
                    '<img src="',r.tbUrl,'" width="',r.tbWidth,'" height="',
                    r.tbHeight,'" /></a>','<div class="clear"></div>',
                    '<a href="',r.originalContextUrl,'">',r.visibleUrl,'</a>',
                    '</div>'
                ];
            break;
            case 'GvideoSearch':
                arr = [
                    '<div class="imageResult">',
                    '<a href="',r.url,'" title="',r.titleNoFormatting,'
                    " class="pic" style="width:150px;height:auto;">',
                    '<img src="',r.tbUrl,'" width="100%" /></a>',
                    '<div class="clear"></div>','<a href="',
                    r.originalContextUrl,'">',r.publisher,'</a>',
                    '</div>'
                ];
            break;
            case 'GnewsSearch':
                arr = [
                    '<div class="webResult">',
                    '<h2><a href="',r.unescapedUrl,'">',r.title,'</a></h2>',
                    '<p>',r.content,'</p>',
                    '<a href="',r.unescapedUrl,'">',r.publisher,'</a>',
                    '</div>'
                ];
            break;
        }

        // The toString method.
        this.toString = function(){
            return arr.join('');
        }
    }
});

Esta función actúa como constructora del resultado clase. Toma el objeto que fue devuelto por la API de Google (que vio al comienzo del paso de jQuery) e inicializa arr según el valor de GsearchResultClass. Tenga en cuenta que arr se le asigna una matriz en lugar de una cadena. Esto es un poco más rápido que múltiples concatenaciones de una cadena.

En la parte inferior de la clase, tenemos toString() método. Básicamente llama a la unión interna de la matriz método, convirtiéndolo en una cadena. toString() es un método mágico, que se llama implícitamente en la línea 38 de script.js - Parte 2 .

¡Con esto, nuestro motor de búsqueda impulsado por Google está completo!

Conclusión

Configurar esta aplicación para buscar en su sitio es realmente simple. Simplemente cambie la URL del sitio propiedad de la config objeto en script.js. Hay muchas formas de mejorar este ejemplo. No todos los datos que provienen de Google se muestran actualmente. También puede usar el tipo de archivo: modificador de búsqueda para buscar tipos específicos de archivos.

Comparte todas las ideas interesantes que se te ocurran en la sección de comentarios.