Is there a way to track the amount a user scrolls once they have reached the bottom of a page using Javascript?

It's a popular UI pattern on mobile devices to have a draggable element containing a scrollable element. Once the user reaches the end of the scrollable content, further scrolling should transition into dragging the outer element. For example, in this demonstration (), after reaching the top and trying to scroll further, the subreddits menu starts to drag.

I am interested in creating a similar functionality using JS/CSS. Is there a way to detect when users continue scrolling beyond the end? Additionally, is it feasible to measure how much they scroll past the boundary?

Answer №1

 window.onscroll = function(element) {
    if ((window.innerHeight + window.pageYOffset) >= document.body.offsetHeight) {
       alert("you're at the bottom of the page");
    }
 };

Utilizing the element parameter to accurately determine the current x and y coordinates where the mouse is located, enabling calculation of scroll distance.

Javascript: How to detect if browser window is scrolled to bottom?

Answer №2

If you want to monitor user activity after reaching the bottom or top of the page, in addition to tracking the scroll event, it's important to also track the wheel event. Furthermore, on mobile devices, tracking touchstart and touchmove events is necessary.

Since not all browsers normalize these events consistently, I created my own normalization function that looks something like this:

var compulsivity = Math.log2(Math.max(scrollAmount, 0.01) * wheelAmount);

Below is a complete interactive playground where you can test these tracking functionalities. It works best in Chrome using Mobile View in Developer Tools, or with TouchEmulator for other browsers.

// JavaScript code for tracking user activity
// ...
.ui-page {
  touch-action: none;
}
h1, h2, h3, h4, h5, h6, p {
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
}
/* Custom CSS styles */
<!-- HTML markup and dependencies -->

Another important aspect to consider is the pull-to-refresh feature and the inertia or momentum of smooth scrolling behaviors. Be sure to observe how the events are tracked by scrolling or swiping, as indicated by color changes in the top and bottom bars of the page.

Answer №3

JavaScript:

// retrieve the button element
var btn = document.getElementById('btn');
// fetch the box element
var box = document.getElementById('box');

// add click event to the button to toggle show/hide for the box
btn.addEventListener('click', () => {
  box.classList.toggle('active');
});

// when scrolling inside the box
box.onscroll = function(){
  // get the top position of the div
  var boxTop = box.scrollTop;
  if(boxTop <= 0){
    // hide the box when it reaches or goes below 0 by toggling the class from "show" to "hide"
    box.classList.toggle('active');
  }
};
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body {
  font-size: 10px;
  font-family: 'Arial', sans-serif;
  height: 1500px;
}

html {
  scroll-behavior: smooth;
}

ul {
  list-style-type: none;
}

#theBox ul li {
  border: 1px solid;
  height: 100px;
}

#navbar-bottom {
  height: 100px;
  width: 100%;
  background: rgb(90, 111, 143);
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  box-shadow: 0 0 2px 2px rgba(90, 111, 143, 0.562);
  display: flex;
  justify-content: space-around;
  align-items: center;
}

#theBox {
  background-color: red;
  height: 350px;
  width: 100%;
  position: fixed;
  bottom: 0;
  transform: translateY(100%);
  transition: all 0.3s;
  overflow-y: scroll;
}

#theBox.active{
  transform: translateY(0);
}

.myBtns {
  width: 50px;
  height: 50px;
  border-radius: 50%;
  border: none;
  position: relative;
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
  cursor: pointer;
}

.myBtns span {
  height: 3px;
  width: 30px;
  background-color: black;
  margin: 3px 0;
}
<main role="main">

    <div id="theBox">
      <ul>
        <li><p>Text</p></li>
        <li><p>Text</p></li>
        <li><p>Text</p></li>
        <li><p>Text</p></li>
        <li><p>Text</p></li>
        <li><p>Text</p></li>
        <li><p>Text</p></li>
        <li><p>Text</p></li>
        <li><p>Text</p></li>
      </ul>
    </div>

    <div id="navbar-bottom">
      <button class="myBtns"></button>
      <button class="myBtns" id="btn">
        <span></span>
        <span></span>
        <span></span>
      </button>
      <button class="myBtns"></button>
    </div>
  </main>

jQuery:

// add click event to the button to toggle show/hide for the box
$('#btn').click(function(){
  $('#box').toggleClass('active');
});

// when scrolling on the box
$('#box').scroll(function () {
  // get the top position of the div
  var boxTop = $('#box').scrollTop();
  if(boxTop <= 0){
    $('#box').toggleClass('active');
  }
});
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body {
  font-size: 10px;
  font-family: 'Arial', sans-serif;
  height: 1500px;
}

html {
  scroll-behavior: smooth;
}

ul {
  list-style-type: none;
}

#theBox ul li {
  border: 1px solid;
  height: 100px;
}

#navbar-bottom {
  height: 100px;
  width: 100%;
  background: rgb(90, 111, 143);
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  box-shadow: 0 0 2px 2px rgba(90, 111, 143, 0.562);
  display: flex;
  justify-content: space-around;
  align-items: center;
}

#theBox {
  background-color: red;
  height: 350px;
  width: 100%;
  position: fixed;
  bottom: 0;
  transform: translateY(100%);
  transition: all 0.3s;
  overflow-y: scroll;
}

#theBox.active{
  transform: translateY(0);
}

.myBtns {
  width: 50px;
  height: 50px;
  border-radius: 50%;
  border: none;
  position: relative;
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
  cursor: pointer;
}

.myBtns span {
  height: 3px;
  width: 30px;
  background-color: black;
  margin: 3px 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<main role="main">

    <div id="theBox">
      <ul>
        <li><p>Text</p></li>
        <li><p>Text</p></li>
        <li><p>Text</p></li>
        <li><p>Text</p></li>
        <li><p>Text</p></li>
        <li><p>Text</p></li>
        <li><p>Text</p></li>
        <li><p>Text</p></li>
        <li><p>Text</p></li>
      </ul>
    </div>

    <div id="navbar-bottom">
      <button class="myBtns"></button>
      <button class="myBtns" id="btn">
        <span></span>
        <span></span>
        <span></span>
      </button>
      <button class="myBtns"></button>
    </div>
  </main>

Answer №4

window.addEventListener('scroll', function() {
    if ((window.innerHeight + window.scrollY) >= document.body.offsetHeight) {
       alert("You have reached the bottom of the page");
    }
});

Check out the live demo here: http://jsfiddle.net/5xpoe4yg/

Answer №5

Here are two different solutions for your issue, catering to touch devices and mouse devices respectively.

For Mouse Devices:

If the target is a mouse device, we can utilize the following method:

document.onwheel = event => ScrollAction(event);

For more information on the wheel event, you can visit this link.

For Touch Devices:

If the target is a touch device, the following methods will be helpful:

document.ontouchcancel = event => TouchInterrupt(event);
document.ontouchend = event => FingerRemoved(event);
document.ontouchmove = event => FingerDragged(event);
document.ontouchstart = event => FingerPlaced(event);

For further details on touch events, please refer to this link.

This complete solution should resolve your issue effectively.

Answer №6

Your specific question can be addressed by monitoring the wheel event, but keep in mind that the accuracy of the result may not be perfect. The wheel event tends to trigger before the scroll event, leading to occasional instances where a negative scroll value is logged when scrolling up from the bottom of the page for the first time:

const content = document.querySelector('.content');

for (let i = 0; i < 50; i++) {
  const p = document.createElement('p');
  p.textContent = 'Content';
  content.append(p);
};

content.addEventListener('wheel', e => {
  const atBottom = content.scrollHeight - content.scrollTop === content.clientHeight;
  if (atBottom) console.log(e.deltaY);
});
* {
  padding: 0;
  margin: 0;
  box-sizing: border-box;
}

body {
  height: 100vh;
  width: 100%;
}

.content {
  overflow-y: scroll;
  height: 100%;
}
<div class="content"></div>

Alternatively, another suggestion is to implement an overlay that can be activated with a click or touch, allowing for smoother scrolling into view. It's worth noting that dealing with deeply nested scrolling elements on web browsers can become complicated quickly, especially without utilizing pure JS solutions which come with their own set of performance challenges.

Answer №7

This popup will open when clicked, allowing you to scroll. Once it reaches the top of the page, its header will stick in place.

var navbar = document.querySelector('.navbar'),
    navheader = document.querySelector('.navheader');

// Toggle navbar
navheader.addEventListener('click', e => {
  navbar.classList.toggle('open');
  if (!navbar.classList.contains('open')) {
    navbar.style.overflow = 'hidden';
    document.body.style.overflow = '';
    navbar.scrollTop = 0;
    stickTop = false;
    navbar.classList.remove('sticky');
    navbar.style.top = '';
    navbar.style.transition = '.2s';
    setTimeout(() => {
      navbar.style.transition = '';
    }, 200);
  }
  else {
    navbar.style.overflow = 'overlay';
    navbar.style.transition = '.2s';
    setTimeout(() => {
      navbar.style.transition = '';
    }, 200);
  }
})

var prevtop = 0;
var stickTop = false;

// Add scroll listener
navbar.addEventListener('scroll', e => {
  // If navbar is open
  if (navbar.classList.contains('open')) {
    if (!stickTop) {
      navbar.style.top = navbar.getBoundingClientRect().top - navbar.scrollTop + 'px';
    }
    
    if ((window.innerHeight - navbar.getBoundingClientRect().bottom) >= 0) {
      document.body.style.overflow = 'hidden';
      navbar.style.overflow = 'auto';
      navbar.style.top = 0;
      navbar.classList.add('sticky');
      stickTop = true;
    }
    
    if (navbar.scrollTop == 0) {
      navbar.classList.remove('open');
      navbar.style.overflow = 'hidden';
      document.body.style.overflow = '';
      stickTop = false;
      navbar.classList.remove('sticky');
      navbar.style.top = '';
      navbar.style.transition = '.2s';
      setTimeout(() => {
        navbar.style.transition = '';
      }, 200);
    }
  }
})
body {
  font-family: sans-serif;
}

.navbar {
  position: fixed;
  top: calc(100vh - 50px);
  height: 100vh;
  left: 0;
  width: 100%;
  overflow: hidden;
}

.navbar.open {
  top: 50vh;
}

.navcontent {
  background: black;
  width: 100%;
  color: white;
}
.navcontent p {
  margin: 0;
}

.navheader {
  height: 50px;
  width: 100%;
  background: lightblue;
  cursor: pointer;
  top: 0;
  position: sticky;
  display: flex;
  justify-content: center;
  z-index: 1;
}

.navheader::before {
  width: 50px;
  height: 3px;
  margin-top: 10px;
  background: white;
  border-radius: 3px;
  content: '';
}
<div class="navbar">
  <div class="navheader"></div>
  <div class="navcontent"><p>S</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>E</p></div>
</div>
<div class="content">
<p>S</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>E</p>
</div>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Step-by-step guide on creating a monochrome version of an image using automation

In WordPress, I have a logo parade where all logos are in color RGB. I really like the effect of having them appear as black and white initially and then transitioning to color when hovered over. I am familiar with using sprites for this effect, but it wo ...

What are the steps to troubleshoot CSS issues in NextJS?

Currently, I am encountering challenges while trying to integrate markdown with CSS. The problem I am facing has been replicated in the link provided: https://codesandbox.io/s/react-quilljsbasic-forked-3rcxw?file=/src/App.js ...

A script in PHP or JavaScript that dynamically generates two dual drop-down menus to assist with data selection

I have experience with php scripting, but I am facing challenges when trying to combine it with JavaScript. The issue arises when I have a form that includes dropdown menus for categories and subcategories. When a category is selected, the options in the s ...

The message appearing on my screen reads: "Attempting to read properties of an undefined value, specifically 'map'."

I am attempting to create a map of my various people, but I keep encountering an error with the title. I'm having trouble understanding where the issue lies, here is my code snippet: export const Testt = ({ childs}) => { console.log(childs) ...

Unable to transfer card from the top position in the screen

Utilizing Tailwind (flowbite) for the frontend of my project, I encountered an issue where the Navbar was overlapping the Card. I attempted using mt-8 to resolve the problem, but it did not yield significant changes. What adjustments should I make to achi ...

Safari's CSS Hacking Problem

After testing the code below, it appears to be working on both Safari and Chrome. I am seeking a hack specifically for Safari, not Chrome .contactDiv img:not(:root:root) { margin-top:-75px; } @media screen and (-webkit-min-device-pixel-ratio:0) { ...

JavaScript, JQuery, and the Observer Design Pattern

Currently, I am in the process of developing a third-party application specifically for certain websites using Jquery. Lately, I have incorporated rx.Observable into my project. However, grasping the utilization of this new JS library has proven to be qui ...

Adding Bootstrap 5 component to a create-react-app

Hello there! I appreciate any assistance with my Bootstrap 5 integration in a React app. I'm facing issues with including the Bootstrap component js, and below is the code snippet where I attempt to import it. import "bootstrap/dist/css/bootstrap ...

Having trouble retrieving accurate JSON data from an excel workbook

Currently, I am utilizing the npm module xlsx for the purpose of writing and reading JSON data. My goal is to take this JSON data and write it into an Excel file: { "name": "John", "class": 1, "address" : [ { "street": "12th Cross", "city": "London" }, { ...

Utilizing Kendo Grid for client-side data paging

I am looking to utilize an MVC helper for constructing a grid on the server side, with the ability to dynamically add and remove rows on the client side. To achieve this functionality, I have implemented the following wrapper: @(Html.Kendo().Grid<SIGE ...

Adaptable layout - featuring 2 cards side by side for smaller screens

I am currently designing a webpage that showcases a row of cards, each featuring an image and a title. At the moment, I am utilizing the Bootstrap grid system to display 4 cards per row on larger screens (col-md-2), but my goal is to modify this layout t ...

Tips for using MatTableDataSource in a custom Thingsboard widget

After creating multiple custom Thingsboard widgets, I've discovered that I can access a significant portion of @angular/material within my widget code. While I have successfully implemented mat-table, I now want to incorporate pagination, filtering, a ...

Align three divs vertically within one div, all floated to achieve the desired layout

Can someone help me align three divs vertically within a container div? The challenge is that all three divs inside the container are floated - one on the right, one in the center, and one on the left to create a navbar. I've seen the navbars in Boot ...

We encountered a ReferenceError stating that 'dc' is not defined, despite having already imported d3, dc, and crossfilter in

In my current angular project, I have included the necessary imports in the component.ts file in the specific order of d3, crossfilter2, dc, and leaflet. Additionally, I have added the cdn for dc-leaflet.js in the index.html file. Despite these steps, wh ...

Deploying NextJs application with Firebase's static site generation (SS

TL;DR The new data I add to my project is not displaying on the site, even though it's in the database. The issue arises after deploying with Firebase. I created a meetup website using Firebase as the backend. Everything works fine in development mo ...

Send a triggering function to two separate components

My objective is as follows: render() { return ( <div> <Child onTrigger={xxx} /> <button onClick={xxx} /> </div> ) } Upon clicking the button, I wish for some action to take place in ...

Issue encountered while submitting form on JQuery Mobile framework

Currently, I am in the process of developing a small web application utilizing JQuery Mobile and Multi Page architecture. The issue arises on my form as the second page. Upon clicking the submit button, an error message is displayed on my console (error i ...

The Gulp task is stuck in an endless cycle

I've set up a gulp task to copy all HTML files from a source folder to a destination folder. HTML Gulp Task var gulp = require('gulp'); module.exports = function() { return gulp.src('./client2/angularts/**/*.html') .pipe( ...

Align an element in the middle next to its sibling element

Utilizing the pagination component in Bootstrap, the structure of the pagination itself is quite straightforward: <ul class="pagination"> <li class="disabled"><a href="#" aria-label="Previous"><span aria-hidden="true">&laquo; ...

Position the li elements within the ul at the bottom

I am trying to align a li element at the bottom of a ul list. +------+ | li1 | | li2 | | li3 | | | | | | | | li4 | +------+ Any suggestions on how to achieve this? MY ATTEMPTED SOLUTION: li4 { position: fixed; bottom: 0; } W ...