
JavaScript Aplicado con una extensión de Chrome y Unsplash
Vamos a utilizar JavaScript sin ningún tipo de framework, sólo con JavaScript "vainilla", para escribir una extensión para Chrome que nos muestre imágenes de unsplash
Prólogo
Las extensiones de Chrome son herramientas potentes que sin embargo no han conseguido convertir en términos de monetización lo que uno podría esperar
En cualquier caso se utilizan muchísimo
En este curso vamos a ver un ejemplo simple y sin complicarnos, sólo con pure JavaScript o también conocido con JavaScript vainilla
Y qué hará?
Simplemente mostrarnos una imagen de unsplash en cuanto abrimos una nueva pestaña
Vamos allá
manifest.json
Éste es el primer archivo que tendremos que crear, un archivo JSON que no es más que un archivo en forma de objeto de JavaScript, donde incluiremos información que Chrome nos pide
{
"manifest_version": 2,
"name": "KUW-Chrome-Unsplash",
"description": "Unsplash images on your new tab",
"version": "1",
"author": "KUWorking",
"browser_action": {
"default_icon": "kuw-chrome-unsplash.png",
"default_title": "KUWorking"
},
"chrome_url_overrides": {
"newtab": "kw-unsplash.html"
},
"permissions": ["activeTab"]
}
Recuerda que en los archivos json las propiedades van con comillas sí o sí, y no juegues con las comas
Puedes ver todas las opciones que aplican en developer.chrome.com
Con nuestro manifest lo que hemos hecho es aparte de definir los números de versiones, descripciones y demás, definir las acciones y los permisos de nuestra extensión
-
La versión del manifiesto tiene que ser 2 ya que es el formato de manifiesto que Chrome espera
-
En
chrome_url_overrides
estamos sustituyendo la página de nueva pestaña que esnewtab
y que la dirigimos haciakw-unsplash.html
-
Y para hacer esta sustitución necesitamos los permisos, que apuntamos en
permissions
, deactiveTab
que es lo que nos permite interceder cuando el usuario abre una nueva pestaña (y se le pedirá que acepte este permiso al instalar la extensión)
kw-unsplash.html
Para crear una página html
básica (que es la que Chrome ejecutará como nueva pestaña) utilizaremos una estructura como la siguiente
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<meta name="author" content="kuworking" />
<meta name="description" content="Unsplash images on your new tab" />
<meta name="keywords" content="chrome,extension,unsplash,new tab" />
<title>kuworking.com</title>
<link rel="stylesheet" href="//fonts.googleapis.com/css?family=font1|font2|etc" type="text/css" />
<style type="text/css"></style>
<script src="kw-unsplash.js"></script>
</head>
<body></body>
</html>
Pásate por los cursos de html
y css
si quieres profundizar, los tienes aquí y aquí
A destacar, importo las fuentes directamente de Google (tienes una herramienta para verlas font-inspiration)
<head>
<link rel="stylesheet" href="//fonts.googleapis.com/css?family=Didact+Gothic" type="text/css" />
</head>
Y preparo el terreno para añadir las normas css
y el JavaScript
<head>
<style type="text/css"></style>
<script src="kw-unsplash.js"></script>
</head>
Con esto ya tenemos nuestra extensión lista (lo que vendría a ser el esqueleto de la extensión) y ya podemos ver qué tal se comporta
Chrome://extensions
Para probar la extensión nos vamos a chrome://extensions
(lo ponemos en nuestro Chrome como si fuera una dirección web, o accedemos a ella a través de los menús) y allí accionamos el Developer mode para poder luego clicar el Load unpacked y seleccionar el directorio donde tenemos estos dos archivos (el manifest
y el html
)
Pero para que funcione necesitamos el icono, bájate cualquier imagen en formato .png
, ponle el nombre que has puesto en el manifest
, y listos, ya tenemos el esqueleto de la extensión
Mostrar un background de unsplash
Utilizamos la API de unsplash, y generamos la imagen con JavaScript, por lo que lo primero es añadir el ancla donde pondremos la imagen que será simplemente un <div>
<body>
<div id="imagen"></div>
</body>
</html>
Y luego, creamos el archivo kw-unsplash.js
que es donde le hemos dicho al documento que tendríamos nuestro script
const start = () => {
const keywords = 'coding,apple'
const url = `https://source.unsplash.com/random/?${keywords}`
const htmlCode = `<img src="${url}" />`
document.getElementById('imagen').innerHTML = htmlCode
}
document.addEventListener('DOMContentLoaded', start, true)
Lo primero es la última línea, donde le digo que cuando el DOM
esté cargado (lo que viene a ser que todo el documento haya sido interpretado por el navegador) ejecute la función que se llama start
Luego defino la función start
, y lo hago utilizando JavaScript moderno con las fat arrows
Esto representa un problema puesto que hay muchos navegadores (antiguos) que no entienden este JavaScript
Por eso necesitaríamos utilizar algún proceso de compilación para transformar ese JavaScript en una versión más antiguo y compatible
La mejor manera (normalmente) de conseguir esto es trabajar con algún framework concreto que ya nos lo integre
Pero aquí no haremos ni una cosa ni la otra, pero que lo sepas
A partir de aquí, la función start
construye la url con la que pediremos la imagen mediante el elemento html
<img>
Pero esto, que nos funcionaría en una página normal, no nos funciona en una extensión por sus limitaciones particulares (lo puedes leer aquí)
El código que sí funciona es el siguiente:
const start = () => {
const keywords = 'coding,apple'
const url = `https://source.unsplash.com/random/?${keywords}`
const htmlCode = `<img src="${url}" />`
document.getElementById('imagen').innerHTML = htmlCode
}
chrome.tabs.getCurrent(start)
En lugar de utilizar una imagen lo que haremos será cambiar el fondo del elemento <body>
El código css
sería el siguiente
<style type="text/css">
body {
background-image: url('');
background-size: cover;
background-repeat: no-repeat;
background-position-x: center;
background-position-y: center;
background-attachment: fixed;
}
</style>
Y en el JavaScript lo que haríamos sería modificar el elemento <body>
y dejaremos de lado la imagen
const start = () => {
const keywords = 'coding,apple'
const url = `https://source.unsplash.com/random/?${keywords}`
document.body.style.backgroundImage = `url('${url}')`
}
chrome.tabs.getCurrent(start)
Añadiendo un caché
El caché nos sirve para que si la imagen ya se ha descargado con anterioridad no se vuelva a descargar
Una manera de hacerlo es añadiendo el header correspondiente
<meta http-equiv="Cache-control" content="public" />
Otra manera de hacerlo y asegurarse es la siguiente
/**
* función para
* - generar la url de unsplash
* - descargar la imagen con fetch
* - almacenar la url en localStorage
*/
const get_image_url = async keywords => {
const response = await fetch(`https://source.unsplash.com/random/?${keywords}`)
const image_url = response.url
window.localStorage.setItem('kw-wallpaper', image_url)
return image_url
}
/**
* función para
* - pedir la imagen con la url en la caché del navegador
* - descargarla de esa caché, y si no está añadirla al caché
* - devolver la url original o la del caché si esta existe
*/
const get_image = async image_url => {
const request = new Request(image_url)
const cache = await caches.open('kw-cache')
const cached_image = await cache.match(request)
cached_image || cache.add(request)
return cached_image ? cached_image.url : image_url
}
/**
* función general que recoge la url que está en localStorage, o si no hay ninguna la genera de nuevo
* .. para luego descargarla y añadirla como fondo de <body>
*/
const start = async () => {
const keywords = 'coding,apple'
const image_url = window.localStorage.getItem('kw-wallpaper') || get_image_url(keywords)
const image = get_image(image_url)
document.body.style.backgroundImage = `url('${image}')`
}
chrome.tabs.getCurrent(start)
Aquí hemos utilizado la caché del navegador (que nos la proporciona el propio navegador) y localStorage
para recordar la url que hemos pedido
Renovar la imagen cada dia
Tal y como está el código, una vez pedimos una imagen, ésta se almacena en localStorage
y ya nunca más la actualizaremos, y queremos que esto sea así pero sólo durante un día
Una manera de hacerlo es almacenar la fecha de la imagen también en localStorage
y así podremos comprobar si estamos dentro del mismo día o no
// función para determinar si lo almacenado en localStorage es de hoy o no
const is_this_a_new_day = () => {
const last_date = Number(window.localStorage.getItem('kw-date')) || ''
const today = Number(new Date().getDay())
window.localStorage.setItem('kw-date', today)
return today === Number(last_date) ? false : true
// convertimos a number el last_date que en localStorage será un string
}
Y ahora con esta función podemos decidir si miramos si hay una imagen en localStorage
o si por el contrario descargamos una nueva imagen sí o sí
const start = async () => {
const keywords = 'coding,apple'
const new_day = is_this_a_new_day()
const image_url = new_day
? await get_image_url(keywords)
: window.localStorage.getItem('kw-wallpaper') || (await get_image_url(keywords))
const image = await get_image(image_url)
document.body.style.backgroundImage = `url('${image}')`
}
Botón para cambiar la imagen
Pero nos falta algo, qué pasa si la imagen descargada no nos gusta?
Necesitamos un botón para poder cambiarla
<body>
<div id="change_wallpaper"></div>
</body>
Lo estilamos con css
#change_wallpaper {
cursor: pointer;
width: 25px;
height: 25px;
border-radius: 2px;
background-color: #79a2ff85;
transition: background-color 0.1s;
}
#change_wallpaper:hover {
background-color: #79a2ff;
}
Y controlamos con JavaScript
cuándo se aprieta para ejecutar la función start
con un argumento que lo definimos como un string
con un texto semántico cualquiera
document.getElementById('change_wallpaper').addEventListener('click', () => start('refresh'), true)
Luego lo que haremos es que si existe ese argumento entonces pediremos una nueva imagen sí o sí
const start = async refresh => {
//...
const new_day = refresh ? true : is_this_a_new_day()
//...
}
Pero hay un último problema y es que si apretamos el botón muy seguido unsplash nos devolverá la misma imagen repetida
La documentación de unsplash nos dice cómo evitarlo
const different_number = new Date().getMilliseconds()
const url = `https://source.unsplash.com/random/?sig=${different_number}&&${keywords}`
Y listos
Nos faltarían dos cosas
- Poner una respuesta gráfica cuando apretamos el botón de cambio de imagen, y anular el botón mientras la imagen se está cargando
- Poner un
<input>
para poder cambiar las keywords ya que ahora están en el código
Pero vamos, el grueso de la extensión ya estaría hecho
Publicando la extensión
Este paso es el más sencillo, necesitarás 5 USD para abrirte cuenta de desarrollador en Google (un único pago) y ya podrás subir tu extensión de Chrome
Abrir cuenta de developers
Tienes que visitar chrome.google.com/webstore/developer/dashboard y allí te pedirán la one-time developer registration fee, piensa antes con qué cuenta personal quieres abrir esta cuenta developer ya que después no podrás cambiar
Subir la extensión
Una vez dentro te pedirán subir la extensión en formato .zip, por lo que coge el directorio donde esté todo tu código, lo empaquetas con cualquier programa de compresión compatible con zip y ya lo puedes subir
Allí te pedirán más información, en concreto de la extensión del curso nos dice que no hemos añadido un short name en el manifiesto, nos pide que añadamos una descripción del mismo, un icono, y pantallazos (screenshots) que servirán para convencer al usuario de que se instale la extensión, incluso podemos tener un vídeo YouTube para añadir más argumentos de compra
Piensa que la mitad del trabajo es programación, y la otra mitad es marketing!
Tienes más información sobre cómo publicar tu extensión en la documentación de Google aquí
Entre otras cosas podrás acotar en qué países quieres publicar tu extensión y a qué precio quieres venderla si es que quieres monetizarla
En realidad el proceso es bastante sencillo aunque generar todo el apartado gráfico y de texto para la extensión es más laborioso (me refiero a lo que verá el usuario en la tienda de extensiones)
Código final
Si quieres testear el código final, aquí lo tienes en versión no-extensión para poder probarlo en codesandbox (sin el botón)
const is_this_a_new_day = () => {
const last_date = Number(window.localStorage.getItem('kw-date')) || ''
const today = Number(new Date().getDay())
window.localStorage.setItem('kw-date', today)
return today === Number(last_date) ? false : true
}
const get_image_url = async keywords => {
const different_number = new Date().getMilliseconds()
const response = await fetch(`https://source.unsplash.com/random/?sig=${different_number}&&${keywords}`)
const image_url = response.url
window.localStorage.setItem('kw-wallpaper', image_url)
return image_url
}
const get_image = async image_url => {
const request = new Request(image_url)
const cache = await caches.open('kw-cache')
const cached_image = await cache.match(request)
cached_image || cache.add(request)
return cached_image ? cached_image.url : image_url
}
const start = async () => {
const keywords = 'coding,apple'
const new_day = is_this_a_new_day()
const image_url = new_day
? await get_image_url(keywords)
: window.localStorage.getItem('kw-wallpaper') || (await get_image_url(keywords))
const image = await get_image(image_url)
document.getElementById('app').style.backgroundImage = `url('${image}')`
document.getElementById('app').style.backgroundSize = 'cover'
document.getElementById('app').style.backgroundRepeat = 'no-repeat'
document.getElementById('app').style.backgroundPositionX = 'center'
document.getElementById('app').style.backgroundPositionY = 'center'
document.getElementById('app').style.backgroundAttachment = 'fixed'
document.getElementById('app').style.width = '100%'
document.getElementById('app').style.height = '100vh'
}
if (document.readyState !== 'loading') start()
else document.addEventListener('DOMContentLoaded', start, true)
🙋♂️
Lista de correo: escribo algo de tanto en cuanto