Google tiene muchas API’s para hacer que para poder interactuar con todos los productos. Una de ellas es la posibilidad de tener un buscador personalizado, y hacer llamadas con AJAX para obtener los resultados más relevantes.
Cómo agregarlo
Sé que os encanta ir a lo práctico, así que empezaremos por ello:
1- Activar la API
Vamos a la consola de apis de Google, iniciamos sesión y hacemos click en Services › Custom Search API.
2- Consiguiendo una API Key
Google usa API Keys para evitar abusos con las API. Para obtener una, debes de entrar en API Access, y hacer click en Create a new Browser Key. Allí deberás de introducir los referers válidos (los sitios desde los que google de dejará llamar a tu API). Lo normal es usar algo como tusitio.com/*
y *.tusitio.com/*
(tusitio.com también puede ser tusitio.blogspot.com).
3- Creando un motor de búsqueda personalizado
Como último paso, deberemos de crear un motor de búsqueda personalizado, con los sitios personalizados que queramos.
4- El código
Ahora ya tenemos nuestra clave y la ID de nuestro motor de búsqueda, así que sólo falta el código HTML, que dependerá si usamos Blogger, wordPress, o otro sitio web.
Para no tener que basar toda la funcionalidad en JavaScript, vamos a partir de un formulario de búsqueda.
Nota: En todos los casos URL_A_TU_SCRIPT.js
es un script como éste, pero con vuestra API key y la ID de vuestro buscador (las dos primeras variables), y que deberéis de subir a dropbox, o a algún lugar similar.
Para blogger
Tendréis que añadirlo en un widget HTML/Javascript.
<link rel="stylesheet" type="text/css" href="//emiliocobos.net/demos/search-api/style.css"></link>
<form action="/search/" method="GET">
<input type="text" name="q" id="search-submit">
<input type="submit" value="Buscar">
</form>
<script type="text/javascript" src="URL_A_TU_SCRIPT.js"></script>
Para wordPress
En este caso deberás de añadir el código en un widget Texto:
<link rel="stylesheet" type="text/css" href="//emiliocobos.net/demos/search-api/style.css"></link>
<form action="/" method="GET">
<input type="text" name="s" id="search-submit">
<input type="submit" value="Buscar">
</form>
<script type="text/javascript" src="URL_A_TU_SCRIPT.js"></script>
Para cualquier otro sitio
Para sitios sin un sistema de búsqueda, recurriremos a google otra vez más. En este caso, hará falta cambiar un dato más (tusitio.com):
<link rel="stylesheet" type="text/css" href="//emiliocobos.net/demos/search-api/style.css"></link>
<form action="http://google.com/search" method="GET">
<input type="text" name="q" id="search-submit">
<input type="submit" value="Buscar">
<input type="hidden" name="site" value="tusitio.com">
</form>
<script type="text/javascript" src="URL_A_TU_SCRIPT.js"></script>
Cómo funciona
De forma breve: Hacemos una solicitud AJAX a googleCreamos un script JSONP para evitar problemas con IE, mostramos los datos en un elemento HTML, y lo mantenemos abierto y animamos gracias a CSS. (el selector ~
junto con las pseudoclases :hover
y :focus
).
Aquí podéis ver el script con las explicaciones. Se podría hacer más (crear un botón para mostrar más resultados, por ejemplo), pero bueno …;):
(function () {
'use strict';
// Tu API Key
var KEY = "AIzaSyBN_cAlr_Jr_a_mWraqzKqkzWHRNIzorxQ",
// La ID de tu motor de búsqueda
ENGINE_ID = "018301372644929241302:rcfsvgrmp5m",
// Número de resultados
NUM_RESULTS = 5,
// Calculamos la url que servirá de base
BASE_URL = "https://www.googleapis.com/customsearch/v1?key=" + KEY + "&cx=" + ENGINE_ID,
// Cogemos el elemento del DOM
input = document.getElementById('search-input'),
// Mandar datos a la consola
log = function (datos) {
return window.console && window.console.log(datos);
},
// Crear un script JSONP para evitar problemas de origen de dominios en IE
// El callback está generado dinámicamente para que ninguna función externa se vea alterada
loadScript = function (url, opciones) {
var fjs = document.getElementsByTagName('script')[0],
js = document.createElement('script'),
opciones = opciones || {},
callbackName = "gsearchCB_" + +new Date();
window[callbackName] = function (data) {
log(data);
if( opciones.callback ) {
opciones.callback.call(null, data);
}
js.parentNode.removeChild(js);
window[callbackName] = null;
}
js.src = url + "&callback=" + callbackName;
js.type = "text/javascript";
js.async = true;
js.onerror = function () {
js.parentNode.removeChild(js);
window[callbackName] = null;
opciones.error && opciones.error();
}
fjs.parentNode.insertBefore(js, fjs);
return true;
},
results,
timer;
if (!input) // Si no hay elemento del DOM, no podemos hacer nada
return;
// Evitamos autocompletar
input.setAttribute('autocomplete', 'off');
function search(query, count, startIndex) {
var url = BASE_URL + "&q=" + query,
count = count || NUM_RESULTS;
// Hecho por si implementaba paginación
if( startIndex )
url += "&start=" + startIndex;
// Lo mismo, si no usamos número de items por defecto
url += "&num=" + count;
loadScript(url, {
error: function(){
results.innerHTML = "<p>Ha habido algún error, probablemente el límite haya sido excedido, prueba mañana</p>";
},
callback: function(data) {
results.innerHTML = parse(data)
}
})
}
function parse(data){
// Obtenemos los datos
var totalResults = parseInt(data.searchInformation.totalResults, 10),
// Los resultados en sí
items = data.items,
// Sólo un contador
i = 0,
// El número de items
len = items ? items.length : 0,
// Variable para usar en el loop que recorrerá los ítems
item,
// La salida en HTML
output;
log("Resultados de la consulta:")
log(data);
// Si no hay nada, lo decimos y salimos
if( ! items )
return "<p>No se ha encontrado nada</p>";
// Si hay algo, mostramos el número total de resultados
output = "<p class=\"total-results\">" + totalResults + " resultados</p><ul>";
for( ; i < len; i++ ){
item = items[i];
// Mostramos un título, una url y un resumen por cada resultado obtenido
output += "<li><h3><a title=\"" + item.snippet.replace(/[<>"]/g, function(a){ return {"\"": """, "<": "<", ">": ">"}[a]}) + "\" href=\"" + item.link + "\">" + item.htmlTitle + "</a></h3>";
output += "<small>" + item.htmlFormattedUrl + "</small><p class=\"snippet\">" + item.htmlSnippet + "</p></li>";
output += "</li>"
}
output += "</ul>";
return output;
}
/*
* Cuando levantamos una tecla del teclado, se ejecuta ésta función,
* que deja un intervalo de 0.5 segundos, y luego busca
*/
function onKeyUp(e){
// log("Keyup");
// Evitar que se solicite cada vez que tocas el teclado
if( timer ) {
clearTimeout(timer);
}
timer = setTimeout(function(){
// Si hemos escrito tres caracteres o menos todavía no hay nada que mostrar
if( input.value.length <= 3 )
return;
// Si no hay elemento donde mostrar los resultados, lo creamos
if( !results ){
results = document.createElement('div');
results.className = "goog-ajax-results";
input.parentNode.appendChild(results);
}
results.innerHTML = "<p>Cargando...</p>";
// Buscamos
search(encodeURIComponent(input.value));
}, 500);
}
// Añadimos el evento
input.addEventListener ?
input.addEventListener('keyup', onKeyUp, false): input.attachEvent('onkeyup', onKeyUp);
})()
Demo y posibles contraindicaciones
El mayor inconveniente es que Google sólo nos deja hacer 100 solicitudes gratis. No sé si es mucho o poco (a mí me parece suficiente para un sitio, pero muy justo). De todas maneras se podría hacer que no hubiera rastro si hay algún error (en la demo lo muestro)… Así que si en la demo recibís un error, símplemente pasaros mañana.
Aparte de eso, me parece una funcionalidad con mucha utilidad (si perdonáis mi CSS hecho en media hora ;))