hand Inicio
hand JSBloqs
hand GutenBloqs
Wallpaper

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

json
{
"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 es newtab y que la dirigimos hacia kw-unsplash.html

  • Y para hacer esta sustitución necesitamos los permisos, que apuntamos en permissions, de activeTab 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

html
<!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)

html
<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

html
<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>

html
  <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

js
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:

js
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

css
<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

js
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

html
<meta http-equiv="Cache-control" content="public" />

Otra manera de hacerlo y asegurarse es la siguiente

js
/**
* 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

js
// 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í

js
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

html
<body>
<div id="change_wallpaper"></div>
</body>

Lo estilamos con css

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

js
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í

js
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

js
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)

js
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)

🙋‍♂️

draw of me

Hola, tienes en mente desarrollar una web?

Si quieres, te ayudo

11ty para webs ultra-rápidaseleventy js
Gatsby para webs complejasgatsby js
WordPress para webs para el usuario finalwordpress

Escríbeme

Lista de correo: escribo algo de tanto en cuanto