Por: Juan Andrés Núñez
Juan Andrés Núñez - juanwmedia

Especialista en tecnologías Web. Me dedico a enseñar desarrollo Web moderno a cualquier persona (físicamente en clase y a través de Internet) desde una perspectiva holística: teniendo en cuenta las competencias técnicas necesarias, junto a las habilidades personales o soft skills. Tienes más información en mi Web.

  • Ahora que somos nosotros los encargados de gestionar la navegación en el cliente a través de Vue Router, es posible que nos interese hacer que la página haga scroll hasta arriba cuando cargamos una ruta. O quizás preservar la posición que tenemos cuando recargamos.
  • Vue Router permite hacer todo eso y mucho más. Eso sí (y es importante) sólo en el modo historia.,
  • Por ejemplo, en nuestro proyecto cuando volvemos al listado de usuarios tras ver el detalle de alguno de ellos, queremos estar en la misma posición de scroll original.
  • Para conseguirlo y tal como pone en la documentación, debemos usar la función scrollBehavior.
  • scrollBehavior recibe varios argumentos para saber de dónde venimos y a donde vamos. En especial recibe el argumento savedPosition si la navegación se ha realizado con los botones adelante/atrás del navegador. Vamos a examinarlos.
const router = new VueRouter({
  ...
  scrollBehavior(to, from, savedPosition) {
    console.log(to, from, savedPosition);
  }
});
  • Como ves el tercer parámetro sólo se muestra si usamos el botón adelante/atrás del navegador. Siendo nulo el resto del tiempo. Además en savedPosition encontramos la posición x e y original, pudiendo entonces volver a ella.
scrollBehavior(to, from, savedPosition) {
  console.log(to, from, savedPosition);
  if (savedPosition) {
    return savedPosition;
  }
}
  • Sin embargo (en el momento de producir esta lección), no funciona. No somos los únicos que nos hemos dado cuenta.
  • Como ves hay diferentes soluciones para este problema conocido. Yo lo he solucionado haciendo de async scrolling, el cual se describe en la documentación. En base consiste en retornar una promesa, pero añadiendo un custom event que cuando se lanza la resuelve. Vamos a ponerlo en práctica.
scrollBehavior: (to, from, savedPosition) => {
  console.log(to, from, savedPosition);
  return new Promise(resolve => {
    const position = (function() {
      if (savedPosition) {
        return savedPosition;
      }
    })();
    router.app.$root.$once("triggerScroll", () => {
      router.app.$nextTick(() => resolve(position));
    });
  });
}
  • Como ves utilizamos el custom event triggerScroll para resolver la promesa en el siguiente ciclo de renderizado en el DOM ($nextTick).
  • Ahora falta emitir el evento. Lo vamos a asociar hook JavaScript de transición after-leave.
<transition name="fade" @after-leave="$root.$emit('triggerScroll')">
...
  • Ahora funciona, aunque sé que esto es un hack. Espero que se solucione pronto.
  • Nos queda el llamado "scroll to anchor", donde al cargar una ruta hacemos scroll hasta un punto determinado. Vamos a añadir un formulario en la sección de contacto y, cuando cargue, forzar un scroll hacia él.
<h2>Contact form</h2>
<form id="form">
  <input type="text" placeholder="Name" />
  <textarea placeholder="Message"></textarea>
  <input type="submit" />
</form>
  • Date cuenta de que hemos asociado la id #form al formulario. Ahora, desde la navegación, vamos a indicar que queremos hacer scroll a ese punto con la propiedad hash.
<router-link class="header__nav__link" :to="{ name: 'Contact', hash: '#form' }">
    Contact
</router-link>
  • Debemos de preguntar si existe ese hash en nuestra función scrollBehavior y entonces devolver el selector asociado.
...
const position = (function() {
  if (savedPosition) {
    return savedPosition;
  } else {
    if (to.hash) {
      return {
        selector: to.hash
      };
    }
  }
})();
...

No te pierdas ninguna novedad

Escuela Vue en Twitter

Participa en la Comunidad Escuela Vue

Comunidad Escuela Vue