document Object, Cookies, and localStorage
document Object Properties and Methods
- The
documentobject 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 |
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 |
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 thefile://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
iframeand you need to load cookies from your site as part of the code in thatiframe, 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 asSecure, 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
falseis 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
httpsbefore the cookie is sent. - If omitted the value
falsein stored in the cookie. - Turning this on means that
secureis 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 ofjwith. - The cookie expires
September 28, 2026, and can only be accessed via secure protocol (https) and be retrieved from pages in or belowlearningtocode.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 theexpirationvariable. - 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 theexpirationvalue by using the JavaScriptsetTime()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 thefindCookieValue()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:
- Determining if the cookie exists
- Finding the right cookie, out of all the cookies that have been set
- Isolating where the cookie's value begins, so that can be passed to the
findCookieValue()method for extraction
- The
queryvariable is simply the name of the cookie that you passed, with the addition of the=sign. - We use the
queryLengthvariable to determine the number of characters that are involved there, by examining thelengthproperty ofquery. - If you passed a name of
userto the function, the value assigned toqueryLengthwould be 5, since that is the length ofuser=. - The
cookieLengthis 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
whileloop searches through every combination of characters in the cookie, via thesubstring()method, starting from the beginning (the0position 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
namebeing 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
positionin the string would be passed tofindCookieValue() - If there are no matches (following loops through the entire cookie content) then the
whileloop terminates and anullvalue gets returned, which means there was no match anywhere indocument.cookiefor 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
endsAtvariable stores the number (the position) of the last character in the value. - The
indexOf()method searches for the first semicolon following theposition(and we know thepositionis 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 setendsAtto be thelengthof thedocument.cookiestring object. - The characters between the
position(the number position in the string where the value begins) andendsAt(the number position in the string where the value ends) are sent back by the method, after being run through anunescape()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
expiresinformation. - 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
Storageobject. - 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 valueconst x = localStorage.getItem(key)- returns the value assigned to that key, so assign this to a variablelocalStorage.removeItem(key)- removes the key and its valuelocalStorage.clear()- wipes all keys/values for that (sub)domain in that browser