UX Planet

UX Planet is a one-stop resource for everything related to user experience.

Follow publication

AngularJS, Skeleton Loader & Endless Scrolling

Carol Skelly
UX Planet
Published in
4 min readApr 12, 2018

I came across a 2015 post on StackOverflow today, and realized that I like this “placeholder grid” or “skeleton” style loading animation that was studied in an article by George Philips. This placeholder animation style will look familiar if you’ve been on Facebook or LinkedIn. The same placeholder style animation is even used here on Medium!

In an effort to implement something similar, I came up with a simple approach. This method uses dynamically populated Bootstrap 4 cards, and a CSS animation delay. Here’s how it works.

Key Components

__ Dynamic data loaded via AJAX (AngularJs HTTP)

__ Endless (infinite) scrolling__ more cards are displayed on scroll

__ Server-side and client-side paging (scroll-able groups)

__ Animations using Animate.css

__ Placeholder preview grid display before card content is populated

Implementation

Get Some Data

The data comes from the College Scorecard API, loaded using an AngularJs $http.get() request…

var apiUrl = "https://api.data.gov/ed/collegescorecard/v1/schools.json?";

$scope.getData = function(startAt,count){
$scope.items = $scope.items||[];
$http.get(apiUrl+"&_page="+startAt+"&_per_page="+count+"")
.then(function(resp) {
var data = resp.data;
/* populate items */
$scope.items = $scope.items.concat(data.results);
$scope.lastIndex = $scope.items.length;
$scope.loading = false;
}, function(data, status) {
console.log("get data error!");
});
};

Populate the Cards

Once the data fills $scope.items, the cards are populated using the Angular ng-repeat directive…

<section class="row">
<div class="col-md-6 col-lg-3 py-3"
ng-repeat="item in items" ng-show="$index<pageIndex">
<div class="card bg-light animated fadeIn">
<div class="card-body bg-white animated fadeIn">
.. card content
</div>
</div>
</div>
</section>

Animate It!

As you can see, both the card and the inner card-body have the animated fadeIn classes which runs the Animate.css animation when each card is displayed. You can also try other animation effects like flash , pulse, etc..

Here’s how the placeholder preview animation trick works. Notice that card has a light gray background color bg-light, and the card-body has a white background color bg-white. We just need a little CSS so that the animation on card-body is delayed slightly 1.3s .

As a result, the light gray card appears first, and then the white card-body fades in 1.3s after, covering the light gray background of the card

.card-body {
-webkit-animation-delay:1.3s
}

Again, you can set the animation-delay to whatever you’d like. Increase the delay time for a longer more dramatic the placeholder preview effect.

Keep it Scrolling

The endless page functionality works using an Angular directive I created called when-visible. It it used to “watch” the current scroll position.

myApp.directive('whenVisible', function() {
/* this directive indicates when an element
is scrolled into view, and adds the `animated`
class to the element which triggers the animate.css
animations
*/

function isScrolledIntoView(elem)
{
var docViewTop = $(window).scrollTop();
var docViewBottom = docViewTop + ($(window).outerHeight());
var elemTop = elem.offset().top;
var elemBottom = elemTop + elem.innerHeight();

return (elemTop <= docViewBottom) && (elemTop > docViewTop);
}

return function(scope, elm, attr) {
var raw = elm[0];
$(window).scroll(function(){
if (isScrolledIntoView(elm)) {
elm.addClass("animated");
if (!scope.busy){
setTimeout(function(){
scope.$apply(attr.whenVisible);
},500);
}
}
else {
elm.removeClass("animated");
}
return false;
});
};
});

The when-visible trigger element is placed below the cards container…

<div when-visible="showMore(itemsPerPage)"
ng-show="!needToLoadMore">
</div>

When the when-visible div is scrolled into view, the showMore() function in the controller is executed. The showMore() function displays the next group of cards…

$scope.showMore = function(howMany){  
if (!$scope.busy){
$scope.pageIndex = $scope.pageIndex + howMany;
$scope.currentPage =
Math.floor($scope.pageIndex /$scope.itemsPerPage);

setTimeout(function(){
$scope.busy = false;
if ($scope.pageIndex >= $scope.lastIndex){
$scope.needToLoadMore = true;
}
else {
$scope.needToLoadMore = false;
}
},300);
$scope.busy = true;
}
};

Each request to the College Scorecard API loads 60 items. Each request is a single “page” of API data. However, each “page” of API data is shown in 5 groups of 12 as you scroll. These are like the pages within the page that are loaded as you scroll. Once all of the 5 card groups are shown, we’re out of API data. So, the “Load More” button is displayed to prompt another API request which gets the next 60 items. You can tweak the variables to however you’d like.

$scope.itemsPerPage = 12;    // infinite scroll page(group) size
$scope.itemsFromServer = 60; // how many to get from API
$scope.pageIndex = 12; // items to show initially

Putting It All Together

Finally, here is the complete source and demo for this entire placeholder animation project. Enjoy!

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

Published in UX Planet

UX Planet is a one-stop resource for everything related to user experience.

Written by Carol Skelly

S/W Engineer. Web developer. @Bootply @Codeply

Write a response

Hey! I just tried to open it from my iphone and the infinite scroll was not working properly. The animated placeholder is super cool by the way :)

--