Created
December 2, 2019 10:03
-
-
Save mrtonyhuynh/f23a95e93879c52f64aed794a07101f6 to your computer and use it in GitHub Desktop.
Infinity Scroll Loadmore & Event Tracking
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!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 »</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 …'); | |
| $(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 »</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