lazy loading, endless scrolling, autopager, endless pages: sample information over here

4 years ago
In contrast of what we assumed, many of our customers actually use the list or grid view to browse through all of our products.

Therefore I implemented the Open Source JScroll Jquery extension. This is how I did it:

1. From Nop.Web, copy Views/Shared/_Root.Head.cshtml to your own template directory and add the following line:

Html.AppendScriptParts("~/Scripts/jquery.jscroll.min.js");
You must place this line as the immediate next line of  Html.AppendCssFileParts("~/Content/jquery-ui-themes/smoothness/jquery-ui-1.10.3.custom.min.css");

2. Download the jquery.jscroll.min.js file from GitHub or from jscroll.com and place this file in folder  Nop.Web/Scripts

3. From Nop.Web, copy Views/Catalog/CategoryTemplate.ProductsInGridOrLines.cshtml to your own template directory

4. Remove the following three lines from this file


<div class="pager">
        @Html.Pager(Model.PagingFilteringContext).QueryParam("pagenumber")
</div>


Now replace the entire Product-List section with the following code

@*product list*@
            @if (Model.Products.Count > 0)
            {
                if (Model.PagingFilteringContext.ViewMode == "list")
                {
                    @*list mode*@
                    <div class="product-list">
                        @foreach (var product in Model.Products)
                        {
                            <div class="item-box">
                                @Html.Partial("_ProductBox", product)
                            </div>
                        }
                        <div class="pager">
                            @Html.Pager(Model.PagingFilteringContext).QueryParam("pagenumber")
                        </div>
                    </div>
                    <script>
                    $('.product-list').jscroll({
                        loadingHtml: '<small>Loading...</small>',
                        padding: 200,
                        debug: true,
                        autoTrigger: true,
                        autoTriggerUntil: 10,
                        nextSelector: 'li.next-page a',
                        contentSelector: '.product-list',
                        callback: function() {$('.pager').hide()}
                    });
                    </script>
                }
                else
                {
                    @*grid mode*@
                    <div class="product-grid">
                        @foreach (var product in Model.Products)
                        {
                            <div class="item-box">
                                @Html.Partial("_ProductBox", product)
                            </div>
                        }
                        <div class="pager">
                            @Html.Pager(Model.PagingFilteringContext).QueryParam("pagenumber")
                        </div>
                    </div>
                    <script>
                        $('.product-grid').jscroll({
                        loadingHtml: '<small>Loading...</small>',
                        padding: 250,
                        debug: true,
                        autoTrigger: true,
                        autoTriggerUntil: 10,
                        nextSelector: 'li.next-page a',
                        contentSelector: '.product-grid',
                        callback: function () { $('.pager').hide() }
                    });
                    </script>
                }
            }




Thats it, your good to go.


*Nop.Web is the root of your website!
4 years ago
file: CategoryTemplate.ProductsInGridOrLines.cshtml


@model CategoryModel
@{
    Layout = "~/Views/Shared/_ColumnsTwo.cshtml";
    Html.AddTitleParts(!String.IsNullOrEmpty(Model.MetaTitle) ? Model.MetaTitle : Model.Name);
    Html.AddMetaDescriptionParts(Model.MetaDescription);
    Html.AddMetaKeywordParts(Model.MetaKeywords);

    var canonicalUrlsEnabled = EngineContext.Current.Resolve<SeoSettings>().CanonicalUrlsEnabled;
    if (canonicalUrlsEnabled)
    {
        var categoryUrl = Url.RouteUrl("Category", new { SeName = Model.SeName }, this.Request.Url.Scheme);
        Html.AddCanonicalUrlParts(categoryUrl);
    }

    var breadcrumbDelimiter = EngineContext.Current.Resolve<CommonSettings>().BreadcrumbDelimiter;
}
@using Nop.Core.Domain.Common;
@using Nop.Core.Domain.Seo;
@using Nop.Core.Infrastructure;
@using Nop.Web.Models.Catalog;
@using Nop.Web.Extensions;
@*category breadcrumb*@
@if (Model.DisplayCategoryBreadcrumb)
{
    <div class="breadcrumb">
        <ul>
            <li><a href="@Url.RouteUrl("HomePage")" title="@T("Categories.Breadcrumb.Top")">@T("Categories.Breadcrumb.Top")</a>
                <span class="delimiter">@breadcrumbDelimiter</span> </li>
            @foreach (var cat in Model.CategoryBreadcrumb)
            {
                var isLastCategory = cat.Id == Model.Id;
                <li>
                    @if (isLastCategory)
                    {
                        <strong class="current-item">@cat.Name</strong>
                    }
                    else
                    {
                        <a href="@Url.RouteUrl("Category", new { SeName = cat.SeName })" title="@cat.Name">@cat.Name</a>
                        <span class="delimiter">@breadcrumbDelimiter</span>
                    }
                </li>
            }
        </ul>
    </div>
    @Html.Widget("categorydetails_after_breadcrumb", Model.Id)
}
<div class="page category-page">
    <div class="page-title">
        <h1>@Model.Name</h1>
    </div>
    <div class="page-body">
        @Html.Widget("categorydetails_top", Model.Id)
        @*description*@
        @if (!String.IsNullOrWhiteSpace(Model.Description))
        {
            <div class="category-description">
                @Html.Raw(Model.Description)
            </div>
        }
        @Html.Widget("categorydetails_before_subcategories", Model.Id)
        @*subcategories*@
        @if (Model.SubCategories.Count > 0)
        {
            <div class="sub-category-grid">
                @foreach (var item in Model.SubCategories)
                {
                    <div class="item-box">
                        <div class="sub-category-item">
                            <h2 class="title">
                                <a href="@Url.RouteUrl("Category", new { SeName = item.SeName })" title="@item.PictureModel.Title">
                                    @item.Name
                                </a>
                            </h2>
                            <div class="picture">
                                <a href="@Url.RouteUrl("Category", new { SeName = item.SeName })" title="@item.PictureModel.Title">
                                    <img alt="@item.PictureModel.AlternateText" src="@item.PictureModel.ImageUrl"
                                         title="@item.PictureModel.Title" />
                                </a>
                            </div>
                        </div>
                    </div>
                }
            </div>
        }
        @Html.Widget("categorydetails_before_featured_products", Model.Id)
        @*featured products*@
        @if (Model.FeaturedProducts.Count > 0)
        {
            <div class="product-grid featured-product-grid">
                <div class="title">
                    <strong>@T("Products.FeaturedProducts")</strong>
                </div>
                <div>
                    @foreach (var item in Model.FeaturedProducts)
                    {
                        <div class="item-box">
                            @Html.Partial("_ProductBox", item)
                        </div>
                    }
                </div>
            </div>
        }
        @Html.Widget("categorydetails_after_featured_products", Model.Id)
        <div class="product-selectors">
            @*view mode*@
            @if (Model.PagingFilteringContext.AllowProductViewModeChanging && Model.Products.Count > 0)
            {
                <div class="product-viewmode">
                    <span>@T("Catalog.ViewMode")</span>
                    @Html.DropDownList("products-viewmode", Model.PagingFilteringContext.AvailableViewModes, new { onchange = "setLocation(this.value);" })
                </div>
            }
            @*sorting*@
            @if (Model.PagingFilteringContext.AllowProductSorting && Model.Products.Count > 0)
            {
                <div class="product-sorting">
                    <span>@T("Catalog.OrderBy")</span>
                    @Html.DropDownList("products-orderby", Model.PagingFilteringContext.AvailableSortOptions, new { onchange = "setLocation(this.value);" })
                </div>
            }
            @*page size*@
            @if (Model.PagingFilteringContext.AllowCustomersToSelectPageSize && Model.Products.Count > 0)
            {
                <div class="product-page-size">
                    <span>@T("Catalog.PageSize")</span>
                    @Html.DropDownList("products-pagesize", Model.PagingFilteringContext.PageSizeOptions, new { onchange = "setLocation(this.value);" })
                    <span>@T("Catalog.PageSize.PerPage")</span>
                </div>
            }
        </div>
        @Html.Widget("categorydetails_before_filters", Model.Id)
        <div class="product-filters-wrapper">
            @*filtering*@
            @if (Model.PagingFilteringContext.PriceRangeFilter.Enabled)
            {
                @Html.Partial("_FilterPriceBox", Model.PagingFilteringContext.PriceRangeFilter, new ViewDataDictionary())
            }
            @*filtering*@
            @if (Model.PagingFilteringContext.SpecificationFilter.Enabled)
            {
                @Html.Partial("_FilterSpecsBox", Model.PagingFilteringContext.SpecificationFilter, new ViewDataDictionary())
            }
        </div>
        @Html.Widget("categorydetails_before_product_list", Model.Id)


            @*product list*@
            @if (Model.Products.Count > 0)
            {
                if (Model.PagingFilteringContext.ViewMode == "list")
                {
                    @*list mode*@
                    <div class="product-list">
                        @foreach (var product in Model.Products)
                        {
                            <div class="item-box">
                                @Html.Partial("_ProductBox", product)
                            </div>
                        }
                        <div class="pager">
                            @Html.Pager(Model.PagingFilteringContext).QueryParam("pagenumber")
                        </div>
                    </div>
                    <script>
                    $('.product-list').jscroll({
                        loadingHtml: '<small>Laden...</small>',
                        padding: 200,
                        debug: true,
                        autoTrigger: true,
                        autoTriggerUntil: 10,
                        nextSelector: 'li.next-page a',
                        contentSelector: '.product-list',
                        callback: function() {$('.pager').hide()}
                    });
                    </script>
                }
                else
                {
                    @*grid mode*@
                    <div class="product-grid">
                        @foreach (var product in Model.Products)
                        {
                            <div class="item-box">
                                @Html.Partial("_ProductBox", product)
                            </div>
                        }
                        <div class="pager">
                            @Html.Pager(Model.PagingFilteringContext).QueryParam("pagenumber")
                        </div>
                    </div>
                    <script>
                        $('.product-grid').jscroll({
                        loadingHtml: '<small>Laden...</small>',
                        padding: 250,
                        debug: true,
                        autoTrigger: true,
                        autoTriggerUntil: 10,
                        nextSelector: 'li.next-page a',
                        contentSelector: '.product-grid',
                        callback: function () { $('.pager').hide() }
                    });
                    </script>
                }
            }


            @Html.Widget("categorydetails_bottom", Model.Id)
        </div>
    </div>

4 years ago
file: _Root.Head.cshtml


@using Nop.Core.Domain.Common;
@using Nop.Core.Infrastructure;
@{
    var displayMiniProfiler = EngineContext.Current.Resolve<Nop.Core.Domain.StoreInformationSettings>().DisplayMiniProfilerInPublicStore;

    //resources
    Html.AppendCssFileParts("~/Content/jquery-ui-themes/smoothness/jquery-ui-1.10.3.custom.min.css");

    Html.AppendScriptParts("~/Scripts/jquery.jscroll.min.js");
    Html.AppendScriptParts("~/Scripts/public.ajaxcart.js");
    Html.AppendScriptParts("~/Scripts/public.common.js");
    Html.AppendScriptParts("~/Scripts/jquery-migrate-1.2.1.min.js");
    Html.AppendScriptParts("~/Scripts/jquery-ui-1.10.3.custom.min.js");
    Html.AppendScriptParts("~/Scripts/jquery.validate.unobtrusive.min.js");
    Html.AppendScriptParts("~/Scripts/jquery.validate.min.js");
    Html.AppendScriptParts("~/Scripts/jquery-1.10.2.min.js");
    
    //X-UA-Compatible tag
    var commonSettings = EngineContext.Current.Resolve<CommonSettings>();
    if (commonSettings.RenderXuaCompatible)
    {
        Html.AppendHeadCustomParts(string.Format("<meta http-equiv=\"X-UA-Compatible\" content=\"{0}\"/>", commonSettings.XuaCompatibleValue));
    }
}
<!DOCTYPE html>
<html @Html.Partial("LanguageAttributes")>
<head>
    <title>@Html.NopTitle(true)</title>
    <meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
    <meta name="description" content="@(Html.NopMetaDescription())" />
    <meta name="keywords" content="@(Html.NopMetaKeywords())" />
    <meta name="generator" content="nopCommerce" />
    @Html.NopHeadCustom()
    @*This is used so that themes can inject content into the header*@
    @Html.Partial("Head")
    @Html.Widget("head_html_tag")
    @Html.NopCssFiles(this.Url, ResourceLocation.Head)
    @Html.NopScripts(this.Url, ResourceLocation.Head)
    @Html.NopCanonicalUrls()
    @Html.Action("RssHeaderLink", "News")
    @Html.Action("RssHeaderLink", "Blog")
    @*Favicon - upload favicon.ico file to the root directory*@
    @Html.Action("Favicon", "Common")
    @if (displayMiniProfiler)
    {
        @StackExchange.Profiling.MiniProfiler.RenderIncludes()
    }
    <!--Powered by nopCommerce - https://www.nopcommerce.com-->
    <!--Copyright (c) 2008-2014-->
</head>
<body>
    @RenderBody()
    @Html.NopCssFiles(this.Url, ResourceLocation.Foot)
    @Html.NopScripts(this.Url, ResourceLocation.Foot)
</body>
</html>

4 years ago
It is working good.
4 years ago
It is not workking for me.

I am using noppcommerce 3.6

Actually there is no effect of jscroll no wrappers and so on.

However I have checked throught debbugger script is running and doing somethisng.
4 years ago
For the nop 3.60 user I'll embed the code in the next two posts!
4 years ago
Nop 3.60
File _Root.Head.cshtml


@using Nop.Core.Domain.Common;
@using Nop.Core.Infrastructure;
@{
    var displayMiniProfiler = EngineContext.Current.Resolve<Nop.Core.Domain.StoreInformationSettings>().DisplayMiniProfilerInPublicStore;

    Html.AppendScriptParts("~/Scripts/jquery.jscroll.min.js");
    Html.AppendScriptParts("~/Scripts/public.ajaxcart.js");
    Html.AppendScriptParts("~/Scripts/public.common.js");
    Html.AppendScriptParts("~/Scripts/jquery-migrate-1.2.1.min.js");
    Html.AppendScriptParts("~/Scripts/jquery-ui-1.10.3.custom.min.js");
    Html.AppendScriptParts("~/Scripts/jquery.validate.unobtrusive.min.js");
    Html.AppendScriptParts("~/Scripts/jquery.validate.min.js");
    Html.AppendScriptParts("~/Scripts/jquery-1.10.2.min.js");

    //X-UA-Compatible tag
    var commonSettings = EngineContext.Current.Resolve<CommonSettings>();
    if (commonSettings.RenderXuaCompatible)
    {
        Html.AppendHeadCustomParts(string.Format("<meta http-equiv=\"X-UA-Compatible\" content=\"{0}\"/>", commonSettings.XuaCompatibleValue));
    }
}
<!DOCTYPE html>
<html @Html.Partial("LanguageAttributes")>
<head>
    <title>@Html.NopTitle(true)</title>
    <meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
    <meta name="description" content="@(Html.NopMetaDescription())" />
    <meta name="keywords" content="@(Html.NopMetaKeywords())" />
    <meta name="generator" content="nopCommerce" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    @Html.NopHeadCustom()
    @*This is used so that themes can inject content into the header*@
    @Html.Partial("Head")
    @Html.Widget("head_html_tag")
    @Html.NopCssFiles(this.Url, ResourceLocation.Head)
    @Html.NopScripts(this.Url, ResourceLocation.Head)
    @Html.NopCanonicalUrls()
    @Html.Action("RssHeaderLink", "News")
    @Html.Action("RssHeaderLink", "Blog")
    @*Favicon - upload favicon.ico file to the root directory*@
    @Html.Action("Favicon", "Common")
    @if (displayMiniProfiler)
    {
        @StackExchange.Profiling.MiniProfiler.RenderIncludes()
    }
    <!--Powered by nopCommerce - https://www.nopcommerce.com-->
</head>
<body>
    @RenderBody()
    @Html.NopCssFiles(this.Url, ResourceLocation.Foot)
    @Html.NopScripts(this.Url, ResourceLocation.Foot)
</body>
</html>
4 years ago
nop 3.60
file: CategoryTemplate.ProductsInGridOrLines.cshtml


@model CategoryModel
@{
    Layout = "~/Views/Shared/_ColumnsTwo.cshtml";
    Html.AddTitleParts(!String.IsNullOrEmpty(Model.MetaTitle) ? Model.MetaTitle : Model.Name);
    Html.AddMetaDescriptionParts(Model.MetaDescription);
    Html.AddMetaKeywordParts(Model.MetaKeywords);

    var canonicalUrlsEnabled = EngineContext.Current.Resolve<SeoSettings>().CanonicalUrlsEnabled;
    if (canonicalUrlsEnabled)
    {
        var categoryUrl = Url.RouteUrl("Category", new { SeName = Model.SeName }, this.Request.Url.Scheme);
        Html.AddCanonicalUrlParts(categoryUrl);
    }

    var breadcrumbDelimiter = EngineContext.Current.Resolve<CommonSettings>().BreadcrumbDelimiter;
}
@using Nop.Core.Domain.Common;
@using Nop.Core.Domain.Seo;
@using Nop.Core.Infrastructure;
@using Nop.Web.Models.Catalog;
@*category breadcrumb*@
@section Breadcrumb
{
    @if (Model.DisplayCategoryBreadcrumb)
    {
        <div class="breadcrumb">
            <ul>
                <li>
                    <a href="@Url.RouteUrl("HomePage")" title="@T("Categories.Breadcrumb.Top")">@T("Categories.Breadcrumb.Top")</a>
                    <span class="delimiter">@breadcrumbDelimiter</span>
                </li>
                @foreach (var cat in Model.CategoryBreadcrumb)
                {
                    var isLastCategory = cat.Id == Model.Id;
                    <li>
                        @if (isLastCategory)
                        {
                            <strong class="current-item">@cat.Name</strong>
                        }
                        else
                        {
                            <a href="@Url.RouteUrl("Category", new { SeName = cat.SeName })" title="@cat.Name">@cat.Name</a>
                            <span class="delimiter">@breadcrumbDelimiter</span>
                        }
                    </li>
                }
            </ul>
        </div>
        @Html.Widget("categorydetails_after_breadcrumb", Model.Id)
    }
}
<div class="page category-page">
    <div class="page-title">
        <h1>@Model.Name</h1>
    </div>
    <div class="page-body">
        @Html.Widget("categorydetails_top", Model.Id)
        @*description*@
        @if (!String.IsNullOrWhiteSpace(Model.Description))
        {
            <div class="category-description">
                @Html.Raw(Model.Description)
            </div>
        }
        @Html.Widget("categorydetails_before_subcategories", Model.Id)
        @*subcategories*@
        @if (Model.SubCategories.Count > 0)
        {
            <div class="category-grid sub-category-grid">
                <div class="item-grid">
                    @foreach (var item in Model.SubCategories)
                    {
                        <div class="item-box">
                            <div class="sub-category-item">
                                <h2 class="title">
                                    <a href="@Url.RouteUrl("Category", new { SeName = item.SeName })" title="@item.PictureModel.Title">
                                        @item.Name
                                    </a>
                                </h2>
                                <div class="picture">
                                    <a href="@Url.RouteUrl("Category", new { SeName = item.SeName })" title="@item.PictureModel.Title">
                                        <img alt="@item.PictureModel.AlternateText" src="@item.PictureModel.ImageUrl" title="@item.PictureModel.Title" />
                                    </a>
                                </div>
                            </div>
                        </div>
                    }
                </div>
            </div>
        }
        @Html.Widget("categorydetails_before_featured_products", Model.Id)
        @*featured products*@
        @if (Model.FeaturedProducts.Count > 0)
        {
            <div class="product-grid featured-product-grid">
                <div class="title">
                    <strong>@T("Products.FeaturedProducts")</strong>
                </div>
                <div class="item-grid">
                    @foreach (var item in Model.FeaturedProducts)
                    {
                        <div class="item-box">
                            @Html.Partial("_ProductBox", item)
                        </div>
                    }
                </div>
            </div>
        }
        @Html.Widget("categorydetails_after_featured_products", Model.Id)
        @if (Model.Products.Count > 0)
        {
            @Html.Partial("_CatalogSelectors", Model.PagingFilteringContext, new ViewDataDictionary())
        }
        @Html.Widget("categorydetails_before_filters", Model.Id)
        <div class="product-filters">
            @*filtering*@
            @if (Model.PagingFilteringContext.PriceRangeFilter.Enabled)
            {
                @Html.Partial("_FilterPriceBox", Model.PagingFilteringContext.PriceRangeFilter, new ViewDataDictionary())
            }
            @*filtering*@
            @if (Model.PagingFilteringContext.SpecificationFilter.Enabled)
            {
                @Html.Partial("_FilterSpecsBox", Model.PagingFilteringContext.SpecificationFilter, new ViewDataDictionary())
            }
        </div>
        @Html.Widget("categorydetails_before_product_list", Model.Id)

        @if (Model.Products.Count > 0)
        {
            <div class="@(Model.PagingFilteringContext.ViewMode == "list" ? "product-list" : "product-grid")">
                <div class="item-grid">
                    @foreach (var product in Model.Products)
                    {
                        <div class="item-box">
                            @Html.Partial("_ProductBox", product)
                        </div>
                    }
                    <div class="pager">
                        @Html.Pager(Model.PagingFilteringContext).QueryParam("pagenumber")
                    </div>
                </div>
                <script>
                    var slistClass = '[email protected](Model.PagingFilteringContext.ViewMode == "list" ? "product-list" : "product-grid")';
                    $(slistClass).jscroll({
                        loadingHtml: '<small>Laden...</small>',
                        padding: 200,
                        debug: true,
                        autoTrigger: true,
                        autoTriggerUntil: 100,
                        nextSelector: 'li.next-page a',
                        contentSelector: slistClass,
                        callback: function() {$('.pager').hide()}
                    });
                </script>
            </div>
        }
        @Html.Widget("categorydetails_bottom", Model.Id)
    </div>
</div>

4 years ago
Thanks a lot... it worked with 3.6.
4 years ago
Thanks a lot. Worked like a charm!