Imagine you are reading a book online using the website Project Gutenberg. After a while you stop reading and maybe close the browser tab. When you'll come back to reading (after a few hours or few days), you'll have to reenter the URL for your book and the HTML page will load at the beginning. It is up to you to remember where you left reading last time and scroll to that position. But, wouldn't it be nice if your browser would remember this for you and actually open the page directly at the desired position. This would be similar to what you do with a physical book, when you insert a bookmark on the page you stopped reading.
The example explained in these notes does exactly that: it remembers for you the last position on the page that you were reading and the next time you open the page, it goes directly to that position, without you having to take any action.
We will first show how to test this feature and then will explain how this Javascript application was built.
We are used to personalized experiences on the Web, mostly related to websites such as Facebook, Google, Amazon, etc. However, in all these cases we need to create an account with the service and login every now and then. The solution that we showed is different from a server-based approach in that it doesn't require us to create an account, and no login is necessary. No data is stored on the server. This is because the example uses the local storage of your browser to store and retrieve the information.
AdvantageThis approach is faster and preserves your privacy.
DisadvantageThis approach is device-bound. If you open the book page on another device (phone, tablet, other computer, etc.) you don't have access to your history. Only a server-based solution can maintain cross-device states.
Let us start explaining this example by quickly showing the HTML and CSS that is relevant to us. You can follow along to try to replicate this example.
In the HTML page, there will be two containers aside
and
main
. In the latter container will be the long text of the
book, we are not interested in it (in fact we copied that from the Guttenberg
page). The aside
block contains everything we need for our example.
Below, we show the HTML and CSS for the element aside
.
<aside> <span>Personalize Page</span> <div id="getInfo"> <input placeholder="your name" size=20> <button>Save</button> </div> <div id="lastVisit"> </div> </aside>
aside { position: fixed; top: 5px; right: 5px; width: 200px; cursor: pointer; /* more styling follows */ } #getInfo{ display: none; }
The span element has the text "Personalize Page", which we will see as long as
we haven't undertaken steps to do the personalization. The element
<div id="getInfo">
that contains the input text field
and the button is made invisible by setting in the CSS file the property
display: none;
. Notice also that the aside
element
has a fixed position on the top right corner of the page and we are changing the
appearance of the cursor to pointer to give feedback that the box can
be clicked.
There is a second div element, <div id="lastVisit">
,
which doesn't have any content for the moment and will be dynamically updated
with Javascript.
If you review the screenshots shown at the top, you will notice that
when we visit the page, there are two states in which the aside
element can be:
aside
box
displays it together with the datetime of last visit (Figure 2.).
Additionally, the page
is scrolled down to the last remembered position.This suggests that by storing the name of the user in the local storage
and checking when the page is loaded whether
the key username
has a value or not, we can decide in which of the
two states the element should be.
In fact, if you look at the Javascript code, the event handler for the
onload
event has exactly an if-else block to achieve this,
as shown below:
function onDocumentReady() { if (localStorage.getItem("username") != null){ // some code here to show the welcome user message and scroll to last position } else { // some code here that binds the two clicking events (for box and button) to // their event handler functions. }
If you declare a variable, but never assign it a value, trying to access
such value will give you back undefined
, which is a special Javascript
datatype (similar to booleans or numbers).
If you have never stored a pair of key/values in the localStorage
,
this means that neither the key nor the value exist, and in this case, trying to
access such a key will return the value null
. This is why we are
checking that (localStorage.getItem("username") != null)
in the code snippet above.
Scenario 1:Suppose we are in the situation of Figure 1. (the user has not yet personalized the page). What we want to happen here is:
onclick
event on the box.localStorage
, but that also updates the box to show
the welcome message (Figure 1.b) and make the button disappear.Scenario 2:Suppose we are in the situation of Figure
2. (the user has personalized the page). In this case, we don't
want the user to be performing any action, everything should happen automatically
when the HTML page is loaded (the window.onload
event):
The information for these three steps was stored previously in the
localStorage
. Here is a screenshot that shows the key/pair
values:
We explained what will happen in the different scenarios, and the only thing we didn't mention is in which moment the key/value pairs for last visit and last scroll position are stored in the storage. We will discuss that in the next section.
If you look at the HTML page for our example, you will not find there any
references to Javascript functions. The only thing you'll see is the tag:
<script src="storageEx.js"> </script>
that indicates
where our Javascript code is stored. As we'll discuss this again this semester,
we are using one of the cornerstone of modularity, separation of concerns
to keep HTML and Javascript separate, so that different developers can work on
different parts of a website.
There are two kinds of events that happen in this example:
Below we show the Javascript statements that will bind to every of these four events the event handler function that will deal with it.
// 1.1 User clicks on the yellow box (aside element) var aside = document.querySelector("aside"); aside.onclick = processAsideClick; // 1.2 User clicks on the "Save" button var button = document.querySelector("aside button"); button.onclick = processButtonClick; // 2.1 The page is loaded window.onload = onDocumentReady; // 2.2 The page is about to be unloaded window.onunload = onLeavingPage;
As always, remember that when we bind an event to the event handler, we don't
use the ( )
because that would mean invoking the function,
but that is not what we want to do at this moment. We are only saying that
once such an event is triggered (fired), we want the broswer to go and
execute the function with this name.
localStorage
as well as using helper functions. The code is
heavily commented to make it easy for you to understand it.