Jump to main content
Menu

document Object, Cookies, and localStorage

document Object Properties and Methods

  • The document object is the web page loaded in the browser window.
  • The list of properties and methods provided in this article is not the full list.
  • There are also properties related to setting link colors, background color, and text color, but CSS should be handling that. The existence of those properties simply reflects the age of the Browser Object Model, as it predated the widespread usage of CSS.
Method / Property Description Code Example
cookie Writing to this property creates / updates a text file on the user's computer.

Cookie data is always a string.

alert(document.cookie);
domain This property allows pages from different subdomains (e.g., examples.learningtocode.info and test.learningtocode.info) to communicate.

For this to work the domain specified needs to be the domain to which the subdomain belongs.

The other window can then be accessed by its name property (part of the window object).

document.domain = 'learningtocode.info';
lastModified A string object with date / time details.

Data comes from the headers sent by the server (the headers belong to the HTTP packets).

If the headers did not contain this information, browsers may output the current date or January 1, 1970.

alert(document.lastModified);
open() / close() Calling document.open() destroys the current page and creates a new one, then document.write() is used to put some content into the new page, and document.close() is called to close off the new page.

This approach is very old and is not advised.

document.open();
document.close();
referrer A string object containing the URL of the referring page (the page that linked to the current page).

This also comes from the HTTP headers and can be spoofed (faked), so do not trust this data.

This will be empty if the user came in via a bookmark, typed in the URL manually, or loaded the document directly into the browser. Links in HTML emails seldom pass a referrer.

alert(document.referrer);
title A read/write property reflecting the <title> of the page. alert(document.title);
write() Obsolete approach to outputting data to the browser; replaced by DOM methods.

Only works properly if it executes while the page is loading; calling after the 'load' event fires for window will wipe out the entire page and only show the content you are outputting via this method.

document.write('some text');

Cookies Overview

  • Cookies are small (no larger than 4 kilobytes) text files stored on the user's hard drive.
  • JavaScript is used to place, read, and delete cookies.
  • Cookies will also expire on their own and be automatically deleted.
  • Server-side programming languages also use cookies (the same ones your JavaScript is using); the cookie data is sent with the HTTP headers requesting the web page files.
  • Cookies are not executables (programs). They just store data.
  • One risk with cookies is an irresponsible web developer storing sensitive information (such as the credit card number you entered) in a cookie on your computer.
    • The potential negative consequences of this are limited because only the site (the domain) creating the cookie can read it.
    • However, if the user's computer has been hacked the unauthorized intruder could read the text file on the hard drive.
    • Or if the website setting the cookie is loading other scripts (from additional domains), those other scripts could also read the cookie.
  • Usually the cookie just holds a user identifier and that is matched to a server-side database record holding the other user data (stored securely on the server).
  • If sessions are in use, a cookie usually holds the session identifier for that particular user. Session data is stored server-side (usually in text files on the server), but the identifier for that session record is stored client-side and is passed via the HTTP headers to the server.
  • Other uses of cookies are to allow websites to store client-side user preferences (such as a light or dark theme selection for the website), to temporarily store form data, etc.
  • Each browser stores its own cookies; they do not share or overlap in any manner.
  • Chrome refuses to set cookies when the file:// protocol is used (you need to be using http:// or https://), so be sure to be testing on a web server or a local environment with a web server integrated. Edge and Safari may work the same way, given their shared code base with Chrome.

Limitations of Cookies

  • File Size / Capacity
    • The limit of 4 kilobytes per cookie means that you will not be storing volumes of text in each one.
  • Number of Cookies Per Domain
    • According to the cookie specification a given domain can store at least 20 cookies.
    • Modern browsers generally have no limit, although HTTP header size limits place an upper bound on how much can be transmitted.
    • Usually just one cookie or a few cookies are sufficient.
  • Total Number of Cookies Per Browser (Across All Domains)
    • The specification indicates at least 300 cookies.
    • Modern browsers generally have no limit except hard drive space.
  • Users Can Disable or Block
    • Over time browsers have allowed more precise user control over cookies.
    • Users can choose to accept or reject cookies from certain domains, accept or reject all cookies, or prompt the user (to accept or reject) each time a cookie is set.
  • Only Readable by Specified Domain and Directory
    • We set these parameters or allow them to default to the current domain and directory.
    • While technically a limitation, this is a valuable protection against malicious website operators who want to sell (or otherwise use) your personal information.
    • If they were able to read all the cookies on your computer, they would likely find something they could use (such as resuming a non-expired session for another website).
    • Cross Site Scripting (XSS) attacks involve getting your website to load JavaScript from another website, which then can access all the cookies for the compromised site.

Testing Cookies Cross-Browser / Cross-Platform

  • The browser's Inspector / Developer Tools (pressing F12 within the browser should launch these tools) will have a tab called Storage or Application (or something similar). Inside that tab you can see the cookie content and edit the content.
  • The Web Developer Toolbar (a browser extension) has a 'Cookies' menu that allows you to see and edit cookie details as well (if you want an alternative to the browser's built-in tools).
  • Note that these tools might only track cookies once you are on a server or testing on localhost (they may need the protocol to be http or https). The Web Developer Toolbar will probably not display cookies set using the file:// protocol.
  • To test in Chrome you need to be on the http:// or https:// protocol; cookies will not be saved when testing locally (on the file:// protocol).
  • Be careful when specifying the 'secure' flag on a cookie, because if you are using http (not https) then the cookie will not be set. You can spend quite a while scratching your head in that situation before realizing what is going wrong.

Setting a Cookie

  • name
    • Required
    • This is a unique name for the cookie; it must not contain whitespace or semicolons.
  • value
    • Required
    • This is the value we are assigning to the cookie.
    • Content is limited to a string or number; nothing more complicated.
  • samesite
    • Optional
    • This is the latest addition to the cookie specification.
    • It was added to prevent situations where you are calling scripts from other domains (which are perhaps setting their own third-party cookies) and they were reading cookies set specifically for your domain (called first-party cookies).
    • For example, lets say that advertisements are loading from a third-party service, and they are loading JavaScript from that service that is setting third-party cookies (tracking cookies) for Doubleclick.net. That service's code could also be reading the cookies set specifically for your website by your code, and sending off that data without your (or the user's) permission. SameSite was designed to stop that theft of first-party cookie data from happening.
    • There are also other scenarios where this is important. For example, if you're embedding part of your site into another site via an iframe and you need to load cookies from your site as part of the code in that iframe, then it is vital to set SameSite properly.
    • Unfortunately, evolving implementations over time have made this somewhat of a mess.
    • The original approach defaulted this to None, which meant that access to first-party cookies was never restricted. The wrinkle here is that the specification also indicated that these needed to be set as Secure, which meant that the cookie was only sent over https. Many websites have not been updated for SameSite and they are running on http, so this is causing breakage in some browsers.
    • The current specification calls for SameSite to default to Lax, which means that first-party cookies are only sent cross-domain when you navigate to the other site. This change in the default was to improve security against some cookie-based exploits. Unfortunately, not all browsers have updated to the current specification.
    • The other value is Strict, which means that the first-party cookies are only available once the user is on that website (and not sent when navigating to that site). This is helpful for cases like cross-domain functionality that requires a login, because getting to the login page is the navigation step, and after you've authenticated you're already on the other site so the first-party cookies are available.
  • expiration date
    • Optional
    • When this date / time is reached the browser deletes the cookie automatically.
    • Always expressed in GMT (Greenwich Mean Time).
    • Can be determined by converting the local time (from the user's computer) into GMT using the toGMTString() JavaScript Date() method.
    • If omitted the value false is entered in the cookie and the cookie is deleted when the browser is completely shut down (all tabs are closed).
  • path information
    • Optional
    • Indicates what directory (and its subdirectories) can retrieve the cookie data.
    • If not explicitly set this defaults to the current directory.
    • Directories above this directory will not have access to the data.
    • For the greatest flexibility set this to '/' or on a shared server limit this to your directory (e.g., '/~username/').
  • domain information
    • Optional
    • Indicates what domain can retrieve the cookie data.
    • If not explicitly set this defaults to the domain of the page creating the cookie.
    • When testing locally this would be localhost.
    • To allow subdomains (e.g., classes.learningtocode.info, www.learningtocode.info, examples.learningtocode.info) start the value with ., such as '.learningtocode.info'
  • secure transmission
    • Optional
    • Specifies that the server use https before the cookie is sent.
    • If omitted the value false in stored in the cookie.
    • Turning this on means that secure is specified.
  • A fully specified cookie is:
    document.cookie = 'username=jwith; SameSite=Lax; expires=Mon, 28-Sep-26 00:00:01 GMT; path=/; domain=learningtocode.info; secure';
    
  • Given the above code the cookie would be called username, which has a value of jwith.
  • The cookie expires September 28, 2026, and can only be accessed via secure protocol (https) and be retrieved from pages in or below learningtocode.info/. The SameSite restrictions are lax.

Example #1: Setting a Cookie

Structure (setting_cookie.html)

<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
  <title>Setting Cookie Example</title>
  <meta charset="utf-8" />
</head>
<body>

<script src="setting_cookie.js"></script>
</body>
</html>

Behavior (setting_cookie.js)

"use strict";

const setCookie = {

  init : function() {
  
    // create a new Date() object
    const expiration = new Date();
  
    // determine the expiration date two days in the future
    const twoDays = expiration.getTime() + (2 * 24 * 60 * 60 * 1000);  
 
    // assign the two day expiration value to the Date() object
    expiration.setTime(twoDays);

    // write the cookie
    document.cookie = 'username=jwith; SameSite=Lax; expires=' + expiration.toGMTString() + '; secure';
 
  }

}

setCookie.init();

// show the cookie value
alert(document.cookie);

See how this first cookies example renders (make sure your browser is set to accept cookies)

Considerations for Example #1

  • We began by creating a new Date() object and associating that with the expiration variable.
  • The getTime() method determines the number of milliseconds since January 1, 1970 (up to the current moment) and adds 2 additional days worth of milliseconds, which ultimately becomes the expiration value by using the JavaScript setTime() method.
  • That number is converted to GMT using the JavaScript toGMTString() method.

Example #2: Setting More Cookie Parameters

  • For this second example we open up the possibility that all seven parameters can be passed (the two required and the five that are optional or any combination thereof), which then creates a cookie with the indicated value(s).
  • The code is written in object literal format.
createCookie : function(name,value,samesite,expiration,path,domain,secure) {

  let data = name + "=" + escape(value);

  if (samesite) { data += "; SameSite=" + samesite; }

  if (expiration) {
    let expiresAt = new Date();
    expiresAt.setTime(expiration);
    data += "; expires=" + expiresAt.toGMTString();
  }

  if (path) { data += "; path=" + path; }

  if (domain) { data += "; domain=" + domain; }

  if (secure) { data += "; secure"; }

  document.cookie = data;

}

Considerations for Example #2

  • The escape() JavaScript method converts non-alphanumeric characters (such as empty spaces) into their hexadecimal equivalents, which in the case of white spaces is %20.
  • Since values are likely to involve multiple words, this is very important.
  • Later we will use the unescape() JavaScript method to convert hexadecimals back into their regular text version (so that we are not seeing a bunch of %20's in the output).

Reading a Cookie & Retrieving the Cookie Value

  • This process involves two methods / functions.
  • The findCookie() method searches through the available cookies that have been set for the current domain in an effort to locate the one matching the indicated cookie name.
  • If findCookie() finds a match, it sends to the findCookieValue() method the starting position for the cookie's value in the long string of data; this is a number object.
  • The findCookieValue() method starts from there, determines how long the value is and what it contains, and returns that information (the value of the cookie).

Locating the Desired Cookie

findCookie : function(name) {

  const query = name + "=";

  // length of the cookie name plus the = sign
  const queryLength = query.length;

  // total cookie length
  const cookieLength = document.cookie.length;

  let i=0;

  while (i<cookieLength) {

    // numerical position where we can try to find a match
    let position = i + queryLength;

    // extract the characters from the counter up to
    // the position and see if they match our query
    if (document.cookie.substring(i,position) === query) {

       // if we find a match send that position
       // to the findCookieValue method
       return someObject.findCookieValue(position);

    }

    // look for the next empty space to advance further
    // down the document.cookie string
    i = document.cookie.indexOf(" ", i) + 1;

    // if you cannot find anything via indexOf the result
    // is -1, so we added 1 to that, resulting in 0
    // if nothing else can be found you're done looking
    if (i === 0) { break; }

  }

  // if you did not find anything you will be out here
  // and returning null
  return null;

}

Exploring the Locating Cookie Code

  • This code is accomplishing three things:
    1. Determining if the cookie exists
    2. Finding the right cookie, out of all the cookies that have been set
    3. Isolating where the cookie's value begins, so that can be passed to the findCookieValue() method for extraction
  • The query variable is simply the name of the cookie that you passed, with the addition of the = sign.
  • We use the queryLength variable to determine the number of characters that are involved there, by examining the length property of query.
  • If you passed a name of user to the function, the value assigned to queryLength would be 5, since that is the length of user=.
  • The cookieLength is the total number of characters in the cookie being examined. Keep in mind that the only cookie(s) that can be examined are those that were placed by that website, so the number is probably pretty small.
  • The while loop searches through every combination of characters in the cookie, via the substring() method, starting from the beginning (the 0 position character) and advancing through the string with each loop until it reaches the final character in the cookies (it can be a really long string if multiple cookies have been placed).
  • Because it knows the number of characters in the query, it can make an exact match to the name being searched for, right up to where the cookie value begins (which is right after the = sign following the name of the cookie).
  • If a match was found that position in the string would be passed to findCookieValue()
  • If there are no matches (following loops through the entire cookie content) then the while loop terminates and a null value gets returned, which means there was no match anywhere in document.cookie for that cookie name.
  • In the code example, you need to adjust the path to findCookieValue() so that the function can be found.

Extracting the Cookie Value

findCookieValue : function(position) {

  let endsAt = document.cookie.indexOf(";", position);
  if (endsAt === -1) { endsAt = document.cookie.length; }
  return unescape(document.cookie.substring(position,endsAt));

}

Exploring the Cookie Value Extraction Code

  • The parameter passed to this method is the start point for the cookie's value.
  • The endsAt variable stores the number (the position) of the last character in the value.
  • The indexOf() method searches for the first semicolon following the position (and we know the position is the very beginning of the cookie's value) and returns the position of that semicolon.
  • If there is no semicolon then indexOf() will return a value of -1, which means that the cookie in question only has a name and value and is the last one in the sequence of cookies.
  • Since this means that the end of the value is the end of document.cookie, we set endsAt to be the length of the document.cookie string object.
  • The characters between the position (the number position in the string where the value begins) and endsAt (the number position in the string where the value ends) are sent back by the method, after being run through an unescape() which converts hexadecimal values back into regular text.

Deleting a Cookie

  • In this case we look for a cookie with the intention of having the browser delete it.
  • To do so we change its expires information.
  • What really matters is that the expiration date is set to a time in the past. The code uses January 1, 1970 but it could also have been a minute ago. The browser will automatically erase the targeted cookie.
  • In the code example, you need to adjust the path to findCookie() so that the function can be found.
eraseCookie : function(name) {

  if (someObject.findCookie(name)) {

     let data = name + "=";

     data += "; expires=Thu, 01-Jan-70 00:00:01 GMT";

     document.cookie = data;

  }

}

Complete Cookies Example

Structure of Starting Page (complete_cookies.html)

<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
  <title>Complete Cookies Example</title>
  <meta charset="utf-8" />
  <link rel="stylesheet" href="complete_cookies.css" />
</head>
<body>

<form method="post" action="#">

<h1>Deli Online</h1>

<ul>

  <li><label>Your Name:
     <input type="text" name="your_name" size="20" />
  </label></li>

  <li><label>Your Phone Number:
     <input type="tel" name="phone_num" size="20" />
  </label></li>

  <li><label>Your Address:
     <textarea name="where" rows="3" cols="20"></textarea>
  </label></li>

  <li><label><abbr title="Number">#</abbr> of Sandwiches:
     <input type="number" name="number" size="2" maxlength="2" />
  </label></li>

  <li>
     <input type="button" value="Place Order" id="placeOrder" />
  </li>

</ul>

</form>

<script src="complete_cookies.js"></script>
</body>
</html>

Behavior for Starting Page (complete_cookies.js)

"use strict";

const deliOrder = {

  // isolating the input button
  actionButton : document.getElementById('placeOrder'),
  
  // references to form elements
  allInputs : document.getElementsByTagName('input'),
  allTextAreas : document.getElementsByTagName('textarea'),

  // counts of form elements
  numberOfTextInputs : 0,
  numberOfTextAreas : 0,
  
  init : function() {

    // assigning totals
    this.numberOfTextInputs = this.allInputs.length;
    this.numberOfTextAreas = this.allTextAreas.length;

    // assigning the event handler to the button
    this.actionButton.addEventListener('click', this.generateCookies, false);

  },

  generateCookies : function() {

    // loop for text inputs
    for (let i=0; i<deliOrder.numberOfTextInputs; i++) {

      // skip the input button; it does not have a value to store in a cookie
      if (deliOrder.allInputs[i].type === 'button') { continue; }

      // store a cookie using the variable name as the cookie name
      deliOrder.createCookie(deliOrder.allInputs[i].name, deliOrder.allInputs[i].value, 'Lax', false, false, false, true);
    
    }

    // loop for textareas
    for (let i=0; i<deliOrder.numberOfTextAreas; i++) {

      // store a cookie using the variable name as the cookie name
      deliOrder.createCookie(deliOrder.allTextAreas[i].name, deliOrder.allTextAreas[i].value, 'Lax', false, false, false, true);

    }

    // load the confirmation page once the cookies are set
    location.href = "complete_cookies2.html";
  
  },

  createCookie : function(name,value,samesite,expiration,path,domain,secure) {

    let data = name + "=" + escape(value);
    if (samesite) { data += "; SameSite=" + samesite; }
    if (expiration) { 
      let expiresAt = new Date();
      expiresAt.setTime(expiration);
      data += "; expires=" + expiresAt.toGMTString();
    }
    if (path) { data += "; path=" + path; }
    if (domain) { data += "; domain=" + domain; }
    if (secure) { data += "; secure"; }
    document.cookie = data;

  }

}

deliOrder.init();

Structure of Second Page (complete_cookies2.html)

<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
  <title>Complete Cookies Example - Confirmation</title>
  <meta charset="utf-8" />
  <link rel="stylesheet" href="complete_cookies.css" />
</head>
<body>

<h1>Order Confirmation</h1>

<div id="dataArea"></div>

<script src="complete_cookies2.js"></script>
</body>
</html>

Behavior for Second Page (complete_cookies2.js)

"use strict";

const order2 = {

  // isolate the results area
  container : document.getElementById('dataArea'),

  init : function() {

    const sandwiches = order2.findCookie('number');
    const recipient = order2.findCookie('your_name');    
    const destination = order2.findCookie('where'); 
    const phone = order2.findCookie('phone_num');
    const total = parseInt(order2.findCookie('number'),10) * 11;

    let str = `<p>We will be delivering ${sandwiches} sandwiches to: ${recipient}</p>`;
    str += `<p>Delivery will be to: ${destination}</p>`;
    str += `<p>Your phone number is: ${phone}</p>`;
    str += `<p>The total is $${total}.00, plus an $8 delivery charge.</p>`;

    this.container.innerHTML = str;

  },

  findCookie : function(name) {  
    const query = name + "=";
    const queryLength = query.length;
    const cookieLength = document.cookie.length;
    let i=0;
    while (i<cookieLength) {
      let position = i + queryLength;
      if (document.cookie.substring(i,position) === query) {
         return this.findCookieValue(position);
      }
      i = document.cookie.indexOf(" ", i) + 1;
      if (i === 0) { break; }  
    }
    return null;  
  },

  findCookieValue : function(position) {
    let endsAt = document.cookie.indexOf(";", position);
    if (endsAt === -1) { endsAt = document.cookie.length; }
    return unescape(document.cookie.substring(position,endsAt));
  }

}

order2.init();

Presentation for Both Pages (complete_cookies.css)

body, h1, input, textarea {font: 75%/2.0 verdana, sans-serif;}
h1 {font-size: 130%; margin: 0;}
input, textarea {font-size: 100%; display: block; width: 200px;}
ul {list-style: none; margin: 0; padding: 0;}
li {margin: 15px 0;}

See how this complete cookies example renders (make sure your browser is set to accept cookies)

localStorage

  • Modern browsers have a built-in relational database (typically SQLite) that is accessed via a Storage object.
  • Similar data privacy protections remain in place for this database as we have for cookies, so that one website cannot read another website's records (and one subdomain cannot read another subdomain's records, even if they share the same domain).
  • The values stored are key/value pairs, but you can store as many as you want and they will persist until the user clears their browser storage.
  • When deciding between cookies and localStorage, you need to consider whether the server needs to access the data.
    • Cookies are automatically shared with server requests (assuming the cookie parameters and the website URL allow the sharing), but localStorage data is not shared.
    • If you wanted to pass localStorage data to the server you would need to have JavaScript access the data and pass the values via an XHR or Fetch call to the server.
  • The methods for accessing localStorage (parameters are all strings and need to be quoted) are:
    • localStorage.setItem(key, value) - sets the record value
    • const x = localStorage.getItem(key) - returns the value assigned to that key, so assign this to a variable
    • localStorage.removeItem(key) - removes the key and its value
    • localStorage.clear() - wipes all keys/values for that (sub)domain in that browser