777

I'm making a pagination system (sort of like Facebook) where the content loads when the user scrolls to the bottom. I imagine the best way to do that is to find when the user is at the bottom of the page and run an Ajax query to load more posts.

The only problem is I don't know how to check if the user has scrolled to the bottom of the page. Any ideas?

I'm using jQuery, so feel free to provide answers that use it.

trusktr
  • 44,284
  • 53
  • 191
  • 263
Johnny
  • 9,725
  • 5
  • 29
  • 34

32 Answers32

1116

Use the .scroll() event on window, like this:

$(window).scroll(function() {
   if($(window).scrollTop() + $(window).height() == $(document).height()) {
       alert("bottom!");
   }
});

You can test it here, this takes the top scroll of the window, so how much it's scrolled down, adds the height of the visible window and checks if that equals the height of the overall content (document). If you wanted to instead check if the user is near the bottom, it'd look something like this:

$(window).scroll(function() {
   if($(window).scrollTop() + $(window).height() > $(document).height() - 100) {
       alert("near bottom!");
   }
});

You can test that version here, just adjust that 100 to whatever pixel from the bottom you want to trigger on.

Nick Craver
  • 623,446
  • 136
  • 1,297
  • 1,155
193

TL;DR;

Math.abs(element.scrollHeight - element.scrollTop - element.clientHeight) < 1

Concept

At its core, "having scrolled to the bottom" refers to the moment when the scrollable area (scrollHeight) minus the distance of the visible content from the top (scrollTop) equals the height of the visible content (clientHeight).

Differently put, we are "scrolled" when this equivalence is true::

scrollHeight - scrollTop - clientHeight === 0

Diagram showing arrows and boxes for the scrollable area, client visible height and distance from the top in a graphical browser.

Preventing Rounding Error

As mentioned however, some of these properties are rounded, which means that the equality can fail in cases where scrollTop would have a decimal component or when the rounded values align poorly.

It is possible to mitigate that problem by comparing the absolute difference to a tolerable threshold:

Math.abs(element.scrollHeight - element.clientHeight - element.scrollTop) < 1

A snippet that prevents rouding error could look like this:

document.getElementById('constrained-container').addEventListener('scroll', event => {
    const {scrollHeight, scrollTop, clientHeight} = event.target;

    if (Math.abs(scrollHeight - clientHeight - scrollTop) < 1) {
        console.log('scrolled');
    }
});
#constrained-container {
  height: 150px;
  overflow-y: scroll;
}

#very-long-content {
  height: 600px;
}
<div id="constrained-container">
  <div id="very-long-content">
    scroll me to the bottom
  </div>
</div>

Note that I've added a div that is too big for its container to force the scrolling but there's no need to "wrap" the content in another element, text directly in an element would make the element overflow.

Debouncing, Delaying and Throttling

The more I understand about it and the less I find it's within the scope of this answer (this Code Review question and its answer, and this linked article are of interest), but in specific cases (if the handler does expensive computation, if we tie an animation to the scroll event, if we only want to launch the event at the end of the scroll motion, or any situation that may warrants it) it can be useful to:

  • debounce (fire the handler when the first scroll happen, then prevent it from firing too fast),
  • delay (prevent the execution of the handler until the scroll event wasn't fired for a period of time. this is often called debouncing in ECMAScript context),
  • or throttle (preventing the handler to fire more than once every period of time).

Great care must be taken in choosing to do any of these things, for instance throttling the event could prevent the last scroll to fire, which could completely defeat an infinite scroller.

Not doing any of those three things works perfectly fine most of the time, as just looking if we're completely scrolled is relatively inexpensive.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
  • 28
    `Math.abs(element.scrollHeight - element.scrollTop - element.clientHeight) <= 3.0` (replace 3.0 with whatever pixel tolerance you think is appropriate for your circumstances). This is the way to go because (A) `clientHeight`, `scrollTop`, and `clientHeight` are all rounded which could potentially lead to a 3px error if all align, (B) 3 pixels is hardly visible to the user so the user may think something is wrong with your site when they "think" they are at the bottom of the page when in fact they are not, and (C) certain devices (especially mouseless ones) can have odd behavior when scrolling. – Jack G Oct 21 '18 at 18:50
125

Nick Craver's answer works fine, spare the issue that the value of $(document).height() varies by browser.

To make it work on all browsers, use this function from James Padolsey:

function getDocHeight() {
    var D = document;
    return Math.max(
        D.body.scrollHeight, D.documentElement.scrollHeight,
        D.body.offsetHeight, D.documentElement.offsetHeight,
        D.body.clientHeight, D.documentElement.clientHeight
    );
}

in place of $(document).height(), so that the final code is:

$(window).scroll(function() {
       if($(window).scrollTop() + $(window).height() == getDocHeight()) {
           alert("bottom!");
       }
   });
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Miles O'Keefe
  • 1,494
  • 1
  • 11
  • 17
40

Further to the excellent accepted answer from Nick Craver, you can throttle the scroll event so that it is not fired so frequently thus increasing browser performance:

var _throttleTimer = null;
var _throttleDelay = 100;
var $window = $(window);
var $document = $(document);

$document.ready(function () {

    $window
        .off('scroll', ScrollHandler)
        .on('scroll', ScrollHandler);

});

function ScrollHandler(e) {
    //throttle event:
    clearTimeout(_throttleTimer);
    _throttleTimer = setTimeout(function () {
        console.log('scroll');

        //do work
        if ($window.scrollTop() + $window.height() > $document.height() - 100) {
            alert("near bottom!");
        }

    }, _throttleDelay);
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
George Filippakos
  • 16,359
  • 15
  • 81
  • 92
35

Nick Craver's answer needs to be slightly modified to work on iOS 6 Safari Mobile and should be:

$(window).scroll(function() {
   if($(window).scrollTop() + window.innerHeight == $(document).height()) {
       alert("bottom!");
   }
});

Changing $(window).height() to window.innerHeight should be done because when the address bar is hidden an additional 60 pixels are added to the window's height, but using $(window).height() does not reflect this change, while using window.innerHeight does.

Note: The window.innerHeight property also includes the horizontal scrollbar's height (if it is rendered), unlike $(window).height() which will not include the horizontal scrollbar's height. This is not a problem in Mobile Safari, but could cause unexpected behavior in other browsers or future versions of Mobile Safari. Changing == to >= could fix this for most common use cases.

Read more about the window.innerHeight property here.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
BlueYoshi
  • 1,534
  • 12
  • 18
30

Here's a fairly simple approach

const didScrollToBottom = elm.scrollTop + elm.clientHeight == elm.scrollHeight

Example

elm.onscroll = function() {
    if(elm.scrollTop + elm.clientHeight == elm.scrollHeight) {
        // User has scrolled to the bottom of the element
    }
}

Where elm is an element retrieved from i.e document.getElementById.

Frederik Witte
  • 936
  • 1
  • 16
  • 31
25

Please check this answer.

 window.onscroll = function(ev) {
    if ((window.innerHeight + window.scrollY) >= document.body.offsetHeight) {
       console.log("bottom");
    }
};

You can do footerHeight - document.body.offsetHeight to see if you are near the footer or reached the footer.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Junaid Qadir Shekhanzai
  • 1,386
  • 1
  • 20
  • 38
16

Here is a piece of code that will help you debug your code. I tested the above answers and found them to be buggy. I have tested the following on Chrome, Internet Explorer, Firefox, iPad (Safari). I don't have any others installed to test...

<script type="text/javascript">
   $(function() {
      $(window).scroll(function () {
         var docElement = $(document)[0].documentElement;
         var winElement = $(window)[0];

         if ((docElement.scrollHeight - winElement.innerHeight) == winElement.pageYOffset) {
            alert('bottom');
         }
      });
   });
</script>

There may be a simpler solution, but I stopped at the point at which it worked

If you are still having problems with some rogue browser, here is some code to help you debug:

<script type="text/javascript">
   $(function() {
      $(window).scroll(function () {
         var docElement = $(document)[0].documentElement;
         var details = "";
         details += '<b>Document</b><br />';
         details += 'clientHeight:' + docElement.clientHeight + '<br />';
         details += 'clientTop:' + docElement.clientTop + '<br />';
         details += 'offsetHeight:' + docElement.offsetHeight + '<br />';
         details += 'offsetParent:' + (docElement.offsetParent == null) + '<br />';
         details += 'scrollHeight:' + docElement.scrollHeight + '<br />';
         details += 'scrollTop:' + docElement.scrollTop + '<br />';

         var winElement = $(window)[0];
         details += '<b>Window</b><br />';
         details += 'innerHeight:' + winElement.innerHeight + '<br />';
         details += 'outerHeight:' + winElement.outerHeight + '<br />';
         details += 'pageYOffset:' + winElement.pageYOffset + '<br />';
         details += 'screenTop:' + winElement.screenTop + '<br />';
         details += 'screenY:' + winElement.screenY + '<br />';
         details += 'scrollY:' + winElement.scrollY + '<br />';

         details += '<b>End of page</b><br />';
         details += 'Test:' + (docElement.scrollHeight - winElement.innerHeight) + '=' + winElement.pageYOffset + '<br />';
         details += 'End of Page? ';
         if ((docElement.scrollHeight - winElement.innerHeight) == winElement.pageYOffset) {
             details += 'YES';
         } else {
             details += 'NO';
         }

         $('#test').html(details);
      });
   });
</script>
<div id="test" style="position: fixed; left:0; top: 0; z-index: 9999; background-color: #FFFFFF;">
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Talon
  • 3,466
  • 3
  • 32
  • 47
11
var elemScrolPosition = elem.scrollHeight - elem.scrollTop - elem.clientHeight;

It calculates distance scroll bar to bottom of element. Equal 0, if scroll bar has reached bottom.

Vasyl Gutnyk
  • 4,813
  • 2
  • 34
  • 37
10

Here is a vanilla JavaScript solution that uses ES6 and debounce:

document.addEventListener('scroll', debounce(() => {
  if(document.documentElement.scrollHeight === window.pageYOffset + window.innerHeight) {
    // Do something
  }
}, 500))

function debounce(e,t=300){let u;return(...i)=>{clearTimeout(u),u=setTimeout(()=>{e.apply(this,i)},t)}}

Demo: https://jsbin.com/jicikaruta/1/edit?js,output

References:

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
l2aelba
  • 21,591
  • 22
  • 102
  • 138
10

This is my two cents:

$('#container_element').scroll(function() {
    console.log($(this).scrollTop() + ' + ' + $(this).height() + ' = ' + ($(this).scrollTop() + $(this).height()) + ' _ ' + $(this)[0].scrollHeight);
    if($(this).scrollTop() + $(this).height() == $(this)[0].scrollHeight) {
        console.log('bottom found');
    }
});
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
ddanone
  • 464
  • 4
  • 8
7

Instead of listening to the scroll event, using Intersection Observer is the inexpensive one for checking if the last element was visible on the viewport (that means the user has scrolled to the bottom). It also supported for Internet Explorer 7 with the polyfill.

var observer = new IntersectionObserver(function(entries){
   if(entries[0].isIntersecting === true)
      console.log("Scrolled to the bottom");
   else
      console.log("Not on the bottom");
}, {
   root:document.querySelector('#scrollContainer'),
   threshold:1 // Trigger only when whole element was visible
});

observer.observe(document.querySelector('#scrollContainer').lastElementChild);
#scrollContainer{
  height: 100px;
  overflow: hidden scroll;
}
<div id="scrollContainer">
  <div>Item 1</div>
  <div>Item 2</div>
  <div>Item 3</div>
  <div>Item 4</div>
  <div>Item 5</div>
  <div>Item 6</div>
  <div>Item 7</div>
  <div>Item 8</div>
  <div>Item 9</div>
  <div>Item 10</div>
</div>
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
StefansArya
  • 2,802
  • 3
  • 24
  • 25
6

My solution in plain js:

let el=document.getElementById('el');
el.addEventListener('scroll', function(e) {
    if (this.scrollHeight - this.scrollTop - this.clientHeight<=0) {
        alert('Bottom');
    }
});
#el{
  width:400px;
  height:100px;
  overflow-y:scroll;
}
<div id="el">
<div>content</div>
<div>content</div>
<div>content</div>
<div>content</div>
<div>content</div>
<div>content</div>
<div>content</div>
<div>content</div>
<div>content</div>
<div>content</div>
<div>content</div>
</div>
Fay Boisam
  • 127
  • 4
  • 10
Bakos Bence
  • 243
  • 5
  • 7
5

Nick answers its fine but you will have functions which repeats itsself while scrolling or will not work at all if user has the window zoomed. I came up with an easy fix just math.round the first height and it works just as assumed.

    if (Math.round($(window).scrollTop()) + $(window).innerHeight() == $(document).height()){
    loadPagination();
    $(".go-up").css("display","block").show("slow");
}
Florin Andrei
  • 287
  • 2
  • 11
4

In case someone wants a vanilla JavaScript solution and needs to detect when a user has scrolled to the bottom of a <div> I managed to implement it by using these lines of code

window.addEventListener("scroll", () => {
    var offset = element.getBoundingClientRect().top - element.offsetParent.getBoundingClientRect().top;
    const top = window.pageYOffset + window.innerHeight - offset;

    if (top === element.scrollHeight) {
        console.log("bottom");
    }
}, { passive: false });
Sarah
  • 73
  • 2
  • 3
mihails.kuzmins
  • 1,140
  • 1
  • 11
  • 19
4

I have done this in a very easy way with pure JavaScript:

function onScroll() {
    if (window.pageYOffset + window.innerHeight >= document.documentElement.scrollHeight - 50) {
        Console.log('Reached bottom')
    }
}
window.addEventListener("scroll", onScroll);
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Abhay Singh
  • 1,595
  • 1
  • 23
  • 40
3

Safari can scroll past the bottom of the page which was causing a bug in our application. Solve this using >= instead of ===.

container.scrollTop >= container.scrollHeight - container.clientHeight
Scott Wager
  • 758
  • 11
  • 9
2

All these solutions doesn't work for me on Firefox and Chrome, so I use custom functions from Miles O'Keefe and meder omuraliev like this:

function getDocHeight()
{
    var D = document;
    return Math.max(
        D.body.scrollHeight, D.documentElement.scrollHeight,
        D.body.offsetHeight, D.documentElement.offsetHeight,
        D.body.clientHeight, D.documentElement.clientHeight
    );
}

function getWindowSize()
{
  var myWidth = 0, myHeight = 0;
  if( typeof( window.innerWidth ) == 'number' ) {
    //Non-IE
    myWidth = window.innerWidth;
    myHeight = window.innerHeight;
  } else if( document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight ) ) {
    //IE 6+ in 'standards compliant mode'
    myWidth = document.documentElement.clientWidth;
    myHeight = document.documentElement.clientHeight;
  } else if( document.body && ( document.body.clientWidth || document.body.clientHeight ) ) {
    //IE 4 compatible
    myWidth = document.body.clientWidth;
    myHeight = document.body.clientHeight;
  }
  return [myWidth, myHeight];
}

$(window).scroll(function()
{
    if($(window).scrollTop() + getWindowSize()[1] == getDocHeight())
    {
        alert("bottom!");
    }
});
hayj
  • 1,159
  • 13
  • 21
2

You can try the following code,

$("#dashboard-scroll").scroll(function(){
    var ele = document.getElementById('dashboard-scroll');
    if(ele.scrollHeight - ele.scrollTop === ele.clientHeight){
       console.log('at the bottom of the scroll');
    }
});
Shahrukh Anwar
  • 2,544
  • 1
  • 24
  • 24
2

Try this for match condition if scroll to bottom end

if ($(this)[0].scrollHeight - $(this).scrollTop() == 
    $(this).outerHeight()) {

    //code for your custom logic

}
Jake Freeman
  • 1,700
  • 1
  • 8
  • 15
Hiren Patel
  • 317
  • 4
  • 7
2

This gives accurate results, when checking on a scrollable element (i.e. not window):

// `element` is a native JS HTMLElement
if ( element.scrollTop == (element.scrollHeight - element.offsetHeight) )
    // Element scrolled to bottom

offsetHeight should give the actual visible height of an element (including padding, margin, and scrollbars), and scrollHeight is the entire height of an element including invisible (overflowed) areas.

jQuery's .outerHeight() should give similar result to JS's .offsetHeight -- the documentation in MDN for offsetHeight is unclear about its cross-browser support. To cover more options, this is more complete:

var offsetHeight = ( container.offsetHeight ? container.offsetHeight : $(container).outerHeight() );
if  ( container.scrollTop == (container.scrollHeight - offsetHeight) ) {
   // scrolled to bottom
}

Yuval A.
  • 5,849
  • 11
  • 51
  • 63
2

I used this test to detect the scroll reached the bottom:

event.target.scrollTop === event.target.scrollHeight - event.target.offsetHeight

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Katzura
  • 147
  • 1
  • 4
1

Here's my two cents as the accepted answer didn't work for me.

var documentAtBottom = (document.documentElement.scrollTop + window.innerHeight) >= document.documentElement.scrollHeight;
alseether
  • 1,889
  • 2
  • 24
  • 39
Razor
  • 17,271
  • 25
  • 91
  • 138
1

Google Chrome gives the full height of the page if you call $(window).height().

Instead, use window.innerHeight to retrieve the height of your window.

The necessary check should be:

if($(window).scrollTop() + window.innerHeight > $(document).height() - 50) {
    console.log("reached bottom!");
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
alierdogan7
  • 600
  • 7
  • 14
1

Many other solutions don't work for me, because on scroll to the bottom, my div was triggering the alert two times and when moving up it was also triggering up to a few pixels so the solution is:

$('#your-div').on('resize scroll', function()
{
    if ($(this).scrollTop() +
        $(this).innerHeight() >=
        $(this)[0].scrollHeight + 10)
    {
        alert('reached bottom!');
    }
});
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
MR_AMDEV
  • 1,712
  • 2
  • 21
  • 38
1

(2021) Lots of answers here involve a ref to an element, but if you only care about the whole page, just use:

function isBottom() {
  const { scrollHeight, scrollTop, clientHeight } = document.documentElement;
  const distanceFromBottom = scrollHeight - scrollTop - clientHeight;
  return distanceFromBottom < 20; // adjust the number 20 yourself
}
ZYinMD
  • 3,602
  • 1
  • 23
  • 32
1

Trying all the answers, none of them worked for me. However, some of the answers did not take into account the issue of zooming, where the calculation may not work as expected when the user zooms in or out.

Here is what you need to do in ReactJS :

Let's say we have a device with a physical screen resolution of 1920x1080 pixels and a device pixel ratio of 2. This means that the device has a virtual screen resolution of 960x540 CSS pixels (since the device pixel ratio is 2, the number of CSS pixels is half the number of physical pixels).

Now let's say we want to calculate the height of the viewport in physical pixels. We can do this by multiplying the height of the viewport in CSS pixels (which we can get using document.documentElement.clientHeight) by the device pixel ratio (which we can get using window.devicePixelRatio).

So, if the height of the viewport in CSS pixels is 540, then:

const clientHeight = document.documentElement.clientHeight * window.devicePixelRatio;

const viewportHeightInPixels = clientHeight; // since devicePixelRatio is 2 in this example
console.log(viewportHeightInPixels); // Output: 1080

In this example, viewportHeightInPixels would be equal to 1080, since we've multiplied the viewport height in CSS pixels (which is 540) by the device pixel ratio (which is 2).

This tells us that the height of the viewport in physical pixels is 1080, which is the same as the device's physical screen resolution.

Here is an example code snippet that uses this calculation to check if the user has scrolled to the bottom of the page:

import { useEffect } from "react";

export default function CheckUserScroll(onBottomReached) {
  useEffect(() => {
    function handleScroll() {
      console.log('handleScroll')
      const scrollTop = document.documentElement.scrollTop;
      const scrollHeight = document.documentElement.scrollHeight;
      // Calculates the height of the client viewport, adjusted for the device's pixel ratio
      const clientHeight = document.documentElement.clientHeight * window.devicePixelRatio;
      if (scrollTop + clientHeight >= scrollHeight) {
        onBottomReached();
      }
    }
    window.addEventListener("scroll", handleScroll);
    return () => window.removeEventListener("scroll", handleScroll);
  }, [onBottomReached]);
}

Here is how you can use it in your JSX code:

import React from 'react';
import CheckUserScroll from './CheckUserScroll';

export default function Test() {
  function handleBottomReached() {
    console.log('bottom reached!');
    // do something else here
  }

  return (
    <div>
      <CheckUserScroll bottomReached={handleBottomReached} />
      <div style={{ height: '3000px' }}>Scroll down to trigger bottomReached function</div>
    </div>
  );
}
Divad
  • 273
  • 3
  • 14
0

Let me show an approach without jQuery. A simple JavaScript function:

function isVisible(elem) {
  var coords = elem.getBoundingClientRect();
  var topVisible = coords.top > 0 && coords.top < 0;
  var bottomVisible = coords.bottom < shift && coords.bottom > 0;
  return topVisible || bottomVisible;
}

A short example of how to use it:

var img = document.getElementById("pic1");
if (isVisible(img)) { 
  img.style.opacity = "1.00";
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Alexei Zababurin
  • 927
  • 12
  • 15
  • 5
    This doesn't answer the question. The question was how to detect that the users has scrolled the window all the way to the bottom. Your code is checking if a particular element is in view. – Peter Hall Jan 05 '16 at 18:58
0

I used ddanone's answer and added an Ajax call.

$('#mydiv').on('scroll', function() {
    function infiniScroll(this);
});

function infiniScroll(mydiv) {
    console.log($(mydiv).scrollTop() + ' + ' + $(mydiv).height() + ' = ' + ($(mydiv).scrollTop() + $(mydiv).height()) + ' _ ' + $(mydiv)[0].scrollHeight);

    if($(mydiv).scrollTop() + $(mydiv).height() == $(mydiv)[0].scrollHeight) {
        console.log('bottom found');
        if(!$.active){ // If there isn't any Ajax call active
                       // (the last Ajax call waiting for results)
                       // do my Ajax call again
            myAjaxCall();
        }
    }
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Henrique C.
  • 948
  • 1
  • 14
  • 36
0

To stop repeated alert of Nick's answer

ScrollActivate();

function ScrollActivate() {
    $(window).on("scroll", function () {
        if ($(window).scrollTop() + $(window).height() > $(document).height() - 100) {
            $(window).off("scroll");
            alert("near bottom!");
        }
    });
}
Arun Prasad E S
  • 9,489
  • 8
  • 74
  • 87
0

Apparently what worked for me was 'body' and not 'window' like this:

$('body').scroll(function() {

    if($('body').scrollTop() + $('body').height() == $(document).height()) {
        // Alert at the bottom
    }
});

For cross-browser compatibility, use:

function getheight() {
    var doc = document;
    return Math.max(
        doc.body.scrollHeight, doc.documentElement.scrollHeight,
        doc.body.offsetHeight, doc.documentElement.offsetHeight,
        doc.body.clientHeight, doc.documentElement.clientHeight);
}

and then instead of $(document).height() call the function getheight()

$('body').scroll(function() {

    if($('body').scrollTop() + $('body').height() == getheight()) {
        // Alert at bottom
    }
});

For near the bottom, use:

$('body').scroll(function() {

    if($('body').scrollTop() + $('body').height() > getheight() - 100) {
        // Alert near bottom
    }
});
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
GeniusGeek
  • 315
  • 6
  • 14
0

Here is the most simple way to do it:

const handleScroll = () => {
if (window.innerHeight + window.pageYOffset >= document.body.offsetHeight) {
   console.log('scrolled to the bottom')
}}

window.addEventListener('scroll', handleScroll)
Dremiq
  • 335
  • 3
  • 10
  • Please explain your answer. (But *** *** *** *** *** ***without*** *** *** *** *** *** "Edit:", "Update:", or similar - the answer should appear as if it was written today). – Peter Mortensen Apr 13 '23 at 10:06