Search code examples
javascriptscrollwindow

Why need to decrease clientHeight from windowHeight while calculating scrollPercent?


I am trying to show how much a user has scrolled through the page a with progress bar and I have done it. But I have a little confusion here.

Here is the code that I found to calculate scrollPercent which works well


windowHeight = Math.max(
html.clientHeight,
html.scrollHeight,
html.offsetHeight,
body.scrollHeight,
body.offsetHeight
);

const  scrolledPercent =
((html.scrollTop || body.scrollTop) / (windowHeight - html.clientHeight)) *
100;

Initially, I thought, to get the scrollPercent , I need to get the current scrollPosition and divide that number with the total height of the page and multiply by 100% . which is like normally how we get % of something.

const  scrolledPercent = 
((html.scrollTop || body.scrollTop) / windowHeight) * 100;

but this line doesnot worked as I expected . If I do this the progress bar wont reach 100% even if I scroll to end of the page. I don't understand why am I wrong here !

So, my question is why do we need to decrease the html.clientHeight from windowHeight ?

Thank you.

Demo here:

// --------------------------------------------
// variables
// --------------------------------------------

const html = document.documentElement,
  body = document.body,
  countryList = document.querySelector(".country__list");
scrollNavigated = document.querySelector(".scroll__navigated");
let windowHeight;

// --------------------------------------------
// function
// --------------------------------------------

async function prepareListOfCountries() {
  let list = await fetch("https://restcountries.eu/rest/v2/all");
  list = Array.from(await list.json());
  let markup = list
    .map((country, index) => {
      return `<li class="country__item card">
              <span class="country__name">${country.name}</span
              ><span class="country__capital">${country.capital}</span>
              <a href="javascript:;" class="country__flag">
               <img src= '${country.flag}'> </a>
             
        </li>`;
    })
    .slice(0, 30)
    .join(" ");
  countryList.innerHTML = markup;
}

function updateScrolledStatus(e) {
  windowHeight = Math.max(
    html.clientHeight,
    html.scrollHeight,
    html.offsetHeight,
    body.scrollHeight,
    body.offsetHeight
  );

  const scrolledPercent =
    ((html.scrollTop || body.scrollTop) / (windowHeight - html.clientHeight)) *
    100;
  // const scrolledPercent =
  //   ((html.scrollTop || body.scrollTop) / windowHeight) * 100; // this line doesnot work
  scrollNavigated.style.width = scrolledPercent + "%";
}

prepareListOfCountries();

// --------------------------------------------
// event-handler
// --------------------------------------------

window.addEventListener("scroll", updateScrolledStatus);
*::after,
*::before,
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

body {
  background: #fff;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen,
 Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
}

.container {
  max-width: 980px;
  margin: 0 auto;
}

.justify-between {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.items-center {
  display: flex;
  align-items: center;
}

.card {
  background-color: #fff;
  box-shadow: 0 0 12px 12px rgba(0, 0, 0, 0.054);
  border-radius: 4px;
  padding: 16px;
}

.country__flag img {
  height: 100%;
  width: 100%;
}

.header {
  padding: 24px 0;
  background-color: #333;
  color: #f1f1f1;
  position: -webkit-sticky;
  position: sticky;
}

.content {
  padding: 50px 0;
}

.content__form {
  margin: 0 auto;
  margin-bottom: 32px;
}

.content__search {
  width: 50%;
  padding: 12px 16px;
  border-radius: 20px;
  border: 1px solid #ddd;
  transition: 0.2s;
}

.content__search:hover {
  box-shadow: 0 1px 6px 0 rgba(32, 33, 36, 0.28);
}

.content__search:focus {
  outline: none;
}

.country__list {
  margin-top: 50px;
  margin: 10px auto;
}

.country__item {
  display: flex;
  justify-content: space-between;
  margin-bottom: 16px;
}

.country__name, .country__capital, .country__flag {
  width: 33.33%;
}

.country__flag {
  width: 32px;
  height: 24px;
}

.scroll__navigator {
  height: 2px;
  margin: 0 auto 32px;
  background-color: #333;
  position: -webkit-sticky;
  position: sticky;
  top: 0;
}

.scroll__navigated {
  display: block;
  height: 100%;
  width: 0;
  background: orangered;
  transition: 0.3s linear;
}
<body>
    <header class="header">
      <div class="container">
        All countries list
      </div>
    </header>
    <main class="content">
      <div class="container">
        <form class="content__form">
          <input class="content__search" />
        </form>
        <div class="scroll__navigator">
          <span class="scroll__navigated"></span>
        </div>
        <section class="country">
          <ul class="country__list">
            <li class="country__item card">
              <span class="country__name">Nepal</span
              ><span class="country__capital">Kathmandu</span>
              <a href="javascript:;" class="country__flag"></a>
            </li>
          </ul>
        </section>
      </div>
    </main>
     
  </body>
 


Solution

  • As an example, say that the height of your client is 100px and the height of your whole page is 500px.

    When the scroll position is 0px, you're able to see the first 100px of your site, so from 0px to 100px.

    At scroll position 100px, you can see the range 100px to 200px, because you've moved the page, and therefore the visible range, on by 100px.

    At scroll position 400px, you can therefore see the range 400px to 500px – in other words, you've scrolled to the bottom.

    This demonstrates that the scrollable height of the page (400px) is less than the actual height of the page (500px), namely by the height of the client.

    To get the percentage scrolled, you need to use the scrollable height, so it is necessary to subtract the height of the client from the height of the page to get a correct value, or you'll never be able to scroll to the bottom. It's not possible to scroll by 500px on a site that is only 500px long!