No tienes acceso a esta clase

隆Contin煤a aprendiendo! 脷nete y comienza a potenciar tu carrera

Aprende todo un fin de semana sin pagar una suscripci贸n 馃敟

Aprende todo un fin de semana sin pagar una suscripci贸n 馃敟

Reg铆strate

Comienza en:

1D
18H
55M
50S

Intersection Observer

6/20
Recursos

Aportes 14

Preguntas 1

Ordenar por:

驴Quieres ver m谩s aportes, preguntas y respuestas de la comunidad?

o inicia sesi贸n.

Bueno, aqu铆 va mi soluci贸n, la verdad batall茅 con dos cosas:

Donde diablos poner el observer y qu茅 se le ten铆a que pasar y c贸mo cambiar la propiedad src y por ende c贸mo sacar la propiedad data-img.

Ok, primero todo 茅ste c贸digo va en el archivo main.js, decid铆 crear una funci贸n createObserver c贸mo indica en la documentaci贸n de MDN, ya que puede ser que requiera poner otro observador despu茅s:

function createObserver() {
    return new IntersectionObserver((elements) => {
        elements.forEach(element => {
            if (element.isIntersecting)
                element.target.setAttribute(
                    'src',
                    element.target.dataset.img
                )
        })
    })
}

//creo el observer,.
let observer = createObserver()

Despu茅s en la funci贸n createMovies en lugar de cargar la propiedad src, cree una nueva llamada data-img:

movieImg.setAttribute(
            'data-img',
            'https://image.tmdb.org/t/p/w300/' + movie.poster_path
        )

En esa misma funci贸n, al crear el elemento de Imagen, lo meto al observer:

movieContainer.appendChild(movieImg)
container.appendChild(movieContainer)
observer.observe(movieImg)

y en el callback pregunto si lo estoy viendo con la propiedad 鈥渋sIntersecting鈥. Si 茅sto es cierto, accedo al elemento con element.target y de ah铆 a la propiedad data-img con el 鈥渄ataset.img鈥. Como se ve, todo lo que creemos con data-algo va a ir en el dataset.algo. Si quieren saber m谩s, aqu铆..

Espero que mi soluci贸n sea parecida a la que ver茅 en unos minutos. debo decir que me tard茅 bastante y si el profe tiene una soluci贸n mas sencilla dejo de estudiar por hoy.

Voy a dejar dos v铆deos que, pueden ayudarte a practicar; adem谩s, complementar a煤n m谩s el conocimiento del curso.

Pues dure como 4 horas haciendo y tratando de entender lo mejor que puede, tengo un peque帽o bug que no me funciona con #trends pero por ahora con el scroll horizontal funciona bien.

Tengo el c贸digo separado por modulos as铆 que mi archivo lazy.js es el siguiente:

const loadImage = (entry) => {
    const imgNode = entry.target;
    const url = imgNode.dataset.src;
    imgNode.src = url;
    console.log(imgNode);

    observer.unobserve(imgNode);
}

const observer = new IntersectionObserver((entries) => {
    entries.forEach(entry => {
        if (entry.isIntersecting) {
            loadImage(entry);
        }   
    })
}, {
    root: null,
    rootMargin: '0px',
    threshold: 0.2
});


const registerImage = (images) => {
    observer.observe(images)
}


export default registerImage;  

en la funci贸n de createMovie() le agrege un registerImage y configurar un data.src

function createMovie(movies, container) {
    /*Clear html of page */
    container.innerHTML = "";

    movies.map(movie => {

        const movieContainer = document.createElement('div');
        movieContainer.classList.add('movie-container');
        //Agregamos evento para llevar a movieDetail
        movieContainer.addEventListener('click', () => {
            location.hash = '#movie=' + movie.id;
        })

        const movieImg = document.createElement('img');
        movieImg.classList.add('movie-img');
        movieImg.setAttribute('alt', movie.title);
        movieImg.dataset.src = `${IMG_URL}${movie.poster_path}`;
        registerImage(movieImg);
        movieContainer.appendChild(movieImg);
        container.appendChild(movieContainer);
    });
}

Mi resumen:
.
Intersection Observer nos permite observar cambios. Es decir, cuando un elemento se intersecta con el wiport
Vamos a tener targets (elementos) y veremos cuando entran dentro del scroll. Luego, le diremos cu谩l de esos queremos observar. As铆 podremos mostrar, ocultar, cargar, etc.
.

  1. Tenemos que crear un observador 鈫 Una instancia del Intersection Observer
  2. Callback

.

Podemos crear observador por cada contenedor que muestra im谩genes.
U observar el root. Esto har铆a que el observador est茅 m谩s cargado, pero no tendr铆amos que hacer tantos observadores.
La primera opci贸n puede aumentar la velocidad de carga. La recomendaci贸n del profe es hacer la segunda y si no estamos conforme con los resultados ir por la primera.
.

M谩s informaci贸n en esta clase de Manipulaci贸n del DOM

interesante que al aplicar el intersection observer notaba un comportamiento raro para lo que ser铆a las categor铆as, ya que siempre se cargaban todas las im谩genes, sin embargo era porque el contenedor principal 鈥.genericList-container鈥 no ten铆a un alto definido, comenc茅 a jugar con un min-height y al menos con unos 1000px me cargaban 8 im谩genes y ya el resto se cargaba conforme se hacia el scroll, de repente hay alguien que resolvi贸 esto de otra manera, por favor comenten para seguir aprendiendo.

Este tema tambien se ve en el curso de manipulacion del Dom, ademas se puede usar el atributo loading=鈥渓azy鈥

ejemplo <img src鈥 loading=鈥渓azy鈥 />

solo hay que ver la compatibilidad de los navegadores en
can i use

Rayos!!! que complejo pero que util鈥

Jmm estaba preocupado cuando vi que muchos se complicaron con funciones, condiciones y dem谩s cosas que a mi se mi hicieron innecesarias

Yo simplemente cre茅 una constante con las opciones, y la funci贸n que utilizar铆a como callback fuera de la funci贸n que anteriormente hice para crear las movie cards:

const options = {
    root: null,
    threshold: 0.1,
}
function loadImg(entries) {
    entries.forEach(entry => {
        if (entry.isIntersecting) {
            const img_src = entry.target.getAttribute('img_src')
            entry.target.src = img_src
        }
    })
}

Y ahora en la funcion encargada de crear las movie cards inserto el observer a cada imagen:

function createMovieCard(movie) {
    ...
    const movie_img = document.createElement('img');
    ...

    movie_img.setAttribute('img_src', `https://www.themoviedb.org/t/p/w600_and_h900_bestv2${movie.poster_path}`)
   ...
    movie_img.addEventListener('click', () => {
        location.hash = `#movie=${movie.id}`
    });
    ...
    
    let target = movie_img
    observer.observe(target)

    return scroller_card
}

Si esto podr铆a ser mejorable o tiene alguna desventaja sobre alguna otra forma de hacerlo d铆ganmelo por favor

Logrado, insist铆, estuve a punto de rendirme, pero lo logr茅, con ayuda de los apuntes del curso de manipulaci贸n del DOM, el proyecto de Lazy Loading.

Aqu铆 el c贸digo:

let callback =(entries) => {
    entries.forEach(element => { 
        if(element.isIntersecting){
            const image = element.target
            const url = image.dataset.src
            image.src = url
        }
    })
}
let observer = new IntersectionObserver(callback)

const registerImage = (imagen) => {
    observer.observe(imagen);
}

y lo llam茅 en la funci贸n que cre las im谩genes:

function createMovies(movies, container){
    container.innerHTML="";
    movies.forEach(movie =>  {
        const movieContainer =document.createElement('div')
        movieContainer.classList.add('movie-container');

        // evento de click para acceder a los detalles de la pel铆cula
        movieContainer.addEventListener('click', () => location.hash = '#movie=' + movie.id);

        const movieImg = document.createElement('img')
        movieImg.classList.add('movie-img')
        movieImg.setAttribute('alt', movie.title);
        
        movieImg.dataset.src = 'https://image.tmdb.org/t/p/w300'+ movie.poster_path
        // movieImg.setAttribute(
        //     'src', 
        //     'https://image.tmdb.org/t/p/w300'+ movie.poster_path,
        //     );
            movieContainer.appendChild(movieImg)
            container.appendChild(movieContainer)
            
        // intersection observer
        registerImage(movieImg)
    });
}

Mi soluci贸n:

Intersection Observer example (or should I say masterclass XD)

Mi solucion fue bastante simple, pero funciona bien, en la funcion que uso para cargar todas las imagenes ejecuto una funcion que es la siguiente鈥

function setObserver (element, url, container) {

  const options = {
    root: container,
  }

  const observer = new IntersectionObserver((entries) => {
    entries.forEach(entry => {
      console.log(entry)

      entry.isIntersecting && element.setAttribute('src', `https://image.tmdb.org/t/p/w300/${url}`)
    })
  },options)

  observer.observe(element)
}

por si les interesa ver como es llamada tambien les dejo el codigo de la funcion que carga las imagenes

function imagesList(containerHTML, data) {
  containerHTML.innerHTML = ''

  data.forEach((movie, index) => {
    const movieContainer = document.createElement('div')
    movieContainer.classList.add('movie-container')
    movieContainer.classList.add('movie-container--loading')
    movieContainer.addEventListener('click',  () => location.hash = `#movie=${movie.id}`)
    movieContainer.style.animationDelay = `.${index}s`

    const movieImg = document.createElement('img')
    movieImg.classList.add('movie-img')
    movieImg.setAttribute('alt', movie.title)
    setObserver(movieImg, `https://image.tmdb.org/t/p/w300/${movie.poster_path}`, containerHTML)
    movieImg.addEventListener('load', () => movieContainer.classList.remove('movie-container--loading'))
    
    movieContainer.appendChild(movieImg)
    containerHTML.appendChild(movieContainer)
  }); 

aclaro por si acaso, el evento load que escucho es solo para que la imagen tenga la animacion de carga hasta que ya este renderizada para el usuario, esto se hace individualmente para cada imagen y con el delay genera una animacion con toda la lista

Despues de leer la documentaci贸n y algunos ayuditas de google llegue a sta soluci贸n.

let options = {
    rootMargin: '0px',
    threshold: 0.1
}

const callback = (entries) => {

    console.log(entries);
    entries.forEach((entry) => {

        if(entry.isIntersecting){
            let currentElement = entry.target;
            let image = entry.target.dataset.image;
            entry.target.src = image;
            observer.unobserve(currentElement);
        }
    });
}

let observer = new IntersectionObserver(callback, options);

export const addImage = (image) => observer.observe(image);

La funci贸n de addImage la agregue a la funcio艅 principara para cargar todo tipo de imagenes y peliculas y la mande a llamar dentro del map para registrar cada una de las imagenes.
Por lo que vi es buena practica no seguir observando en cada momento el elemento.

import { CustomAxios , d} from '../customAxios/customAxios.js';
import { addImage } from '../lazyLoading/observerImage.js';
export const getAndAppendMovies = async (path, parentContainer, optionalConfig = {}) => {
    try {
        const { data } = await CustomAxios(path, optionalConfig);
        const movies = data.results;
        const $fragment = d.createDocumentFragment();
        
        //console.log(data);
        if(data.total_results === 0) {

            parentContainer.innerHTML = `
                <h2 style="color: #f00; height: 60vh;">
                    Total de resultados: ${data.total_results}
                </h2>
            `;
            return;
        }

        movies.map( movie => {

            console.log("Pelicula creada!");
            const $movieContainer = d.createElement('div');
            $movieContainer.classList.add('movie-container');

            const $img = d.createElement('img');
            $img.classList.add('movie-img');
            $img.dataset.image = `https://image.tmdb.org/t/p/w300/${movie.poster_path}`;
            $img.dataset.id = movie.id;
            $img.alt = `${movie.title}`;
            $movieContainer.appendChild($img);
            $fragment.appendChild($movieContainer);

            //Aqu铆 est谩!
            addImage($img);
        });

        parentContainer.innerHTML = '';
        parentContainer.appendChild($fragment);
    }catch(err) {

        console.log(err);
    }
    
};```

Primer comentario.