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 “isIntersecting”. Si ésto es cierto, accedo al elemento con element.target y de ahí a la propiedad data-img con el “dataset.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=“lazy”

ejemplo <img src… loading=“lazy” />

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.