Frontend разработка с Laravel, как сделать "бесконечную" прокрутку

01.09.2015

Бесконечная прокрутка - подгрузка нового контента по мере прокрутки страницы пользователем.

Задача: мы делаем блог на Laravel 5, у нас уже есть вывод постов со стандартной постраничной навигацией и мы хотим сделать infinite scrolling.

Для этой цели есть множество готовых решений на javascript, я выбрал jScroll (автор Philip Klauzinski).

Возьмем все необходимое из bower (менеджер пакетов для фронтенда) и воспользуемся gulp чтобы наши скрипты собирались в 1 файл и минимизировались на лету. 
Проверяем что у нас уже установлены npm и node

npm -v && node -v


npm version

Устанавливаем gulp глобально

sudo npm install gulp -g

и делаем ссылку 

sudo npm link gulp

gulp install

Теперь выполнение команды gulp выполнит задания, которые записаны в файле gulpfile.js (он уже был изначально при создании проекта). Если выполнять с параметром --production, то при склейке файлов будет происходить их минимизация.  
Если выполнить gulp watch, то gulp будет следить за файлами и автоматически пересобирать при изменениях. 
В gulpfile.js мы должны прописать что откуда куда собирать.

Пример:
 css стили из assets

В этом проекте у меня получилось файлов больше:

var elixir = require('laravel-elixir');

elixir(function (mix) {
    // all styles in resources/assets/css/
    mix.styles([
        "reset.css",
        "style.css",
        "css-stars.css"
    ], 'public/css/style.min.css');

    // scripts in resources/assets/js/
    mix.scripts([
        "highlight.js",        
        "barrating.js",       
        "custom.js",      
    ], 'public/js/main.js');   
});

В данном примере из resources/assets/js/ три файла комбинируюся в один и результат сохраняется в публичную директорию public, подкатегорию js, файл main.js

Аналогично устанавливаем bower:

sudo npm install bower -g
sudo npm link bower

Теперь можем установить jScroll:

bower install jscroll

Автоматически установится зависимость - jquery. В каталоге bower_components будет 2 подкатегории: jscroll и jquery.
Добавим обе библиотеки в gulpfile, чтобы они попали в наш main.js
и еще добавим 1 файл "infinite.scrolling.js" для нашего кода вызова библиотек

    mix.scripts([
        ...
        "../../../bower_components/jquery/dist/jquery.js",
        "../../../bower_components/jscroll/jquery.jscroll.js", 
        "infinite.scrolling.js"    
    ], 'public/js/main.js'); 


На мобильных устройствах с медленным и дорогим интернетом функция автоматической подгрузки может доставить проблемы, поэтому давайте сделаем чтобы пользователям с мобильных устройств не нужно было скачивать все эти библиотеки и функции бесконечной прокрутки у них не было.    

    // desctop version of scripts
    mix.scripts([
        "highlight.pack.js",
        "barrating.js",
        "custom.js",
        "../../../bower_components/jquery/dist/jquery.js",        
        "../../../bower_components/jscroll/jquery.jscroll.js",        
        "infinite.scrolling.js"
    ], 'public/js/main.js');

    //mobile version of scripts
    mix.scripts([
        "highlight.pack.js",
        "barrating.js",
        "custom.js"
    ], 'public/js/main.mobile.js');
    
    mix.version(["css/style.min.css", "js/main.js", "js/main.mobile.js"]);

Я добавил вызов "version" чтобы gulp сгенерировал в каталоге /public/build файлы со случайным именем, которые будут доступны по нормальным именам через помошник elixir, это нужно чтобы при изменении этих файлов у пользователей не оставалось старых в кеше - при построении меняются названия файлов и браузер скачивает новые файлы.

Чтобы отличать мобильные на серверной стороне, добавим зависимость:

php composer.phar require jenssegers/agent

В config/app.php добавляем провайдер
Jenssegers\Agent\AgentServiceProvider::class,
и алиас: 'Agent'     => Jenssegers\Agent\Facades\Agent::class,

в blade шаблоне меняем 

<script src="/js/main.js"></script>

на 

@if (Agent::isMobile())
<script src="{{ elixir("js/main.mobile.js") }}"></script>
@else
<script src="{{ elixir("js/main.js") }}"></script>
@endif

запускаем gulp следить:

gulp watch --production

гулп следит

В infinite.scrolling.js делаем вызов jscroll:

$(function () {
    $('.articles').jscroll({
        contentSelector: '.articles',
        nextSelector: '.pagination li:last a',
        callback: function () {
            $('.pagination').remove();            
        }
    });
});

Список постов в шаблоне вместе с пагинацией нужно завернуть в div с классом articles и этого достаточно - подгрузка уже должна работать. Ajax-ом подгружается вся следующая страница из пагинации, из нее извлекается .articles и добавляется дочерним элементом в articles первой страницы. 
Функция в callback удаляет пагинацию с каждой страницы, пользователи не пользуются когда работает автоматическая подгрузка. Поисковые роботы и пользователи с мобильными устройствами по прежнему используют пагинацию в виде ссылок.

Можно улучшить. 

Загружается вся страница с header, footer и так далее, а лучше если бы подгружался только материал статей.

Можно вынести из шаблона resources/views/articles/index.blade.php содержимое секции content в отдельный файл resources/views/articles/index-content.blade.php

index.blade.php станет таким:

@extends('main')
@section('content')
    @include('articles.index-content')
@endsection

Если запрос через ajax, то рендерим "articles.index-content", а для обычных запросов - как и было "articles.index"

    public function index(Request $request)
    {
        $articles = Article::orderBy('id', 'desc')->paginate();
        $view = $request->ajax() ? 'articles.index-content' : 'articles.index';
        return view($view, compact('articles'));
    }

Давайте представим ситуацию что у пользователя пропадет интернет пока он доберется до низа страницы, что тогда будет?

зависание загрузки - пример

Надпись "Loading..." будет продолжать отображаться даже когда интернет появится, потому что ajax запрос завершился неудачно. Если мы уберем эту надпись и ничего больше не будем делать, останется пагинация -  функционал и внешний вид сайта не пострадает. Однако, если обрыв произойдет после первой подгрузки, пагинации уже не будет видно, пользователь может не догадаться что контент не закончился, а если и догадается, то ему придется обновлять страницу и скроллить заново. 

Проконтролируем чтобы при обрыве отображалась пагинация и скрывалось сообщение "Loading..."

$(function () {
    $('.articles').jscroll({
        contentSelector: '.articles',
        nextSelector: '.pagination li:last a',
        callback: function () {
            $('.pagination').hide();            
        }
    });
    $.ajaxSetup({
        error: function (xhr, textStatus, errorThrown) {
            if ($('.jscroll-loading').length) {
                $('.pagination:last').show();
                $('.jscroll-loading').remove();
            }
        }
    });
});


 


2 оценки

Оставить комментарий