Scroll Híbrido

En este artículo vamos a ver cómo crear la funcionalidad del scroll híbrido en las páginas de categorías y los resultados de búsqueda.

Pero, ¿Qué es un scroll híbrido?. Básicamente mezcla las experiencias de:

  • Un scroll infinito en el cual se van cargando productos en el listado a medida que el usuario hace scroll en la página, sin ver el footer hasta que se terminen de cargar todos los productos (como lo hace una app nativa en un celular).
  • Una experiencia de paginado clásico donde el usuario tiene el control sobre cuántos productos ver por cada página, dejando el footer más visible que en el caso del scroll infinito.

El scroll híbrido funciona de la siguiente manera:

  • Apenas carga la página, se muestran 12, 16 o 20 (dependiendo lo definido lo definido en los tpls que vamos a ver en este tutorial)
  • Cuando el usuario comienza a hacer scroll se cargan automáticamente más productos (ocultando el footer) hasta llegar a 50.
  • Luego se muestra un botón "ver más productos" y el footer del sitio.
  • Haciendo click en este botón se cargan más productos y se vuelve al modo "scroll infinito"
  • Cuando el usuario hace scroll, se vuelven a cargar automáticamente más productos hasta llegar a 60 más.
  • Se repiten los pasos

De esta forma garantizamos que el usuario llegue a ver el contenido que se encuentra debajo del listado de productos, por ejemplo el footer, donde puede haber información relevante como medios de pago, medios de envío, datos de contacto, etc.

Por otro lado si el usuario accede al detalle de un producto y vuelve hacia atrás, no perderá la cantidad de productos cargados anteriormente.

HTML

1. Lo primero que vamos a hacer es en el archivo templates/category.tpl borrar la grilla que tenes hasta ahora y reemplazarla por lo siguiente:

{% if products %}
    <div class="js-product-table row">
        {% include 'snipplets/product_grid.tpl' %}
    </div>
    {% if pages.current == 1 and not pages.is_last %}
         <div class="text-center mt-5 mb-5">
            <a class="js-load-more btn btn-primary">
                <span class="js-load-more-spinner" style="display:none;">{% include "snipplets/svg/sync-alt.tpl" with {svg_custom_class: "icon-inline icon-spin"} %}</span>
                {{ 'Mostrar más productos' | t }}
            </a>
        </div>
         <div id="js-infinite-scroll-spinner" class="mt-5 mb-5 text-center w-100" style="display:none">
            {% include "snipplets/svg/sync-alt.tpl" with {svg_custom_class: "icon-inline icon-3x svg-icon-text icon-spin"} %} 
        </div>
    {% endif %}
{% else %}
    <p class="text-center">
        {{(has_filters ? "No tenemos productos en esas variantes. Por favor, intentá con otros filtros." : "Próximamente") | translate}}
    </p>
{% endif %}

Es importante mantener los IDs y las clases “js-...” ya que luego las conectaremos con el JavaScript.

También necesitamos agregar la siguiente línea al comienzo del tpl (si ya la tenes, no hace falta hacer este paso)

{% paginate by 12 %}

Esto define el paginado inicial (la cantidad de productos que se cargan antes de empezar a hacer scroll) al cargar la página.

2. Dentro del template search.tpl en la carpeta templates, aplicar un cambio muy similar al paso anterior:

{% if products %}
    <div class="js-product-table row">
        {% include 'snipplets/product_grid.tpl' %}
    </div>
    {% if pages.current == 1 and not pages.is_last %}
        <div class="text-center mt-5 mb-5">
            <a class="js-load-more btn btn-primary">
                <span class="js-load-more-spinner" style="display:none;">{% include "snipplets/svg/sync-alt.tpl" with {svg_custom_class: "icon-inline icon-spin"} %}</span>
                {{ 'Mostrar más productos' | t }}
            </a>
        </div>
         <div id="js-infinite-scroll-spinner" class="mt-5 mb-5 text-center w-100" style="display:none">
             {% include "snipplets/svg/sync-alt.tpl" with {svg_custom_class: "icon-inline icon-3x svg-icon-text icon-spin"} %} 
        </div>
    {% endif %}
{% else %}
    <p class="text-center">
        {{ "No hubo resultados para tu búsqueda" | translate }}
    </p>
{% endif %}

Y también agregar (o reemplazar) el valor de paginado al comienzo del tpl

{% paginate by 12 %}

2. Agregamos la clase js-hide-footer-while-scrolling al contenedor general del footer.

3. Por último para la parte de HTML, necesitamos agregar una carpeta SVG dentro de la carpeta snipplets, donde vamos a crear el snipplet para el ícono que gira indicando que se están cargando más productos, con el nombre sync-alt.tpl 

<svg class="{{ svg_custom_class }}" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M483.515 28.485L431.35 80.65C386.475 35.767 324.485 8 256 8 123.228 8 14.824 112.338 8.31 243.493 7.971 250.311 13.475 256 20.301 256h28.045c6.353 0 11.613-4.952 11.973-11.294C66.161 141.649 151.453 60 256 60c54.163 0 103.157 21.923 138.614 57.386l-54.128 54.129c-7.56 7.56-2.206 20.485 8.485 20.485H492c6.627 0 12-5.373 12-12V36.971c0-10.691-12.926-16.045-20.485-8.486zM491.699 256h-28.045c-6.353 0-11.613 4.952-11.973 11.294C445.839 370.351 360.547 452 256 452c-54.163 0-103.157-21.923-138.614-57.386l54.128-54.129c7.56-7.56 2.206-20.485-8.485-20.485H20c-6.627 0-12 5.373-12 12v143.029c0 10.691 12.926 16.045 20.485 8.485L80.65 431.35C125.525 476.233 187.516 504 256 504c132.773 0 241.176-104.338 247.69-235.493.339-6.818-5.165-12.507-11.991-12.507z"/></svg>

CSS

Requisito:

Tener agregados en tu diseño las clases helpers. Podés seguir este este pequeño tutorial para hacerlo (simplemente es copiar y pegar algunas clases, no toma más de 1 minuto).

Agregamos el siguiente SASS de colores en style-colors.scss.tpl (o la hoja de tu diseño que tenga los colores y tipografías de la tienda). Recordá que las variables de colores y tipografías pueden variar respecto a tu diseño:

@mixin prefix($property, $value, $prefixes: ()) {
  @each $prefix in $prefixes {
      #{'-' + $prefix + '-' + $property}: $value;
  }
    #{$property}: $value;
}


{# /* // Buttons */ #}


.btn{
  text-decoration: none;
  text-align: center;
  border: 0;
  cursor: pointer;
  -webkit-appearance: none;
  -moz-appearance: none;
  appearance: none;
  text-transform: uppercase;
  background: none;
  @include prefix(transition, all 0.4s ease, webkit ms moz o);
  &:hover,
  &:focus{
    outline: 0;
    opacity: 0.8;
  }
  &[disabled],
  &[disabled]:hover{
    opacity: 0.5;
    cursor: not-allowed;
    outline: 0;
  }
  &-default{
    padding: 10px 15px; 
    background-color: rgba($main-foreground, .2);
    color: $main-foreground;
    fill: $main-foreground;
    font-weight: bold;
  }
  &-primary{
    padding: 15px;
    background-color: $primary-color;
    color: $main-background;
    fill: $main-background;
    letter-spacing: 4px;
    &:hover{
      color: $main-background;
      fill: $main-background;
    }
  }


  &-block{
    float: left;
    width: 100%;
  }
}


button{
  @extend %body-font;
  cursor: pointer;
  &:focus{
    outline: 0;
    opacity: 0.8;
  }
}

JS

⚠️ A partir del día 30 de enero de 2023, la librería jQuery será removida del código de nuestras tiendas, por lo tanto la función "$" no podrá ser utilizada.

1. El JavaScript necesitamos agregarlo en el archivo store.js.tpl (o donde tengas tus funciones de JS). El código que necesitamos es el siguiente:

{% if template == 'category' or template == 'search' %}

        !function() {

            {# /* // Infinite scroll */ #}

            {% if pages.current == 1 and not pages.is_last %}
                LS.hybridScroll({
                    productGridSelector: '.js-product-table',
                    spinnerSelector: '#js-infinite-scroll-spinner',
                    loadMoreButtonSelector: '.js-load-more',
                    hideWhileScrollingSelector: ".js-hide-footer-while-scrolling",
                    productsBeforeLoadMoreButton: 50,
                    productsPerPage: 12
                });
            {% endif %}
        }();

{% endif %}

En este JS podemos encontrar algunas propiedades que podés cambiar:

PropiedadDescripción
productGridSelectorEs el selector para el div que contiene al listado de productos
spinnerSelectorEl selector para el icono girando que se muestra cuando el usuario llega a abajo de todo en la pantalla y todavía no se cargaron productos.
loadMoreButtonSelectorSirve para ocultar o mostrar el botón de “Ver más productos”
hideWhileScrollingSelectorComo su nombre indica, sirve para ocultar elementos mientras el modo “scroll infinito” está activado
productsBeforeLoadMoreButtonEs la cantidad de productos que se van a cargar hasta mostrar el botón de “Ver más productos”
productsPerPage
Cantidad de productos que se muestra al cargar la página. Debe coincidir con el {% paginate by 12 %} en category.tpl y search.tpl

En el caso de necesitar aplicar JS justo después de que los productos se cargan, podés usar la siguiente función:

afterLoaded: function() {


},

Dentro del contexto aplicaría así:

spinnerSelector: '#js-infinite-scroll-spinner',
loadMoreButtonSelector: '.js-load-more',
hideWhileScrollingSelector: ".js-hide-footer-while-scrolling",
productsBeforeLoadMoreButton: 50,
productsPerPage: 12,
afterLoaded: function() {
    console.log("test");
},

Esto puede ser útil en caso de usar un plugin como Masonry, donde necesitas acomodar los elementos cada vez que se cargan. 

Listo, ya tenés en el scroll híbrido funcionando ¡Excelente!