What is the shortest function for reading a cookie by name in JavaScript?

247,044

Solution 1

Shorter, more reliable and more performant than the current best-voted answer:

const getCookieValue = (name) => (
  document.cookie.match('(^|;)\\s*' + name + '\\s*=\\s*([^;]+)')?.pop() || ''
)

A performance comparison of various approaches is shown here:

https://jsben.ch/AhMN6

Some notes on approach:

The regex approach is not only the fastest in most browsers, it yields the shortest function as well. Additionally it should be pointed out that according to the official spec (RFC 2109), the space after the semicolon which separates cookies in the document.cookie is optional and an argument could be made that it should not be relied upon. Additionally, whitespace is allowed before and after the equals sign (=) and an argument could be made that this potential whitespace should be factored into any reliable document.cookie parser. The regex above accounts for both of the above whitespace conditions.

Solution 2

This will only ever hit document.cookie ONE time. Every subsequent request will be instant.

(function(){
    var cookies;

    function readCookie(name,c,C,i){
        if(cookies){ return cookies[name]; }

        c = document.cookie.split('; ');
        cookies = {};

        for(i=c.length-1; i>=0; i--){
           C = c[i].split('=');
           cookies[C[0]] = C[1];
        }

        return cookies[name];
    }

    window.readCookie = readCookie; // or expose it however you want
})();

I'm afraid there really isn't a faster way than this general logic unless you're free to use .forEach which is browser dependent (even then you're not saving that much)

Your own example slightly compressed to 120 bytes:

function read_cookie(k,r){return(r=RegExp('(^|; )'+encodeURIComponent(k)+'=([^;]*)').exec(document.cookie))?r[2]:null;}

You can get it to 110 bytes if you make it a 1-letter function name, 90 bytes if you drop the encodeURIComponent.

I've gotten it down to 73 bytes, but to be fair it's 82 bytes when named readCookie and 102 bytes when then adding encodeURIComponent:

function C(k){return(document.cookie.match('(^|; )'+k+'=([^;]*)')||0)[2]}

Solution 3

Assumptions

Based on the question, I believe some assumptions / requirements for this function include:

  • It will be used as a library function, and so meant to be dropped into any codebase;
  • As such, it will need to work in many different environments, i.e. work with legacy JS code, CMSes of various levels of quality, etc.;
  • To inter-operate with code written by other people and/or code that you do not control, the function should not make any assumptions on how cookie names or values are encoded. Calling the function with a string "foo:bar[0]" should return a cookie (literally) named "foo:bar[0]";
  • New cookies may be written and/or existing cookies modified at any point during lifetime of the page.

Under these assumptions, it's clear that encodeURIComponent / decodeURIComponent should not be used; doing so assumes that the code that set the cookie also encoded it using these functions.

The regular expression approach gets problematic if the cookie name can contain special characters. jQuery.cookie works around this issue by encoding the cookie name (actually both name and value) when storing a cookie, and decoding the name when retrieving a cookie. A regular expression solution is below.

Unless you're only reading cookies you control completely, it would also be advisable to read cookies from document.cookie directly and not cache the results, since there is no way to know if the cache is invalid without reading document.cookie again.

(While accessing and parsing document.cookies will be slightly slower than using a cache, it would not be as slow as reading other parts of the DOM, since cookies do not play a role in the DOM / render trees.)


Loop-based function

Here goes the Code Golf answer, based on PPK's (loop-based) function:

function readCookie(name) {
    name += '=';
    for (var ca = document.cookie.split(/;\s*/), i = ca.length - 1; i >= 0; i--)
        if (!ca[i].indexOf(name))
            return ca[i].replace(name, '');
}

which when minified, comes to 128 characters (not counting the function name):

function readCookie(n){n+='=';for(var a=document.cookie.split(/;\s*/),i=a.length-1;i>=0;i--)if(!a[i].indexOf(n))return a[i].replace(n,'');}

Regular expression-based function

Update: If you really want a regular expression solution:

function readCookie(name) {
    return (name = new RegExp('(?:^|;\\s*)' + ('' + name).replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&') + '=([^;]*)').exec(document.cookie)) && name[1];
}

This escapes any special characters in the cookie name before constructing the RegExp object. Minified, this comes to 134 characters (not counting the function name):

function readCookie(n){return(n=new RegExp('(?:^|;\\s*)'+(''+n).replace(/[-[\]{}()*+?.,\\^$|#\s]/g,'\\$&')+'=([^;]*)').exec(document.cookie))&&n[1];}

As Rudu and cwolves have pointed out in the comments, the regular-expression-escaping regex can be shortened by a few characters. I think it would be good to keep the escaping regex consistent (you may be using it elsewhere), but their suggestions are worth considering.


Notes

Both of these functions won't handle null or undefined, i.e. if there is a cookie named "null", readCookie(null) will return its value. If you need to handle this case, adapt the code accordingly.

Solution 4

code from google analytics ga.js

function c(a){
    var d=[],
        e=document.cookie.split(";");
    a=RegExp("^\\s*"+a+"=\\s*(.*?)\\s*$");
    for(var b=0;b<e.length;b++){
        var f=e[b].match(a);
        f&&d.push(f[1])
    }
    return d
}

Solution 5

How about this one?

function getCookie(k){var v=document.cookie.match('(^|;) ?'+k+'=([^;]*)(;|$)');return v?v[2]:null}

Counted 89 bytes without the function name.

Share:
247,044

Related videos on Youtube

Yahel
Author by

Yahel

Updated on May 10, 2022

Comments

  • Yahel
    Yahel almost 2 years

    What is the shortest, accurate, and cross-browser compatible method for reading a cookie in JavaScript?

    Very often, while building stand-alone scripts (where I can't have any outside dependencies), I find myself adding a function for reading cookies, and usually fall-back on the QuirksMode.org readCookie() method (280 bytes, 216 minified.)

    function readCookie(name) {
        var nameEQ = name + "=";
        var ca = document.cookie.split(';');
        for(var i=0;i < ca.length;i++) {
            var c = ca[i];
            while (c.charAt(0)==' ') c = c.substring(1,c.length);
            if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
        }
        return null;
    }
    

    It does the job, but its ugly, and adds quite a bit of bloat each time.

    The method that jQuery.cookie uses something like this (modified, 165 bytes, 125 minified):

    function read_cookie(key)
    {
        var result;
        return (result = new RegExp('(?:^|; )' + encodeURIComponent(key) + '=([^;]*)').exec(document.cookie)) ? (result[1]) : null;
    }
    

    Note this is not a 'Code Golf' competition: I'm legitimately interested in reducing the size of my readCookie function, and in ensuring the solution I have is valid.

    • mVChr
      mVChr about 13 years
      Stored cookie data is kind of ugly, so any method to handle them will probably be as well.
    • Yahel
      Yahel about 13 years
      @mVChr seriously. At what point was it decided that cookies should be accessed from a semi-colon delimited string? When was that ever a good idea?
    • None
      None almost 13 years
      Why is this question still open and why does it have a bounty? Are you really that desperate to save maybe 5 bytes???
    • vladimir.gorea
      vladimir.gorea over 4 years
      cookieArr = document.cookie.split(';').map(ck=>{return {[ck.split('=')[0].trim()]:ck.split('=')[1]}})
  • xavierm02
    xavierm02 almost 13 years
    Scopes are only create when entering a function. Therefore, you could gather your two var declaration.
  • None
    None almost 13 years
    @xavierm02 - What? You're talking about the read_cookie(k,r) piece, I'm assuming, but not sure what your commennt it :) The point of it is to define r as undefined and thus save the few bytes from typing var r
  • Félix Saparelli
    Félix Saparelli almost 13 years
    Nope, he's talking about defining C twice in the 1st function. However, you can gather all three declarations, not just two. You can do that: var c = document.cookie.split('; '), C, i=c.length; (defining the loop thus for(;i>0;i--)), and therefore removing all other var statements from the readCookie() function.
  • None
    None almost 13 years
    oh, I wasn't even paying attention to the first function :)
  • xavierm02
    xavierm02 almost 13 years
    The var C in the for loop is useless (C is already declares in this function scope) and you could take the i you use in the for loop and declare it with c and C a bit before in the code.
  • xavierm02
    xavierm02 almost 13 years
    And you should use --i insteand of i--. If I remember well, it's a bit faster.
  • None
    None almost 13 years
    @xavierm02 - FYI I just tested that theory (I remember it as well) in Chrome & FF and neither show any speed difference over 1,000,000,000 calls to x-- vs --x. (10,000,000 iteration loop with 100 -- calls inside to minimize the impact of the loop itself)
  • xavierm02
    xavierm02 almost 13 years
    Hm... In some implementations, it's faster. Because ++i returns the actual value that the variable has whereas i++ returns the old value, which means the two values coexist. But I can't remember in what browser it was. I don't even know if it was in JavaScript...
  • xavierm02
    xavierm02 almost 13 years
    Here you go : jsperf.com/pre-increment-vs-post-increment I was right, ++i is faster, at least if you get the value before and after. And that's what you do in a for loop.
  • None
    None almost 13 years
    @xavierm02 - I'm using the dev version of chrome, which is probably why I didn't see a difference. post -increment is actually repeatedly 1% faster (with a 1.36% margin of error) for me than pre-increment according to that test vs the 27% slower that it shows for Chrome 11. Firefox 3.6 and 4 also show that they're dead-even. That being said, thanks for the test, it's nice to know that many browsers do run pre-increment faster so all things being equal I'll probably start doing that more often.
  • Kevin M
    Kevin M over 10 years
    This function does not return the full value of the cookie if it contains an equals sign within the cookie value. You can change this by using substring to remove the cookie name instead of relying on the split cookies[C[0]] = c[i].substring(C[0].length + 1);
  • T.J. Crowder
    T.J. Crowder over 10 years
    An argument called c, and an argument called C. Fail. That's just a shockingly bad idea.
  • Mac
    Mac over 9 years
    I've just noticed that in Firefox, the regex approach I posted above is not as performant as the looping approach. The tests I'd run previously were done in Chrome, where the regex approach performed modestly better than other approaches. Nonetheless, it's still the shortest which addresses the question being asked.
  • Andreas
    Andreas about 9 years
    One important thing: Since the value of "cookies" is cached, new cookies or changed cookies from other windows or tabs will not become visible. You can avoid this issue by storing the string-value from document.cookie in a variable and check if it is unchanged on each access.
  • Fagner Brack
    Fagner Brack almost 9 years
    @zyklus "Who tried to store high ascii cookie names?"/"Who uses double bytes ANYWHERE in JavaScript?" Anyone who lives outside USA. USA does not represent the whole world. "Minimizing code has use cases", of course, but if you care about script size, then it means your use case requires a more robust code that works for most inputs. You don't need to care that much about code size if you are not developing open-source or at scale. My intent is not to sound elitist, it is just to help for posterity, that is why comments exists, not to fight against better alternatives.
  • None
    None almost 9 years
    Fair enough, though I would argue that you shouldn't be using unicode in JS regardless as it needlessly doubles your file sizes. My shortened solutions weren't meant to be robust, I even acknowledged that they wouldn't fully work in the original answer. It was more of a challenge to see just how small I could get the code while still being functional in almost all use cases.
  • electroid
    electroid over 8 years
    I changed last line return d[0]; and then I used if (c('EXAMPLE_CK') == null) to check if cookie is not defined.
  • Brent Washburne
    Brent Washburne over 8 years
    Why does getCookieValue(a, b) take parameter b?
  • instead
    instead over 8 years
    @BrentWashburne instead of writing var b =... you have ,b in arguments and b= inside function. You can save 1 byte :D "var b" (4 bytes) ",bb" (3 bytes)
  • Brent Washburne
    Brent Washburne over 8 years
    Aside from the +1 in code golf, does that make the code any better?
  • Gigi
    Gigi about 8 years
    Upvoted, but not for readability... took me a while to figure out what a and b do.
  • The Muffin Man
    The Muffin Man almost 8 years
    Clever, but silly to write it that way to save 1 byte.
  • Stijn de Witt
    Stijn de Witt over 7 years
    The question is literally asking for the shortest solution, so this should be the accepted answer imho. I'm looking for a short code snippet to read one cookie, but the accepted answer gives me a script to populate a global variable (which I have to keep track of from then on) and I still need to read from that global, make sure it's initialized etc. So the accepted answer is answering another question: How to read cookies as efficiently as possible.
  • Vitim.us
    Vitim.us over 7 years
    a parameter is not regex escaped, while it can be useful, it is not safe. Things likegetCookieValue('.*') will return any random cookie
  • dvdmn
    dvdmn almost 7 years
    well it matters if you are working on a site with 5000 average active users.. every byte counts
  • Stijn de Witt
    Stijn de Witt almost 7 years
    For anyone saying it's silly to save a byte.. look at what Google is doing with it's search page. It's insane the kind of optimizations they put in there. Multiplied by a billion, a single byte is a lot! (of course, the website I'm working on will get just as much traffic! :p )
  • instead
    instead almost 7 years
    Why did You replaced b from function parameter with var b inside function?
  • Jim Pedid
    Jim Pedid over 6 years
    Create the regexp once and use it over and over for better performance.
  • vladimir.gorea
    vladimir.gorea over 4 years
    what about cookieArray = document.cookie.split(';').map(ck=>{return {[ck.split('=')[0].trim()]:ck.split('=')[1]}})
  • Matt
    Matt almost 4 years
    I have no idea what this thing is doing, but it works.
  • bsmaniotto
    bsmaniotto over 3 years
    Should it return an empty string when there are no matches? For me undefined is a more intuitive return in this case, any cons?
  • vanowm
    vanowm over 3 years
    You can save 5 bytes by adding b into function arguments, return b[2] instead of b.pop() and combine everything into return statement: function getCookieValue(a,b){return(b=document.cookie.match('(^|;)\\s‌​*'+a+'\\s*=\\s*([^;]‌​+)'))?b[2]:''}
  • vanowm
    vanowm over 3 years
    Is COOKIE_NAME a string or variable? Your example doesn't make sense...
  • ruffin
    ruffin over 3 years
    @vanowm He knows. It was an approved edit by another user, though very - painfully - done. Please read the existing comments before repeating what's already been said. Btw, Matt, you disappoint me. Don't use it unless you understand it! (and if you don't understand, ask!) ;^D
  • vanowm
    vanowm over 3 years
    @ruffin did you read my entire comment or you stopped at first comma? Perhaps read it through next time you comment. The only reason I "repeated" it, is to explain the changes in the proposed code below. And since the original question is about "shortest function", readability is not required
  • ruffin
    ruffin over 3 years
    @vanowm You missed my point; well done. Look at the question's history. I wasn't arguing that change's merits about shortness; I was saying Mac agreed with you and the community edited that out. That is, the original answer used getCookieValue(a,b) and was shorter. As of today, Mac hasn't been on SO since 2016. If you think it's a useful edit, don't make a comment rehashing what's already been discussed (see links from my earlier comment); suggest/make an reversion. ◔_◔
  • vanowm
    vanowm over 3 years
    @ruffin, No, seriously, did you read my entire comment after the comma or not? because you sure sound like you didn't. Who cares about b as parameter, yes, it was in the original code, woopeedoo! that's not the main point, there are other changes that not only shorten the code, but also improve performance.I see no point continue this discussion.
  • ruffin
    ruffin over 3 years
    @vanowm Good day, sir! ;^D
  • ErikE
    ErikE about 3 years
    If there ARE spaces before the next ; delimiting the next cookie value, those spaces will be included as part of the returned value.
  • ErikE
    ErikE about 3 years
    RFC 2109 has been replaced TWICE. It's not current. See stackoverflow.com/questions/1969232/… for a more thorough and up-to-date discussion on characters allowed in cookies.
  • Kevin Seymour
    Kevin Seymour about 2 years
    If you are planning on using this code in IE you will need to modify it: the ?.pop() bit is called optional chaining and is not supported.