Skip to content

Instantly share code, notes, and snippets.

@mrtonyhuynh
Created December 2, 2019 10:03
Show Gist options
  • Select an option

  • Save mrtonyhuynh/f23a95e93879c52f64aed794a07101f6 to your computer and use it in GitHub Desktop.

Select an option

Save mrtonyhuynh/f23a95e93879c52f64aed794a07101f6 to your computer and use it in GitHub Desktop.
Infinity Scroll Loadmore & Event Tracking
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>User Behavior Tracking</title>
<!-- Bootstrap core CSS -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css"
integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
<style>
.sidebar-wrap {
position: sticky;
top: 0;
}
</style>
</head>
<body>
<main role="main" class="container">
<div class="row">
<div class="col-md-8" id="feed">
<!-- <div class="page-1"></div>-->
</div><!-- /.blog-main -->
<aside class="col-md-4 sidebar">
<div class="sidebar-wrap">
<div class="p-4 mb-3 bg-light rounded">
<h4 class="font-italic">About</h4>
<p class="mb-0">Etiam porta <em>sem malesuada magna</em> mollis euismod. Cras mattis consectetur
purus sit amet fermentum. Aenean lacinia bibendum nulla sed consectetur.</p>
</div>
<div class="p-4">
<h4 class="font-italic">Archives</h4>
<ol class="list-unstyled mb-0">
<li><a href="#">March 2014</a></li>
<li><a href="#">February 2014</a></li>
<li><a href="#">January 2014</a></li>
<li><a href="#">December 2013</a></li>
<li><a href="#">November 2013</a></li>
<li><a href="#">October 2013</a></li>
<li><a href="#">September 2013</a></li>
<li><a href="#">August 2013</a></li>
<li><a href="#">July 2013</a></li>
<li><a href="#">June 2013</a></li>
<li><a href="#">May 2013</a></li>
<li><a href="#">April 2013</a></li>
</ol>
</div>
<div class="p-4">
<h4 class="font-italic">Elsewhere</h4>
<ol class="list-unstyled">
<li><a href="#">GitHub</a></li>
<li><a href="#">Twitter</a></li>
<li><a href="#">Facebook</a></li>
</ol>
</div>
</div>
</aside><!-- /.blog-sidebar -->
</div><!-- /.row -->
</main><!-- /.container -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Faker/3.1.0/faker.min.js"></script>
<script>
$(function () {
var $feed = $('#feed');
var feedSettings = {
scrollOffet: 500,
pagelimit: 10,
itemLength: 0,
autoLoadMore: false
};
/* --- LOAD NEXT PAGE --- */
function loadNextPage() {
console.log('Stop monitoring scroll as finishedloadmore..');
$(this).off('scroll', checkScroll); // stop monitoring scroll
console.log('ajaxLoadNext()');
$feed.find('.show-more').remove(); // remove load more link on success
// So luong item trong 1 page
var pageMax = feedSettings.itemLength + feedSettings.pagelimit;
console.log('pageMax: ' + pageMax);
var firstNewItemId = null;
var feedLength = $feed.find('.card').length;
for (var count = feedLength; count < pageMax; count++) {
var itemId = faker.random.uuid();
if (!firstNewItemId) firstNewItemId = itemId;
var itemImage = faker.image.image();
var itemTitle = faker.lorem.sentence();
var itemContent = faker.lorem.paragraphs();
var item = `<div class="post card mb-5" id="post_${itemId}" data-ubt="{category:'newfeed', action:'view', label: 'post', value: '${itemId}'}">
<img src="${itemImage}" class="card-img-top">
<div class="card-body">
<h5 class="card-title">${itemTitle}</h5>
<p class="card-text">${itemContent}</p>
</div>
</div>`;
$('#feed').append(item);
}
feedSettings.itemLength = count; // update offset
feedSettings.scrollPosTrigger = $(document).height() - ($(window).height() + feedSettings.scrollOffet); // Cach footer 1 khoang = 1 page + offset thi se goi ham load next page
// Add loadmore button again
$feed.append('<button class="btn btn-primary show-more" type="button">Show more &raquo;</button>');
console.log('Start monitoring scroll...');
$(window).on('scroll', checkScroll);
}
/* --- ON CLICK SHOW MORE --- */
$feed.on("click", '.show-more', function () {
$(this).html('Loading next ' + feedSettings.pagelimit + ' items &hellip;');
$(this).addClass('active');
setTimeout(loadNextPage, 300);
});
/* --- SCROLL HANDLER --- */
var checkScroll = _.debounce(function () {
console.log('Checking scroll...');
// neu vi tri scroll >= diem load page
console.log('SCROLLING', $(this).scrollTop(), feedSettings.scrollPosTrigger);
if ($(this).scrollTop() >= feedSettings.scrollPosTrigger) {
$feed.find('.show-more').click();
}
}, 300);
/* --- BEHAVIOR TRACKING --- */
function calculateVisibilityForDiv($el) {
var windowHeight = $(window).height(), // Viewport heigh
docScroll = $(document).scrollTop(), // Current scroll position
divPosition = $el.offset().top, // Element postion
divHeight = $el.height(), // Element height
hiddenBefore = docScroll - divPosition, // Hidden part above viewport top
hiddenAfter = (divPosition + divHeight) - (docScroll + windowHeight); // hidden part below viewport bottom
if ((docScroll > divPosition + divHeight) || (divPosition > docScroll + windowHeight)) {
// If the element is completely above or below the viewport
return 0;
} else {
var result = 100;
if (hiddenBefore > 0) {
result -= (hiddenBefore * 100) / divHeight;
}
if (hiddenAfter > 0) {
result -= (hiddenAfter * 100) / divHeight;
}
return result; // % body is visible
}
}
var parseUbt = function (rawUbt) {
if (rawUbt) {
// Replace ":" with "@colon@" if it's between double-quotes
var formattedUbt = rawUbt.replace(/:\s*"([^"]*)"/g, function (match, p1) {
return ': "' + p1.replace(/:/g, '@colon@') + '"';
})
// Replace ":" with "@colon@" if it's between single-quotes
.replace(/:\s*'([^']*)'/g, function (match, p1) {
return ': "' + p1.replace(/:/g, '@colon@') + '"';
})
// Add double-quotes around any tokens before the remaining ":"
.replace(/(['"])?([a-z0-9A-Z_]+)(['"])?\s*:/g, '"$2": ')
// Turn "@colon@" back into ":"
.replace(/@colon@/g, ':');
return JSON.parse(formattedUbt);
} else {
return null;
}
};
var behaviourTrackkingListener = _.debounce(function () {
$('[data-ubt]').each(function () {
var visiblePercent = calculateVisibilityForDiv($(this));
// If visible body is more than 30%
if (visiblePercent > 30) {
var ubt = $(this).data('ubt');
console.log('ubt', ubt);
var userBehavior = parseUbt(ubt);
console.log('userBehavior', userBehavior);
console.log(userBehavior, ' has visible body:', visiblePercent + '%');
}
});
}, 500);
/* --- INIT SCROLL LOADER --- */
function init() {
console.log('Adding "Show more" link');
$feed.append('<button class="btn btn-primary show-more" type="button">Show more &raquo;</button>');
$feed.find('.show-more').click();
// User behavior tracking
$(window).on('scroll', behaviourTrackkingListener);
}
/* --- TRIGGER SCROLL LOADER --- */
init();
});
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment