No tienes acceso a esta clase

¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera

Infinite Scrolling: evento de scroll

11/20
Recursos

Aportes 26

Preguntas 7

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad?

o inicia sesión.

Passive lo que hace es evitar el llamado de preventDefault() en el caso de que este existiese en la función llamada por el Listener. En los navegadores que usa la gente normal el valor por defecto es false por lo que no se aplica, pero en el caso de Safari e Internet Explorer el valor por defecto es true. Por lo que supongo que es recomendable ponerle un valor para que el código se ejecute igual en todos los navegadores.

esta clase la vi mas veces de lo que me gustaría admitir

Por lo que entendí al leer la documentación, el tercer parámetro que se le envía al EventListener es opcional y especifica las condiciones en la que se ejecuta el mismo.

addEventListener(type, listener, options);

Existen varias opciones, como use, signal, passive y capture, este último es el que se usa por defecto al colocar un valor booleano, como es el caso. Lo que determina capture es el orden en el que se ejecutan los eventos cuando existen varios que responden a la misma acción. Al estar en false el orden de ejecución es desde el padre del evento(por ejemplo un botón) a la raiz del árbol (DOM), y en el caso de ser true el orden se invierte, por lo que se priorizan los elementos más cercanos al DOM en orden jerárquico. Primero se ejecutan los eventos que tengan capture true(del más grande al más chico) y luego los false (del más chico al más grande).
.
Esta imágen me ayudo a entenderlo.

Capture phase = true
Bubbling phase = false (por defecto)
Target Phase = elemento “mas pequeño” que escucha al evento

documentación EventTarget.addEventListener()

A esto se refiere el tercer parámetro del EventListener: Bubbling y Capturing
En el siguiente link hay una explicación detalla con ejemplos interactivos LINK

No habia entendido como funcionan y se relacionan los atributos clientHeight, scrollTop y scrollHeight. Leyendo los entendi asi que este es un resumen de como funcionan y se relacionan:
.
1- document.documentElement.clientHeight: Devuelve la altura VISIBLE del elemento documentElement en pixeles incluyendo el padding del elemento pero no el border, ni el margin del elemento ni la barra de dezplazamiento horizontal del navegador.
.
Como clientHeight NO devuelve la altura total de un elemento sino la altura visible del elemento, entonces si el tamaño de pantalla del dispositivo se reduce o si el tamaño de la ventana del navegador se reduce entonces la altura visible del elemento tambien se reducira por lo tanto el valor de .clientHeight se reducira y visceversa.

.
2- document.documentElement.scrollTop: Devuelve el numero de pixeles desplazados al hacer scroll vertical en el elemento documentElement.
.
TENER EN CUENTA QUE SI EL TAMAÑO DE PANTALLA DEL DISPOSITIVO SE REDUCE O SI EL TAMAÑO DE LA VENTANA DEL NAVEGADOR SE REDUCE ENTONCES EL VALOR DE clientHeight SE REDUCIRA Y EL VALOR MAXIMO DE scrollTop AUMENTARA YA QUE AL REDUCIRSE LA VENTANA TOCA HACER MAS SCROLL PARA RECORRER EL ELEMENTO documentElement. SI EL TAMAÑO DE LA VENTANA DEL NAVEGADOR AUMENTA ENTONCES OCURRE LO CONTRARIO
.

3- document.documentElement.scrollHeight: Devuelve la altura total del elemento documentElement en pixeles incluyendo el padding del elemento pero no el border, ni el margin del elemento ni la barra de dezplazamiento horizontal del navegador.
.
Si analizamos nos damos cuenta que la altura total del elemento documentElement es igual a la altura visible de dicho elemento mas el maximo de pixeles desplazados al hacer scroll vertical en dicho elemento, osea, documentElement.scrollHeight es igual a documentElement.clientHeight + documentElement.scrollTop cuando el valor de scrollTop es el maximo

Mi solución al reto del botón fue agregarle un “genericSection.removeChild(‘Element’)” al evento del botón en la función de TrendingMovies y “Paginación

Me costo entender como implementar Infine Scroll en la navegación, pero ahí les va mi explicación:

  1. declaramos la variable page = 1 e inicializamos infiteScroll como undefined.
let page = 1;
let infiniteScroll;
  1. cuando navegemos por la aplicación, si existe algún valor a infiteScroll será removido.
function navigator() {
  console.log({ location });

  if (infiniteScroll) {
    window.removeEventListener('scroll', infiniteScroll, { passive: false });
    infiniteScroll = undefined;
  }

  if (location.hash.startsWith('#trends')) {
    trendsPage();
  1. Cuando ingresemos a la pagina de trendsPage, infiniteScroll tendrá el valor de getPaginatedTrendingMovies. (dependiendo de hacia donde navegues el valor de infinite Scroll va a cambiar para cargar el api de su pagina)
function trendsPage() {
  infiniteScroll = getPaginatedTrendingMovies;
}
  1. termina de cargar la función navigation y ejecuta window.addEventListener(‘scroll’, infiniteScroll,{ passive: false });
function navigator() {
  console.log({ location });

  if (infiniteScroll) {
    window.removeEventListener('scroll', infiniteScroll, { passive: false });
    infiniteScroll = undefined;
  }
  
  if (location.hash.startsWith('#trends')) {
    trendsPage();

  if (infiniteScroll) {
    window.addEventListener('scroll', infiniteScroll, { passive: false });
  }
}

Así fue como entendí como aplicar infiniteScroll en la navegación. Espero que le haya servido a alguien esta explicación.

A mi en lugar de hacer tantos cambios para el infiniteScroll, me funcionó simplemente envolverla en una función flecha:

window.addEventListener('scroll',()=>infiniteScroll());

con eso me ahorré la lógica de remover el listener

yo lo hice un poco distinto, espero que no este mal, use otro intersection observer que vea el footer, funciona perfecto, pero a lo mejor juan no lo uso por algun motivo

passive
Un valor booleano que, si es true , indica que la función especificada por el listener nunca llamará a preventDefault() . Si un oyente pasivo llama a preventDefault() , el agente de usuario no hará nada más que generar una advertencia en la consola. Si no se especifica, el valor predeterminado es false , excepto que en navegadores que no sean Safari e Internet Explorer, el valor predeterminado es true para los eventos wheel , rueda del mousewheel , touchstart y touchmove . Consulte Mejorar el rendimiento del desplazamiento con oyentes pasivos para obtener más información.

en mi reto mi app no es para moviles y todas las peliculas cargan dentro de un div con tamaño fijo… estuve pensando como hacer hasta que recorde la herencia, y el div tiene los mismos parametros scrollTop, ScrollHeight (el clientHeight es el tamaño del div que le pones en css), y funciona igual y es mas exacto, y si quieres usar las misma logica dle profe usa height:auto en tu css

Y si como otra posible solucion habriamos agregado un "div " invisible como lastChild sea cual fuere la seccion que estemos, y un intersection observer que si intersecta al div hacemos el llamado de la funcion?

dentro de la función trendsPage() en el archivo de Navigation.js construí esto al final:

        let counter = 1

        window.addEventListener('scroll', () => {

        const { 
            scrollTop,
            clientHeight,
            scrollHeight } = document.documentElement;
        
        const scrollIsBottom = (scrollTop + clientHeight) >= scrollHeight - 15;

            if(scrollIsBottom){
                counter++
                getTrendingMovies(page = 1 + counter)
            } //this way we make sure we're loading new content only when the scrollIsBottom results TRUE and at the same time we're at the bottom of the page
        })

Así cada vez que se haga scroll crearemos esas variables y la validación para llamar a getTrendingMovies con el counter (que es un número que aumentará en 1 cada vez que llamemos esta función). Además cada vez que nos remitamos a esa pagina de tendencias no cargará todo completamente sino que volverá a cargar solamente las 8 películas iniciales

El ‘false’ al final de un addEventListener siginifica que el evento será capturado durante la Bubbling phase.
.
.

¿Qué significa esto de Bubbling phase?

.
.
Cuando le asignamos un evento a un nodo HTML, existen dos fases o etapas a la hora de procesar eventos.
La fase de Capturing y la fase de Bubbling. Es decir, primero el evento viaja desde todo el documento html pasando por todos los nodos hasta el nodo que queremos, luego desde ese nodo el evento se ejecuta con todos los nodos padres.
La captura viaja de “afuera” hacia “adentro”, y luego la burbuja va de “adentro” hacia “afuera”.
.
.

.
.
El evento va de arriba hacia abajo (Capturing down) y luego de abajo hacia arriba (Bubbling up).
El setear el useCapture como TRUE o FALSE, lo que le dice al evento es en cuál fase quieres que sea ejecutado.
.
useCapture = true
.
El handler estará seteado en la fase Capturing. El evento llegará hasta el nodo antes que a sus hijos. Se ejecutará primero al tocar ese nodo y luego en la fase de burbuja se ejecutarán los eventos de los hijos a los padres.
.
useCapture = false
.
El handler estará seteado en la fase de Bubbling. El evento llegará hasta el nodo después que sus hijos. Es decir, el evento irá hasta el fondo, rebotará en el último hijo y regresará hasta el nodo que señalamos en la fase de bubbling, en el camino de vuelta.
.
Eso quiere decir que si haces esto:

child.addEventListener("click", second);
parent.addEventListener("click", first, true);

.
Cuando le des click a ‘child’ se ejecutará el método ‘first’ antes que ‘second’, porque está useCapture como false (que es su forma por defecto). Es decir, primero el evento se detendrá en el “parent” y luego irá hasta el “child”, rebota el evento y ejecutará ‘second’.

Para mi fortuna ya habia usado el clientHeight y el scrollHeight para mis carruseles de mi pagina de incio (hice un indicador de progreso de scroll), asi que al nombrarlo entendi de lo que hablaban… sin embargo me parece que se complico un poco todo XD pero de nuevo, para mi fortuna yo estoy haciendo mi pagina diferente, solo tengo en cuenta lo que hace el profe pero a estas alturas del proyecto no puedo aplicar muchas de las cosas tal cual el las propone, lo que en cierta forma significa un reto para mi cada nuevo “feature” y eso me agrada.

Mucha suerte compañeros y animo… les dejo un screenshoot de mi pagina de incio :3

Para el infinity scroll yo use otro intersection observer en la última película cargada, y de esta forma volvia a ejecutar la función getTrendingMovies() aunmentando page y luego hacia la validaci’on:

if(lastMovieInterception){
    observadorMoreMovies.unobserve(lastMovieInterception)
  }

Yo solucioné todo el problema del evento scroll usando

    document.onscroll = () => getPaginatedMovies('trending/movie/day')

Por cada página, se llama a getPaginatedMovies con un endpoint diferente. Después se podrían hacer cosas como poner todos los endpoints en variables, llevar a document.onscroll a navigator, etc. pero no me compliqué mucho.

Aquí la función para detectar si el usuario alcanzó el límite de scrolling inferior:

const scrollBottomReached = () => {
    const {scrollTop, scrollHeight, clientHeight } = document.documentElement;
    return (scrollTop + clientHeight) >= (scrollHeight - 15);
}

Que se puede usar elegantemente:

window.addEventListener('scroll', () => {
	if(scrollBottomReached()){
		// Aquí tu lógica
	}
})

Hola, muchas gracias por los aportes anteriores. Excelente clase, he tenido que verla algunas veces. Comparto mi código, con el fin de saber si tiene alguna contra hacerlo de este modo, los resultados en la app son los mismos.

function getTrendingMovies() {    
    createMovies(ENDPOINT_TRENDING, genericSection, {}, { lazyLoad: true, clean: true,});    
};

window.addEventListener('scroll', () => {
    const { scrollTop, scrollHeight, clientHeight } = document.documentElement;
    const scrollIsBottom = ( scrollTop + clientHeight) >= (scrollHeight - 1);
    if (scrollIsBottom) {
        createMovies(ENDPOINT_TRENDING, genericSection, {
            params: {
                page: page++,
            },
        }, {
                lazyLoad: true,
                clean: false,
            },
        );
    };
}); 

Amigos por favor tener en cuenta que en esta parte del codigo:

const { scrollTop, scrollHeight, clientHeight } = document.documentElement;
  const scrollIsBottom = (scrollTop + clientHeight) >= (scrollHeight - 15);

se suma el scrollTop y el clientHeight, yo los tenia restando y dure un buen rato tratando de ver cual era el error, era un signo. Atencion a los pequenios detalles

Yo lo hice con React

import InfiniteSroll from './InfiniteSroll'

function MyComponent() {
	 const onInfiniteScroll = () => console.log('onInfiniteScroll')
	return <InfiniteScroll onInfiniteScroll={onInfiniteScroll}>
		// ... algo como una lista
	</InfiniteScroll>
}
// InfiniteScroll.js
import PropTypes from 'prop-types'
import { useEffect } from 'react'

const InfiniteScroll = ({ children, onInfiniteScroll }) => {
  useEffect(function setupListener() {
    function infiniteScroll() {
      const { scrollTop, scrollHeight, clientHeight } = document.documentElement
      const scrollIsBottom = scrollTop + clientHeight >= scrollHeight - 10
      if (scrollIsBottom) {
        onInfiniteScroll()
      }
    }
    window.addEventListener('scroll', infiniteScroll)

    return function cleanupListener() {
      window.removeEventListener('scroll', infiniteScroll)
    }
  })

  return (
    <div id="wrapper">
      <div id="content">{children}</div>
      <div id="last" />
    </div>
  )
}

InfiniteScroll.propTypes = {
  children: PropTypes.node.isRequired,
  onInfiniteScroll: PropTypes.func.isRequired,
}

export default InfiniteScroll

Mi propuesta propuesta es reutilizar las funciones de petiiciones de datos agregando un parámetro por defecto de página 1 cuando se ejecuta la primera vez, y luego con el evento scroll allí sí colocamos la página que es el page de la variable global. para esto necesitaremos una función para determinar si el scroll está en el umbral

const scrollIsOnThreshold = () => {
    const { scrollTop, clientHeight, scrollHeight } = document.documentElement;
    const scrollDiff = scrollHeight - (scrollTop + clientHeight);
    const scrollIsBottom = scrollDiff <= 30;
    if (scrollIsBottom) page += 1;
    return scrollIsBottom;
};

Si está en el umbral para hacer el trigger entonces aumentamos de página y retornamos el resultado (true), luego creamos el callback infiniteScroll basándonos en esta función y la función promesa reutilizada.

const trendsPage = () => {
    	.
	.
	.
    // Obteniendo los datos a renderizar
    getData.getTrendingMovies();
    // Scroll infinito
    infiniteScroll = () => {
        if (scrollIsOnThreshold()) getTrendingMovies(page);
    };
};

.
.
Mi propuesta en el reposiitorio, commit actual : Click aquíi

Mencionar que para la solución que propone el profesor se tiene que crear una función de más películas para cada vista, sea de búsqueda, de tendencias o de categorías, porque tienen distintos parámetros.

Así quedó mi proyecto.

https://rica999.github.io/BROWSER-MOVIES-MOVIEDB/

Repositorio: https://github.com/rica999/BROWSER-MOVIES-MOVIEDB

Se me hizo infinite scroll absolutamente toda mi app y ni siquiera se porque 😂 solo aplique la lógica de los scrollTop, scrollHeigth, y clientHeigth con el if de validación y nada mas y ahora en cualquier página tengo infinite scroll, ni siquiera use la parte donde el profe declara la variable infiniteScroll ni el eventListener, sin embargo entro en los botones de c/categoria y tengo scroll infinito.
Lo gracioso es que es de las primeras veces que me sale algo y nisiquiera se el porque 😂😱, claramente mi app esta diseñada diferente a la del profe desde el inicio en cuestión de maquetación y eso

Usar un Intersection Observer haria la misma funcion que escuchar el evento scroll en todo el body, no? El nodo a observar ya quedaria en la imaginacion de cada quien… ( a mi se me ocurriria observar el footer o la ultima pelicula…)
Segun un estudio en este articulo, llamar una funcion por cada scroll puede darle demasiada carga al hilo principal. En esta app pequena a lo mejor no hace tanto efecto, pero hay que tomarlo en cuenta. 😃

A mi me funciono todo bien sin usar el false del tercer parámetro del addEventListener.