Contenido del curso
Configuración inicial y maquetación del proyecto
Consumiendo la API
Navegación
Views
Próximos pasos
Retos: historial de navegación y página de tendencias
Contenido del curso
Retos: historial de navegación y página de tendencias
Oscar Santiago Lara Escobar
studentJuan Castro
teacherJosue Cerron Tuesta
studentCarlos Rodríguez
studentCarlos Rodríguez
studentJuan Castro
teacherLUZ ADRIANA MARTINEZ RAMIREZ
studentLUZ ADRIANA MARTINEZ RAMIREZ
studentJuan Castro
teacherRubén Cuello
studentJuan Castro
teacherCristian Soto Repol
studentOscar Fuentes Esteves
studentJuan Castro
teacherOscar Fuentes Esteves
studentSebastián Andrés Sanhueza Tapia
studentHéctor Galindo
studentJesus Hurtado
studentJuan Fernando Yepes Muñoz
studentBrayan Alexis Fajardo Ortiz
studentJohan Sebastian
studentDonovan RM
studentHugo Herrera
studentValentina Hernández
studentFrancisco Arturo Leon Velasco
studentEliezer Hernandez
studentAxel Enrique Galeed Gutierrez
studentVictor Andres Barilin
studentJuan Carlos Domínguez Pérez
studentJorge Sandoval
studentDario Paladines
studentJeison Wu Mitre
studentRicardo Alfonso Chavez Vilcapoma
studentQuedé anonadado con la solución del historial de navegación con el objeto history y su método back() jajaja
Yo había creado un array el cual agregaba el location.hash cada vez que este cambiaba y cuando se le daba click al arrowBtn iba invocando los location.hash guardados para poder generar nuevamente la petición.
let historyArr = []; arrowBtn.addEventListener("click", () => { if (historyArr.length > 1) { location.hash = historyArr[historyArr.length - 2]; historyArr.splice(-2,2); } else { historyArr.pop(); location.hash = "#home"; } }); function navigator() { if(location.hash.startsWith("#trends") || location.hash.startsWith("#search=") || location.hash.startsWith("#movie=") || location.hash.startsWith("#category=")){ historyArr.push(location.hash) } }
Tu solución me gusta mucho más. Felicitaciones. :clap:
Yo pense lo mismo.
Una solución supersencilla, podemos agregar una propiedad de carga en el window.history, es decir que cuando de cambie un hostname a otro o vengamos de otro hostname entonces podemos agregar ese href de carga inicial de la siguiente manera:
window.addEventListener( 'DOMContentLoaded', () => { navigator(); // Agregando un estado de carga inical window.history.pushState({ loadUrl: window.location.href }, null, ''); }, false, );
.
Esa propiedad de carga de estado la he llamado loadUrl entonces si cargamos la aplicación desde su inicio el href no deberá contener ningún tipo de hash pero si venimos de youtube por ejemplo entonces el loadUrl nos dará todo el href se esa ruta de carga con todo y hash. Entonces si la ruta de carga inicial contiene el símbolo de hash (#) entonces nos mandará a la home desde el evento click del botón.
arrowBtn.addEventListener('click', () => { const stateLoad = window.history.state ? window.history.state.loadUrl : ''; if (stateLoad.includes('#')) { window.location.hash = ''; } else { window.history.back(); } });
Después de que neveguemos en diferentes rutas de la aplicación el window.history.state se borra dando como resultado null por eso es que la variable stateLoad regresamos un string vacío o lo que queremos realmente que es el window.history.state.loadUrl
En mi caso yo he creado el home para que sea sin hash así:
window.location.hash = '';
. Pero el profe le ha puesto un hash '#home' entonces lo colocan así:
window.location.hash = '#home';
Exactamente esto era lo que esperaba. Muy bien hecho. :clap:
Solucion al history.back()
Documentacion a la solucion del problema same-origin
Atrevido, me gusta
Seguro más de uno habrá notado que la búsqueda funciona bastante bien salvo para los casos en los que el título tenga espacios en medio. Por ej no pueden buscar "Dragon Ball Z", ya que no encontrará resultados.
Una solución simple a este problema es modificar el string que vamos a enviar a la consulta de manera de reemplazar los espacios (%20) del título por espacios de verdad:
La búsqueda funcionará sin problemas luego de eso :smile:
:clap:
hola, también podrías poner
let query = location.hash.split('=')[1]; getMoviesBySearch(decodeURI(query))
esto cambiara los %20 por espacios vacíos
Tal vez es mi código, pero noto lo siguiente:
cuando hago una busqueda por ejemplo la palabra "jojo"
me lleva a la página:
http://127.0.0.1:5500/?#search=jojo
Al hacer back desde el botón con nuestro history.back()
Me regresa a la siguiente URL:
http://127.0.0.1:5500/#search=jojo
Como ven, el history.back() me quita el "?" del parametro, por lo cual provoca tener que hacer un click de más. En el video, todo parece funcionar bien.
¿A alguien más le pasa?
¿Podrías mostrarme tu código, por fa? No debería estar agregando el ?en la URL. Sospecho que algo tendrá que ver con el uso de formularios. No pasa nada, se puede solucionar y no es tan complicado, no te preocupes. Pero necesito el código para ayudarte a revisar. :D
Gracias por la respuesta, mi código lo tengo aquí: Repo Git API REST
PD. Ya quiero que lancen el Curso Profesional de Consumo de API REST con JavaScript. 🙏🏻
Me puse a revisar el history en la consola y tiene un item que cambia y cuenta cuanto hemos avanzado, "history.length" cuando es 1, es la pestaña nueva vacia, cuando es 2 es la url que pegamos, y de ahí en adelante va a contar el historial, entonces sí ese valor es menor a 2, llévame al home, sí es mayor a 2 llevame al historial. Creo que es la solución más sensilla que he visto puesto que solo se agrega el if.
Mi solución al reto:
arrowBtn.addEventListener('click', () => { if (history.length > 1) { history.back() } else { location.hash = "#home" } })
Aqui mi solucion, espero a alguien le sirva!
let historial = [] arrowBtn.addEventListener('click', () => { historial.pop() if (historial.length > 0) { location.hash = '#search=' + historial[historial.length - 1] } else { location.hash = '#home' } })
Puse el botón para volver atrás que recuerda la ultima posición del usuario con este método que se lo robé a alguien en los comentarios.
arrowBtn.addEventListener('click', () => { window.history.back() });
Y por si el usuario quiere volver al home de un solo click, al clapper board le agregue el evento de volver al home
clapperboardHeader.addEventListener('click', () => { location.hash = '#home' })
Por si a alguien le sirve de idea aquí esta como se ve :)
Mi solución:
arrowBack.addEventListener('click', () => { // Get the URL of the previous page const previousUrl = document.referrer; // Check if the previous URL belongs to the same application if (previousUrl.includes(location.hostname)) { // If it does, go back to the previous URL history.back(); } else { // If it doesn't belong to the same application, redirect to the application's home window.location.href = '/'; // Replace '/' with the URL of your home page } });
dom.arrowBtn.addEventListener('click', () => { const previousUrl = document.referrer; // Aca validamos si el dominio de la url de nuestro servidor es la misma de previousUrl if (previousUrl.includes(location.hostname)) { history.back(); } else { location.hash = '#home'; } // location.hash = '#home'; });
Lo único que no logré cuadrar fué que deba darle un solo click a la flecha para devolverme entre busquedas, no entiendo porqué, siempre me hace pulsar dos veces, no sé si alguien le pasó igual?
arrowBtn.addEventListener('click', () => { if (window.history.length <= 2) { location.hash = ''; return } history.back(); });
Sí, me pasa igual
yo utilice el siguiente codigo y me funciono con 1 solo click
arrowBtn.addEventListener('click', () =>{ window.history.go(-2); });
La parte del historial la hice de la siguiente forma: Hice un arreglo en el que guardaría los valores del location.hash:
navigation.js
const historial = []; searchFormBtn.addEventListener('click', () => { location.hash = `#search=${searchFormInput.value}`; historial.push(searchFormInput.value); navigation() }); arrowBtn.addEventListener('click', () => { if(historial.length == 0){ location.hash = '#home'; }else if(historial.length == 1){ location.hash = '#home'; searchFormInput.value = ''; searchFormInput.setAttribute('placeholder', 'Buscar'); historial.pop(); }else{ location.hash = `#search=${historial[historial.length-2]}`; searchFormInput.value = ''; searchFormInput.setAttribute('placeholder', historial[historial.length-2]); historial.pop(); } navigation() });
Y también implemente un setTimeout para que el usuario no tuviera que borrar lo que escribió en la búsqueda:
const searchPlaceholderWait = () => { setTimeout(() => { searchFormInput.value = ''; searchFormInput.setAttribute('placeholder', historial[historial.length-1]); },2000); }
Finalmente la funcion searchPlaceholderWait() la meti dentro de un condicional if, dentro de la funcion searchPage():
if(historial.length != 0){ searchPlaceholderWait(); }
Esta es mi solución:
Les comparto mi solución al reto del historial de navegación. :D Repositorio de GitHub.
const $$ = (id) => document.querySelectorAll(id); const goBackButton = () => { const IS_HASH_HOME = location.hash !== '#home'; const IS_LAST_NOT_HOME = ARRAY_HASHES.at(-1) !== '#home'; if (IS_HASH_HOME) ARRAY_HASHES.pop(); location.hash = (IS_LAST_NOT_HOME) ? `${ARRAY_HASHES.at(-1)}` : '#home'; }; const addHash = () => { const IS_REPEAT = ARRAY_HASHES.some((hash) => location.hash === hash); if (!IS_REPEAT)ARRAY_HASHES.push(location.hash); }; const BUTTONS_GO_BACK = Array.from($$('.header__arrow-left')); const ARRAY_HASHES = [ '#home', location.hash ]; for (const BUTTON of BUTTONS_GO_BACK) { BUTTON.addEventListener('click', goBackButton); } window.addEventListener('hashchange', addHash, false);
Luego de renegar buscando informacion se me ocurrio esta manera
let canBack = 0 const navigator = () => { if (location.hash.startsWith('#trends')) { trends() } else if (location.hash.startsWith('#search=')) { const loc = location.hash const [_, search] = loc.split('=') search ? searchPage(search.replaceAll('%20', ' ')) : homePage() } else if (location.hash.startsWith('#movie=')) { moviePage() } else if (location.hash.startsWith('#category=')) { const loc = location.hash const [id, name] = loc.split('=')[1].split('-') categoryPage(id, name.replace('%20', ' ')) } else { homePage() } canBack++ scroll(0, 0) } arrowBtn.addEventListener('click', () => canBack > 1 ? window.history.back() : location.hash = '#')
en el curso de v8 y el navegador el Profe Diego nos dio una pista de como funcionaban el botón atrás de los navegadores, e hicimos un link list asi que meti cada Location.hash en el valor de cada link list donde el link list por defecto trae al home en el head y cada que el location.hash cambia le agrega un hijo , de este modo el hijo siempre volvera al padre.
class Node { constructor(value){ this.value = value; this.prev = null; this.next = null; } } class MemoryBack { constructor(value){ this.head = { value: value, prev: null, next: null, } this.tail = this.head; this.length = 0; } append(value) { const newNode = new Node(value); newNode.prev = this.tail; this.tail.next = newNode; this.tail = newNode; this.length++; return this; } prepend(value) { const newNode = new Node(value); newNode.next = this.head; this.head.prev = newNode; this.head = newNode; this.length++; } } const memoryBackBtn = new MemoryBack("#home"); console.log("initialbackbutton" , memoryBackBtn); searchFormBtn.addEventListener('click', () => { location.hash = `#search=${searchFormInput.value}`; }); trendingBtn.addEventListener('click', () => { location.hash = '#trends' }); arrowBtn.addEventListener('click', () => { // history.back(); if (memoryBackBtn.length === 1) { memoryBackBtn.head.next = null; memoryBackBtn.tail.value = memoryBackBtn.head memoryBackBtn.tail.next = null; } location.hash = memoryBackBtn.tail.prev.value; memoryBackBtn.tail= memoryBackBtn.tail.prev; memoryBackBtn.tail.prev.next = null; memoryBackBtn.length -= 1; }); window.addEventListener('DOMContentLoaded', navigator, false); window.addEventListener('hashchange', navigator, false); function navigator() { console.log({ location }) if (location.hash.startsWith('#trends')){ trendsPage(); memoryBackBtn.append(location.hash); console.log(memoryBackBtn); } else if (location.hash.startsWith('#search=')) { searchPage(); } else if (location.hash.startsWith('#movie=')) { movieDetailsPage(); memoryBackBtn.append(location.hash); console.log(memoryBackBtn); } else if (location.hash.startsWith('#category=')) { categoriesPage(); memoryBackBtn.append(location.hash); console.log(memoryBackBtn); } else { homePage(); }
bueno mi solución fue que primero cree un array vacío para ir almacenando mi query que lo usare mas adelante como argumento de una función igual a la función de búsquedas
const navigationHistory = []; searchFormBtn.addEventListener('click', () => { location.hash = `#search=${searchFormInput.value}`; const [_, query] = location.hash.split('='); navigationHistory.push(query); });
lo segundo que hice fue que cada ves que el botón arrow back lanza un evento pasa por la condición que cuando el array tiene mas de un elemento valla quitando elementos, dentro de la condición una función para que quite el ultimo elemento y también para que seleccione el ultimo elemento. Luego ese ultimo elemento seleccionado se envía como argumento a la función backHistory que no es mas que una función similar a la de búsqueda. Cuando el array queda con un solo elemento el próximo evento del botón será regresar al home
arrowBtn.addEventListener('click', () => { if(navigationHistory.length >= 2){ function resolve () { navigationHistory.pop(); //ultimo elemento del array const ultimateElement = navigationHistory[navigationHistory.length - 1]; return ultimateElement; } backHistory(resolve()) } else { location.hash = '#home'; } });
y esta es la funcion backHistory que recibe como argumento la query que fuimos almacenando en el array:
function backHistory(query) { console.log('back!!'); console.log(query) headerSection.classList.remove('header-container--long'); headerSection.style.background = ''; arrowBtn.classList.remove('inactive'); arrowBtn.classList.remove('header-arrow--white'); headerTitle.classList.add('inactive'); headerCategoryTitle.classList.add('inactive'); searchForm.classList.remove('inactive'); trendingPreviewSection.classList.add('inactive'); categoriesPreviewSection.classList.add('inactive'); genericSection.classList.remove('inactive'); movieDetailSection.classList.add('inactive'); getMoviesBySearch(query); }
Quise colocarle un query-parameter a la seccion de tendencias en la pagina principal de que limite el numero de peliculas a mostrar, pero según revisé la documentación la API no posee ese tipo de query-parameter en el enpoint de trends, ya que lo hize con ?limit=5, pero no valio 😅
yo lo que aproveche es que como "movies" te da un array, en la funcion de trendingPreview le hice un splice con los numeros que queria mostrar y ya.
async function getTrendingMoviesPreview() { const { data } = await api(`trending/movie/day`); const movies = data.results; movies.splice(10, movies.length - 10); placeImages(movies, trendingPreviewMoviesIMGContainer); }
Para eliminar espacios en le input