Comparte el resultado
Clase 25 de 29 • Curso de Manipulación del DOM
Contenido del curso
Clase 25 de 29 • Curso de Manipulación del DOM
Contenido del curso
Carlos Eduardo Gomez García
Sebastián Buitrago
Celia Cruz
Alonso Cangalaya
John F Chacpi Marchena
Camilo Gonzalez
David Rolando Balderramos Mendoza
José Zárate Vera
William Santiago Alzate Barriga
Jose Anibal Garcia Giraldo
John Ruiz
Max Andy Diaz Neyra
Max Andy Diaz Neyra
Marco Marin
Fernando Enrique Zepeda Castellanos
Bryan Hernández
Diego Alexander Guatibonza Bello
Roger Colquehuanca
Jonathan 🦑 Alvarez
Roger Colquehuanca
Daniel Alberto Esquinazi
Jonathan 🦑 Alvarez
Miguel Angel Reyes Moreno
Lorenzo Peñalver
Brandon Argel Verdeja Domínguez
Carlos Iván Ochoa Gómez
Luis Alejandro Vera Hernandez
Joaquin Ariel Tejerina
Juan Diego Loaiza Martinez
Christian Boffill
Jorge luis castro marchan
Oscar J. Carabali
Vale, para agregar el fondo gris lo que hice fue crear un wrapper que encerrara a la imagen y que se adaptara al tamaño de la misma con un mínimo de 100px, y este wrapper tiene el fondo gris, así cuando se inserta sale con fondo gris:
... imagen.width = "320"; imagen.dataset.src = `https://randomfox.ca/images/${random()}.jpg` const imageWrapper = document.createElement("div"); imageWrapper.className = "bg-gray-300"; imageWrapper.style.minHeight = "100px"; imageWrapper.style.display = "inline-block"; imageWrapper.appendChild(imagen); container.appendChild(imageWrapper); ...
Para eliminar las imagenes, le puse id a los botones:
<button class="p-3" id="add">Agregar Imagen</button> <button class="p-3" id="clean">Limpiar</button>
En JavaScript los selecciono y les pongo sus eventos, para eliminarlos simplemente recorro todos los nodos hijos de mountNode, los recorro y los elimino:
const cleanImages = () => { console.log(mountNode.childNodes); [...mountNode.childNodes].forEach(child => { child.remove(); }) } addButton.addEventListener("click", addImage) cleanButton.addEventListener("click", cleanImages)
Y para imprimir los logs, en el HTML agregue variables y una función super globales para tener acceso desde index.js y desde lazy.js:
<script> let appendedImages = 0; let loadedImages = 0; const printLog = () => { console.log(`⚪ Se han agregado ${appendedImages} imágenes`); console.log(`🟣 Se han cargado ${loadedImages} imágenes`); console.log("---------------------------------------"); } </script> <script type="module" src="%PUBLIC_URL%/_dist_/index.js"></script>
Entonces, en la función createImageNode antes de retornar el container, aumento uno a las imagenes que se han agregado y llamo a la función:
... imageWrapper.appendChild(imagen); container.appendChild(imageWrapper); appendedImages++; printLog(); return container; ...
Y dentro de lazy.js en loadImage antes de dejar de observar hago lo mismo, pero aquí incremento las imágenes cargadas:
const loadImage = entry => { const container = entry.target; const imagen = container.firstChild; const imagen = container.querySelector("img"); const url = imagen.dataset.src; imagen.src = url; loadedImages++; printLog(); observer.unobserve(container) }
Y listo :D . El código completo está aquí por si quieren darle un vistazo: . Resolución del reto
bro porque no agregas la rama de gh-pages
Yo borre las imagenes asi ...
deleteButton.addEventListener('click', deleteImages) const deleteImages = () => mountNode.innerHTML = ''
Si se pudo!! En mi caso, usé webpack como builder 😄 Si le quieren dar un ojo se los dejo aquí
el diseño se ve espectacular!
Les comparto mi proyecto 😁 El curso ha estado genial!!! sitio: https://findafox.netlify.app/ repo: https://github.com/jmilo13/findFox/
Compañero, falta reiniciar el contador de imágenes cargadas al darle clic en limpiar. Saludos :D
Como adicional al primer reto, seria esencial agregar un icono de cargando al recuadro gris, esto le dará una mejor percepción al usuario de lo que esta sucediendo con la imagen. En la siguiente url podrán encontrar un ejemplo de cómo crear este icono con solo html y css
Les comparto mi solución al Reto 🎉 Aquí el repo y aquí el resultado 😁
Wow,
Muy bien te quedo, tengo muchas dudas con JS para poder desarrollar este reto, pero voy a estudiar tu repo. Muchas gracias
Hola camaradas, les dejo mi solución de los retos: ++index.js++
import { registerImage, resetLogState } from "./lazy"; import { createImageNode } from "./utils"; const API = 'https://randomfox.ca/floof/'; const mountNode = document.querySelector('#images') const btnAdd = document.querySelector('button[type="submit"]'); const btnClean = document.querySelector('button[type="reset"]'); const addImage = async ()=>{ const [node, Image] = await createImageNode(API) registerImage(Image); mountNode.append(node); } btnAdd.addEventListener('click', addImage); btnClean.addEventListener('click', ()=>{ mountNode.innerHTML = ""; resetLogState(); })
++utils.js++
const fetchUrl = async (API)=> { const response = await fetch(API); // const data = await response.json(); // const url = data.image; const { image : url } = await response.json(); return url; } export const createImageNode = async (API)=>{ const container = document.createElement("div"); container.classList.add('p-4'); const image = document.createElement("img"); image.dataset.src = await fetchUrl(API); // image.classList.add('mx-auto'); image.className = "mx-auto rounded-md bg-gray-300"; image.width = 320; container.append(image); return [container,image] }
++lazy.js++
let appendedImages = 0; let loadedImages = 0; const printLog = () => { console.log(`⚪ Se han agregado ${appendedImages} imágenes`); console.log(`🟣 Se han cargado ${loadedImages} imágenes`); console.log("---------------------------------------"); } const isIntersecting = (entry)=> entry.isIntersecting // true si esta dentro de la pantalla const loadImage = (entry)=>{ loadedImages++; printLog() const img = entry.target; img.src = img.dataset.src; observer.unobserve(img); } const observer = new IntersectionObserver((entries)=>{ const entry = entries[0]; if(isIntersecting(entry)) loadImage(entry) }); export const registerImage = (image) =>{ // IntersectactionObservador -> observer(image) appendedImages++; printLog() observer.observe(image); } export const resetLogState = ()=>{ appendedImages = 0; loadedImages = 0; }
Les dejo mi sitio lazy-fox: https://modozen.github.io/lazy-fox/
Ahí va mi Lazy Loading. Tuve que ver el repositorio del profesor para el tema de la imagen gris, pero entendí el funcionamiento.
El uso de clases para el pintado de los estatus de las imágenes me ha funcionado, aquí les comparto:
Creamos la clase, la inicializamos y la exportamos (lazy.js).
La clase cuenta con métodos públicos y privados para su encapsulamiento y los métodos "getAppended o getLoaded" pueden o no ser públicos dependiendo de la necesidad.
El método "clear" en mi caso aplique una limpieza en consola.
class ImageLoaded { #appendImages = 0; #loadedImages = 0; #getAppended(){ return this.#appendImages; } #getLoaded(){ return this.#loadedImages; } addAppend(){ this.#appendImages += 1; } addLoad(){ this.#loadedImages += 1; } clear(){ console.clear(); this.#appendImages = 0; this.#loadedImages = 0; } printLog() { console.log(` ⚪ Se han agregado ${this.#getAppended()} imágenes 🟣 Se han cargado ${this.#getLoaded()} imágenes ---------------------------------------`); } } export const imageStatus = new ImageLoaded();
//imageStatus const accion = ({ target }) => { const imagen = (target.firstChild).firstChild; imagen.src = imagen.dataset.src; imageStatus.addLoad(); imageStatus.printLog(); observer.unobserve(target) }
import { registerImage, imageStatus } from './lazy'; //... const addButton = document.querySelector('.btn-add'); const cleanButton = document.querySelector('.btn-clean'); const addImage = () => { const newImage = createImageNode(); container.appendChild(newImage) registerImage(newImage) imageStatus.addAppend(); imageStatus.printLog() }; const cleanImage = () =>{ const allImages = [...container.querySelectorAll('div')]; allImages.forEach(img => img.remove()) imageStatus.clear(); } addButton.addEventListener('click', addImage); cleanButton.addEventListener('click', cleanImage);
Amigo, estuvo buenísimo. Me inspiré en tu aporte para hacer el mío, me retó mucho porque nunca había usado clases en Javascript. Me costó mucho pero al final, con dedicación, paciencia e investigación lo logré hacer por mí mismo jeje Salvo que yo usé métodos y propiedades estáticas para no hacer instancia de la clase a utilizar, desconozco si fue buena práctica lo tuyo o lo mío jeje. Saluditos y sigue así!
Index.js
lazy
Como lograr este efecto de un loading para cada imagen de una lista api json con fetch?
quería lograr un loading a cada Imagen el texto carga rápido pero la imagen demora un poco mas.
Vi que lo hacen con imágenes estáticas pero quiero lograrlo en un api json.
Hola hola!
Los loading state consisten en lo siguiente:
Así que en efecto se hace con una imágen estática que ya esté en el proyecto, o con CSS si es una animacíon simple: https://loading.io/css/
¿Tienes otra cosa en mente para tu animación?
Gracias @jonalvarezz Espero lograr este efecto en web
Los datos es de un Web Api json y hay un loading previo por cada imagen(como un placeholder) hasta que este listo la imagen original
Como generaste la imagen en base64?
const image = document.createElement("img"); image.className = "mx-auto rounded-md bg-gray-300"; image.src = "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzIwIiBoZWlnaHQ9IjMyMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB2ZXJzaW9uPSIxLjEiLz4=";
Ah pero que buena pregunta! Todo está inspirado en como Next.js lo hace: https://github.com/vercel/next.js/blob/canary/packages/next/client/image.tsx#L391
De allí lo saque y también la técnica de usar la imagen base64. (aunque veo que ya actualizaron el valor y cambió)
Bien, esto fue lo que yo hice :D index.html :
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <link rel="icon" href="%PUBLIC_URL%/favicon.ico" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="description" content="Web site created using create-snowpack-app" /> <!-- Snowpack will replace %PUBLIC_URL% with the value you have for `baseUrl` in `snowpack.config.js`. Make sure to update it with your GitHub Page URL. https://<your-username>.github.io/<your-repo-name> More about config options: - https://www.snowpack.dev/reference/configuration#buildoptions.baseurl --> <link rel="stylesheet" type="text/css" href="%PUBLIC_URL%/_dist_/index.css" /> <title>Snowpack & Tailwind App</title> </head> <body> <!-- This HTML file is a template. To begin the development, run `npm start` or `yarn start`. To create a production bundle, use `npm run build` or `yarn build`. Start modyfing from here. --> <div class="py-10 max-w-screen-xl mx-auto px-4 sm:px-6 lg:px-8"> <div class="text-center"> <p class="text-base leading-6 text-indigo-600 font-semibold tracking-wide uppercase" > Platzi </p> <h3 class="mt-2 text-3xl leading-8 font-extrabold tracking-tight text-gray-900 sm:text-4xl sm:leading-10" > Fox API </h4> <p class="mt-4 max-w-2xl text-xl leading-7 text-gray-500 mx-auto"> Coleccionando imágenes de Zorros </p> <button class="button" id="buttonFox">Click me to see a new fox!</button> <button id="deleteFoxes" class="buttonClear">Clean images</button> <div id="app"></div> </div> </div> <!-- Add more scripts here --> <script type="module" src="%PUBLIC_URL%/_dist_/index.js"></script> </body> </html>
index.css :
@import 'tailwindcss/dist/base.css'; @import 'tailwindcss/dist/components.css'; @import 'tailwindcss/dist/utilities.css'; .button, .buttonClear{ color: white; padding: 5px; border-radius: 5px; } .button{ background-color: orange; } .buttonClear{ background-color: rgb(255, 81, 0); }
index.js :
import {registerImage} from './lazy'; const maximum = 122; const minimum = 0; const random = () => Math.floor(Math.random() * (maximum - minimum) + minimum); const createImageNode = () => { const container = document.createElement('div'); container.className = "p-4 bg-gray-200 mt-5"; const image = document.createElement('img'); image.className = 'mx-auto'; image.width = '320'; image.dataset.src = `https://randomfox.ca/images/${random()}.jpg`; container.append(image); return container; } const mountNode = document.getElementById('app'); const buttonFox = document.getElementById('buttonFox'); const buttonClear = document.getElementById('deleteFoxes'); var imagenesCargadas = 0; const addImage = () => { const newImage = createImageNode(); mountNode.append(newImage); registerImage(newImage); imagenesCargadas += 1 console.log(`Se han cargado ${imagenesCargadas} fotos`) } buttonFox.addEventListener('click' , addImage); buttonClear.addEventListener('click' , () => { let containerWithImage = document.querySelectorAll('#app .p-4'); //!Obtiene nodeList containerWithImage = Array.from(containerWithImage); //!Convierte a Array containerWithImage.forEach(element => { element.remove(); }); }) //*Las líneas de abajo ya muestran 1 imagen por defecto //const nuevaImagen = createImageNode(); //mountNode.append(nuevaImagen);
lazy.js :
const isIntersecting = entry => { return entry.isIntersecting //* true si está dentro de la pantalla del navegador } var imagenesMostradas = 0; const loadImage = (entry) => { const container = entry.target; //* div const image = container.firstChild; const url = image.dataset.src; //*carga imagen image.src = url; observer.unobserve(container); imagenesMostradas += 1; console.log(`Se han mostrado ${imagenesMostradas} fotos a la vista del navegador`) } const observer = new IntersectionObserver((allEntries) => { allEntries .filter(isIntersecting) .forEach(loadImage) }) export const registerImage = image => { observer.observe(image) }
Me gustó tu forma de contar las imágenes!
Fondo gris a las imágenes sólo se lo agregué con una clase de tailwind:
img.className = "mx-auto my-16 bg-gray-200";
Para el botón de borrar lo agregué desde js:
const cleanImages = () => { const gallery = document.getElementById("gallery"); const nodes = gallery.childNodes; if (!nodes.length > 0) return; [...nodes].forEach((node) => { gallery.removeChild(node); }); resetImages(); }; const clean = document.createElement("button"); clean.className = "mx-auto my-4 block bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded transition duration-300 ease-in-out"; clean.textContent = "Borrar imágenes"; clean.type = "reset"; clean.addEventListener("click", cleanImages); const gallery = document.createElement("div"); gallery.id = "gallery"; container.append(button, clean, gallery);
Y para el conteo de imágenes:
let totalImages = 0; let loadedImages = 0; const isIntersecting = (entry) => entry.isIntersecting; const observer = new IntersectionObserver((entries) => { entries.filter(isIntersecting).forEach((entry) => { const imgNode = entry.target; imgNode.src = imgNode.dataset.src; imgNode.onload = () => { loadedImages++; logState(); } observer.unobserve(entry.target); }); }); export const registerImage = (image) => { observer.observe(image); totalImages++; logState(); }; export const resetImages = () => { totalImages = 0; loadedImages = 0; logState(); }; function logState() { console.group("Lazy loading"); console.log(`⚪️ Total Imágenes: ${totalImages}`); console.log(`🟣 Imágenes cargadas: ${loadedImages}`); console.log(`🟡 Imágenes por cargar: ${totalImages - loadedImages}`); console.groupEnd(); }
Asi quedo
html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <link rel="icon" href="%PUBLIC_URL%/favicon.ico" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="description" content="Web site created using create-snowpack-app" /> <!-- Snowpack will replace %PUBLIC_URL% with the value you have for `baseUrl` in `snowpack.config.js`. Make sure to update it with your GitHub Page URL. https://<your-username>.github.io/<your-repo-name> More about config options: - https://www.snowpack.dev/reference/configuration#buildoptions.baseurl --> <link rel="stylesheet" type="text/css" href="%PUBLIC_URL%/_dist_/index.css" /> <title>Random fox</title> </head> <body> <!-- This HTML file is a template. To begin the development, run `npm start` or `yarn start`. To create a production bundle, use `npm run build` or `yarn build`. Start modyfing from here. --> <div class="py-10 max-w-screen-xl mx-auto px-4 sm:px-6 lg:px-8"> <div class="text-center"> <p class="text-base leading-6 text-indigo-600 font-semibold tracking-wide uppercase" > Manipulando el Dom </p> <h3 class="mt-2 text-3xl leading-8 font-extrabold tracking-tight text-gray-900 sm:text-4xl sm:leading-10" > Random Fox </h4> <p class="m-4 max-w-2xl text-xl leading-7 text-gray-500 mx-auto"> Manipulacion del DOM con API de <span> <a href="https://randomfox.ca/" target="_blank">RandomFox</a></span> </p> <button id="addButton" class="p-3">Agregar Imagen</button> <button id="removeButton" class="p-3">Remover Imagenes</button> <div id="images"> </div> </div> </div> <!-- Add more scripts here --> <script type="module" src="%PUBLIC_URL%/_dist_/index.js"></script> </body> </html>
index.js
/** * This file is just a silly example to show everything working in the browser. * When you're ready to start on your site, clear the file. Happy hacking! **/ import { registerImage } from "./lazy"; const URL = "https://randomfox.ca/images"; const maximum=122; const minimum= 1; //crea un numero aleatorio para traer las imagenes const random = () => Math.floor(Math.random() * (maximum - minimum)) + minimum // reporte en consola de cuantos argumentos han traido y estan cargados const reportImgs = { imgLoaded: 0, totalImg: 0 } const stopContentLoader = (event) => { const img = event.target; // img.parentElement.lastChild.remove(); img.style.width = '320px'; img.style.height= '320px'; reportImgs.imgLoaded++; // debugger; showReportImg(); } const showReportImg = () => { console.log('----------------------------------------') console.log(`%c⚪Total Imgs: ${reportImgs.totalImg}`, 'color: white; font-size: 0.8rem'); console.log(`%c🟣Total Cargadas: ${reportImgs.imgLoaded}`, 'color: hotpink; font-size: 0.8rem'); console.log('----------------------------------------') } const allElementsDeleted = () => { console.log(`⚠ Todas las imagenes fueron eliminadas`); } const createImageNode = () => { const container = document.createElement('div'); container.className = 'flex flex-col items-center p-4 mx-auto'; const img = document.createElement('img'); img.className = 'mx-auto rounded bg-gray-300'; img.width = "320"; img.style.minHeight = '300' img.dataset.src = `${URL}/${random()}.jpg`; // TODO img.onload = stopContentLoader; //onload llama una funcion container.appendChild(img); return container; }; const nuevaImagen = createImageNode(); const addImage = () => { const newImage = createImageNode(); mountNode.append(newImage); registerImage(newImage); reportImgs.totalImg++; showReportImg(); } const removeAllImg = () =>{ mountNode.innerHTML = ""; reportImgs.imgLoaded = 0; reportImgs.totalImg = 0; allElementsDeleted() } //captura la etiqueta img del html const mountNode = document.getElementById('images') //captura el button del html const addButton = document.querySelector('#addButton'); addButton.className = 'text-white px-3 py-2 rounded-lg bg-indigo-600 focus:outline-none'; const removeButton = document.querySelector('#removeButton') removeButton.className ='rounded-lg mt-5 px-3 py-2 text-indigo-600 border border-indigo-600 focus:outline-none'; addButton.addEventListener('click', addImage); removeButton.addEventListener('click', removeAllImg);
lazy.js
const isIntersecting = (entry) => entry.isIntersecting; //true (dentro de la pantalla) const loadImage = (entry) => { const container = entry.target; //regresa un container (DIV) const imagen = container.firstChild; const url = imagen.dataset.src //cargar la imagen. imagen.src=url // console.log(entry); //desregistrar el evento observer.unobserve(container); }; const observer = new IntersectionObserver((entries)=>{ entries.filter(isIntersecting).forEach(loadImage); }); export const registerImage = (imagen) => { //intersectationObservador -> observer(imagen) observer.observe(imagen) }
Esta muy, buena tu solución para mostrar las imagenes totales y las imagenes cargadas,yo copie la parte del index.js, le agregue al de lazy.js Gracias por la solución, yo no sabia muy biien como manejarlo, sos un grande!!
lazy.js
import { reportImgs, showReportImg } from'./index' const isIntersecting = (entry) => { // console.log("entry de isIntersecting: ",entry) // console.log("entry.target.isConnected de isIntersecting: ",entry.target.isConnected); // SI esta a 200px de la pantalla hacer x o y cosa return entry.isIntersecting // true ( dentro de la pantalla) } const loadImage = (entry) => { const container = entry.target; // div que tiene adentro a la imagen const imagen = container.firstChild; const url = imagen.dataset.src; //load image imagen.src = url; // unobserve observer.unobserve(container) reportImgs.imgLoaded++; showReportImg(); } const observer = new IntersectionObserver((entries) => { // console.log("entries de: IntersectionObserver: ",entries); entries.filter(isIntersecting).forEach(loadImage) }) export const registerImage = (imanen) => { // IntersectionObserver => observer(imagen) observer.observe(imanen) };
Aquí les dejo mi taller: https://christbm.github.io/workshop2/
Mi solución, para eliminar las imagenes hice lo siguiente:
const cleanImages = () => { const nodeElementsImages = mountNode.children const listaElements = [...nodeElementsImages] listaElements.forEach((element) => element.remove()) } //Agregamos el evento cleanButton.addEventListener('click', cleanImages)
Para agregar el fondo gris :
const imagen = h('img.mx-auto', { width: 320, height : 300, style: { background:'grey' }, "data-src" : `https://randomfox.ca/images/${random()}.jpg`, })
Tengo una pregunta, como hago para hacer este efecto en una imagen de background. He estado intentandolo, creo la imagen background desde js pero me sale error y quiero saber como hacerlo.