visit
Today, while browsing a list of web APIs over at MDN, I ran across one that surprised me - the . This is clearly marked as experimental (currently only supported in Chrome/Edge) but looks to be fascinating.
However, sometimes you need to work with cookies, and this looks like a really nice new way of dealing with them. Here's a quick look.
For the past few hundred years or so, working with cookies in the browser involved string parsing. There really wasn't even an "API." You would read document.cookie
which returned a delimited string of cookie values:
'_ga=GA1.1.277504870.1615419234; ezux_ifep_238929=true; ezouspvh=1900; __qca=I0-1813967; ezouspva=0; ezouspvv=0; ezux_et_238929=10099; ezux_tos_238929=5877205; _ALGOLIA=anonymous-c45533a4-0752-4b4d-90cb-b641b642cf4f; _ga_T5V3C8M5RY=GS1.1.1669062149.711.1.1669064843.0.0.0; hitCounter=0'
Even odder, while the value of document.cookie
is a list of all cookies, you can use:
document.cookie = 'name=val';
document.cookie =
"doSomethingOnlyOnce=true; expires=Fri, 31 Dec 9999 23:59:59 GMT; SameSite=None; Secure";
To check for support, you can look for the cookieStorage
object:
if(!("cookieStore" in window)) {
console.log('Not supported');
return;
}
To get a cookie, you use get
. Fancy, right? It does support optionally checking for a URL scope, typically used on larger sites to restrict a cookie to be available on only a portion of it. Here's the simplest example:
let hitCounter = await cookieStore.get('hitCounter');
As mentioned before, the API is asynchronous, so you can either await
it or use then
. I prefer await
.
If the cookie doesn't exist, you'll get a null
response; otherwise, an object like so:
{
"domain": null,
"expires": null,
"name": "hitCounter",
"path": "/",
"sameSite": "strict",
"secure": true,
"value": "7"
}
cookieStore.set('hitCounter', 9);
This will also be asynchronous, so either await
or be sure to use then
. Note that even though I pass a number, it will become a string when stored. If you want to set options, you use the form:
const tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);
cookieStore.set({
name:'hitCounter',
value:9,
expires:tomorrow
});
In this example, I simply specified a 24-hour expiration date as an option, but there are other options as well (check the set
for a list).
cookieStore.delete('nameOfCookie');
There's also an option like get
where you can pass an object containing additional options. A good example here would be path
, and again, think of the large website needing to ensure a cookie named foo
in a path doesn't interfere with one in another path.
The final method I'll show is the getAll
method which returns an array of cookies. Running cookieStore.getAll().then(console.log)
on my blog returns:
cookieStore.onchange = (event) => {
console.log('cookie change event', event);
};
type
will tell you the type of change, being either deleted
or changed
.
changed
is an array of cookies that have been changed.
deleted
is an array of cookies that have been deleted.
The docs for the event don't specify why changed
and deleted
are an array, but if I had to guess, due to the async nature of the API, it's possible to change/delete N cookies before the event can fire in response; hence needing the results in an array.
While - in general - I would not recommend using cookies for new projects, I did whip an incredibly simple demo that keeps track of the number of times you've visited a site. It does this using a hitCounter
cookie. First, the HTML:
<h2>Cookie Store Demo 1</h2>
<p>
You have been to this page <span id="hitCount"></span> time(s).
</p>
document.addEventListener('DOMContentLoaded', init, false);
async function init() {
if(!("cookieStore" in window)) {
console.log('Not supported');
return;
}
cookieStore.onchange = (event) => {
console.log('cookie change event', event);
};
let hitCounter = await cookieStore.get('hitCounter');
if(!hitCounter) {
hitCounter = { value: 0 }
}
hitCounter.value = parseInt(hitCounter.value,10)+1;
try {
await cookieStore.set('hitCounter', hitCounter.value);
/*
const tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);
cookieStore.set({
name:'hitCounter',
value:hitCounter.value,
expires:tomorrow
});
*/
} catch(e) {
console.error(e);
}
document.querySelector('#hitCount').innerText = hitCounter.value;
}
Basically, read the cookie, and if it's null, default to 0. Then add one to it after parsing it to an integer, and store the value. In theory, I don't have to await
; I could just fire and forget and immediately update the DOM, but you get the idea.
Before moving on, a quick note about the try/catch
. The browser may block writes to cookies, and if so, you will get an error:
Error: An unknown error occurred while writing the cookie.