Quelle est la fonction la plus courte pour lire un cookie par nom en JavaScript?

202

Quelle est la méthode la plus courte, précise et compatible avec tous les navigateurs pour lire un cookie en JavaScript?

Très souvent, lors de la création de scripts autonomes (où je ne peux pas avoir de dépendances extérieures), je me retrouve à ajouter une fonction de lecture des cookies, et généralement à recourir à lareadCookie() méthode QuirksMode.org (280 octets, 216 minifiés.)

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;
}

Il fait le travail, mais c'est moche, et ajoute un peu de ballonnement à chaque fois.

La méthode que jQuery.cookie utilise quelque chose comme ça (modifié, 165 octets, 125 minifié):

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

Notez que ce n'est pas une compétition de «Code Golf»: je suis légitimement intéressé à réduire la taille de ma fonction readCookie et à m'assurer que la solution que j'ai est valide.

Yahel
la source
8
Les données de cookies stockées sont un peu laides, donc toute méthode pour les gérer le sera probablement aussi.
mVChr
4
@mVChr sérieusement. À quel moment a-t-on décidé que les cookies devaient être accessibles à partir d'une chaîne délimitée par des points-virgules? Quand était-ce une bonne idée?
Yahel
8
Pourquoi cette question est-elle toujours ouverte et pourquoi a-t-elle une prime? Êtes-vous vraiment désespéré de sauver peut-être 5 octets ???
Mark Kahn
cookieArr = document.cookie.split (';'). map (ck => {return {[ck.split ('=') [0] .trim ()]: ck.split ('=') [1] }})
vladimir.gorea

Réponses:

216

Plus courte, plus fiable et plus performante que la réponse la mieux votée actuelle:

function getCookieValue(a) {
    var b = document.cookie.match('(^|;)\\s*' + a + '\\s*=\\s*([^;]+)');
    return b ? b.pop() : '';
}

Une comparaison des performances de diverses approches est présentée ici:

http://jsperf.com/get-cookie-value-regex-vs-array-functions

Quelques notes d'approche:

L'approche regex n'est pas seulement la plus rapide dans la plupart des navigateurs, elle offre également la fonction la plus courte. De plus, il convient de souligner que selon la spécification officielle (RFC 2109) , l'espace après le point-virgule qui sépare les cookies dans le document.cookie est facultatif et un argument pourrait être fait qu'il ne devrait pas être invoqué. De plus, les espaces blancs sont autorisés avant et après le signe égal (=) et un argument pourrait être fait que cet espace blanc potentiel devrait être pris en compte dans tout analyseur document.cookie fiable. Le regex ci-dessus tient compte des deux conditions d'espaces ci-dessus.

Mac
la source
4
Je viens de remarquer que dans Firefox, l'approche regex que j'ai publiée ci-dessus n'est pas aussi performante que l'approche en boucle. Les tests que j'avais exécutés précédemment ont été effectués dans Chrome, où l'approche regex a été légèrement meilleure que les autres approches. Néanmoins, c'est toujours le plus court qui répond à la question posée.
Mac
6
Pourquoi prend getCookieValue(a, b)le paramètre b?
Brent Washburne
16
Vote positif, mais pas pour la lisibilité ... m'a pris un certain temps pour comprendre quoi aet bfaire.
Gigi
10
Intelligent, mais idiot de l'écrire de cette façon pour économiser 1 octet.
The Muffin Man
5
aLe paramètre n'est pas échappé à l'expression régulière, alors qu'il peut être utile, il n'est pas sûr. Des choses comme getCookieValue('.*')
renverront
187

Cela n'atteindra document.cookie qu'une seule fois. Chaque demande ultérieure sera instantanée.

(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
})();

J'ai peur qu'il n'y ait vraiment de moyen plus rapide que cette logique générale à moins que vous ne soyez libre d'utiliser .forEachce qui dépend du navigateur (même dans ce cas, vous n'économisez pas beaucoup)

Votre propre exemple légèrement compressé en 120 bytes:

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

Vous pouvez l'obtenir 110 bytessi vous en faites un nom de fonction d'une lettre, 90 bytessi vous supprimez le fichier encodeURIComponent.

Je l'ai compris 73 bytes, mais pour être honnête, il est 82 bytesnommé readCookieet 102 bytesajouté encodeURIComponent:

function C(k){return(document.cookie.match('(^|; )'+k+'=([^;]*)')||0)[2]}
Mark Kahn
la source
J'ai oublié la déclaration de retour, il n'y a rien de mal à la fermeture, sheesh.
Mark Kahn
3
...Quoi? diff dit que vous l'avez fait. d.pr/sSte il n'avait pas l' ()appel à la fin, donc il définissait une fonction anonyme mais ne l'exécutait jamais.
Yahel
2
Voilà: jsperf.com/pre-increment-vs-post-increment J'avais raison, ++ i est plus rapide, du moins si vous obtenez la valeur avant et après. Et c'est ce que vous faites dans une boucle for.
xavierm02
1
Une chose importante: puisque la valeur des «cookies» est mise en cache, les nouveaux cookies ou les cookies modifiés d'autres fenêtres ou onglets ne seront pas visibles. Vous pouvez éviter ce problème en stockant la valeur de chaîne de document.cookie dans une variable et en vérifiant si elle est inchangée à chaque accès.
Andreas
2
@StijndeWitt - comment ne répond-il pas à la question? 73 octets n'est pas assez court pour vous? :) La dernière réponse que j'ai est identique à celle ci-dessous, à l'exception de quelques vérifications d'espaces, lol
Mark Kahn
21

Hypothèses

Sur la base de la question, je pense que certaines hypothèses / exigences pour cette fonction comprennent:

  • Il sera utilisé comme une fonction de bibliothèque , et donc destiné à être déposé dans n'importe quelle base de code;
  • En tant que tel, il devra fonctionner dans de nombreux environnements différents , c'est-à-dire travailler avec du code JS hérité, des CMS de différents niveaux de qualité, etc.
  • Pour interagir avec du code écrit par d'autres personnes et / ou du code que vous ne contrôlez pas, la fonction ne doit faire aucune hypothèse sur la manière dont les noms ou les valeurs des cookies sont encodés . L'appel de la fonction avec une chaîne "foo:bar[0]"doit renvoyer un cookie (littéralement) nommé "foo: bar [0]";
  • De nouveaux cookies peuvent être écrits et / ou des cookies existants modifiés à tout moment pendant la durée de vie de la page.

Sous ces hypothèses, il est clair que encodeURIComponent/ decodeURIComponent ne doit pas être utilisé ; cela suppose que le code qui a défini le cookie l'a également codé à l'aide de ces fonctions.

L'approche par expression régulière pose problème si le nom du cookie peut contenir des caractères spéciaux. jQuery.cookie contourne ce problème en codant le nom du cookie (en fait à la fois le nom et la valeur) lors du stockage d'un cookie et en décodant le nom lors de la récupération d'un cookie. Une solution d'expression régulière est ci-dessous.

À moins que vous ne lisiez que les cookies que vous contrôlez complètement, il serait également conseillé de lire document.cookiedirectement les cookies et de ne pas mettre en cache les résultats, car il n'y a aucun moyen de savoir si le cache est invalide sans le relire document.cookie.

(Bien que l'accès et l'analyse document.cookiessoient légèrement plus lents que l'utilisation d'un cache, ce ne serait pas aussi lent que la lecture d'autres parties du DOM, car les cookies ne jouent pas de rôle dans les arbres DOM / rendu.)


Fonction basée sur la boucle

Voici la réponse Code Golf, basée sur la fonction PPK (basée sur la boucle):

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, '');
}

qui une fois minifié, arrive à 128 caractères (sans compter le nom de la fonction):

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,'');}

Fonction basée sur des expressions régulières

Mise à jour: si vous voulez vraiment une solution d'expression régulière:

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

Cela échappe tous les caractères spéciaux dans le nom du cookie avant de construire l'objet RegExp. Minifié, cela revient à 134 caractères (sans compter le nom de la fonction):

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

Comme Rudu et cwolves l'ont souligné dans les commentaires, l'expression régulière d'échappement d'expressions régulières peut être raccourcie de quelques caractères. Je pense que ce serait bien de garder la règle d'échappement cohérente (vous l'utilisez peut-être ailleurs), mais leurs suggestions valent la peine d'être prises en compte.


Remarques

Ces deux fonctions ne gèrent pas nullou undefined, c'est-à-dire s'il y a un cookie nommé "null", readCookie(null)renverront sa valeur. Si vous devez gérer ce cas, adaptez le code en conséquence.

Jeffery à
la source
Quel est l'avantage de la #\sfin de votre replace-Regex? Ce remplacement générique peut être simplifié un peu plus (-, n'a pas de sens sauf entre parenthèses qui sont échappées): /[[\]{}()*+?.\\^$|]/g Il peut être encore plus trieur avec encodeURIComponent: /[()*.\\]/g
Rudu
@Rudu Cette expression régulière d'échappement de regex vient de Simon Willison et est également utilisée par la bibliothèque XRegExp . Il est destiné au cas générique (par exemple, si vous le faites, new RegExp('[' + str + ']')vous voudriez vous échapper -), vous avez donc probablement raison de le raccourcir. Comme il ne sauverait que quelques octets et que l'échappement de caractères supplémentaires n'affecterait pas l'expression régulière finale, je suis enclin à le laisser tel quel.
Jeffery Jusqu'au
@Rudu J'éviterais également encodeURIComponent()dans ce cas parce que je pense que l'OP recherche une fonction générique qui peut fonctionner avec n'importe quel nom de cookie. Si un code tiers définit un cookie nommé "foo: bar", utiliser encodeURIComponent()signifiera essayer de lire un cookie nommé "foo% 3Abar".
Jeffery Jusqu'au
Je sais d'où vient le code - il n'est pas parfait (se transformera a<tab>ben a\<tab>b... ce qui n'est pas une évasion valable, bien que ce \<space>soit). Et # semble n'avoir aucune signification dans JS RegEx
Rudu
1
Beaucoup de bibliothèques tierces utilisent encodeURIComponent, si un cookie était nommé, foo=il serait stocké car foo%3Dsans encoder le nom de la même manière, vous ne le trouverez pas (votre code ne le trouvera pas) - il n'y a pas de bonne réponse. Si vous pouvez contrôler la façon dont les cookies sont placés, vous pouvez simplifier la réponse / faire des hypothèses pour aider à les sortir. Le plus simple / le meilleur est d'utiliser uniquement a-zA-Z0-9 pour le nom du cookie et d'éviter que le encodeURIComponentdésordre entier et RegExp ne s'échappe. Mais vous pouvez toujours besoin de vous soucier decodeURIComponentde la valeur du cookie puisque c'est une pratique recommandée.
Rudu
15

code de 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
}
ChicoDeFe
la source
J'ai changé la dernière ligne return d[0];et ensuite je if (c('EXAMPLE_CK') == null)vérifiais si le cookie n'est pas défini.
electroid
10

Celui-ci, ça va?

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

Compté 89 octets sans le nom de la fonction.

Simon Steinberger
la source
Réponse intelligente. Jusqu'au point. Mérite plus de votes positifs. kpourrait être nommékey pour la clarté, mais de toute façon.
Gerold Broser
Merci :-) À présent, la réponse acceptée a été mise à jour et contient également celle-ci. Mais sous une forme encore légèrement plus compressée avec seulement 73 caractères.
Simon Steinberger
Donc, vous visez la brièveté, je vois. Pourquoi Programming Puzzles & Code Golf n'est-il pas sur votre liste de sites? ? S'amuser! Et, BTW, je suis aussi un grimpeur. Berg Heil! ;)
Gerold Broser
5

Ici va .. Cheers!

function getCookie(n) {
    let a = `; ${document.cookie}`.match(`;\\s*${n}=([^;]+)`);
    return a ? a[1] : '';
}

Notez que j'ai utilisé les chaînes de modèle d'ES6 pour composer l'expression regex.

mfalade
la source
4

ceci dans un objet que vous pouvez lire, écrire, écraser et supprimer les cookies.

var cookie = {
    write : function (cname, cvalue, exdays) {
        var d = new Date();
        d.setTime(d.getTime() + (exdays*24*60*60*1000));
        var expires = "expires="+d.toUTCString();
        document.cookie = cname + "=" + cvalue + "; " + expires;
    },
    read : function (name) {
        if (document.cookie.indexOf(name) > -1) {
            return document.cookie.split(name)[1].split("; ")[0].substr(1)
        } else {
            return "";
        }
    },
    delete : function (cname) {
        var d = new Date();
        d.setTime(d.getTime() - 1000);
        var expires = "expires="+d.toUTCString();
        document.cookie = cname + "=; " + expires;
    }
};

la source
2

Ces deux fonctions semblent également valables en termes de lecture des cookies. Vous pouvez cependant raser quelques octets (et cela entre vraiment dans le territoire de Code Golf ici):

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

Tout ce que j'ai fait avec cela est de réduire toutes les déclarations de variables en une seule instruction var, de supprimer les deuxièmes arguments inutiles dans les appels à substring et de remplacer l'appel unique charAt dans un déréférencement de tableau.

Ce n'est toujours pas aussi court que la deuxième fonction que vous avez fournie, mais même cela peut avoir quelques octets supprimés:

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

J'ai changé la première sous-expression de l'expression régulière en une sous-expression de capture, et j'ai changé la partie résultat [1] en résultat [2] pour coïncider avec ce changement; également supprimé les parenthèses inutiles autour du résultat [2].

Jeff Avallone
la source
2

Pour vraiment supprimer autant de ballonnements que possible, envisagez de ne pas utiliser du tout de fonction wrapper:

try {
    var myCookie = document.cookie.match('(^|;) *myCookie=([^;]*)')[2]
} catch (_) {
    // handle missing cookie
}

Tant que vous êtes familier avec RegEx, ce code est raisonnablement propre et facile à lire.

Abhi Beckert
la source
1

Pour que tous les cookies soient accessibles par nom dans une carte :

const cookies = "a=b ; c = d ;e=";
const map = cookies.split(";").map((s) => s.split("=").map((s) => s.trim())).reduce((m, [k, v]) => (m.set(k, v), m), new Map());
console.log(map); //Map(3) {'a' => 'b', 'c' => 'd', 'e' => ''}
map.get("a"); //returns "b"
map.get("c"); //returns "d"
map.get("e"); //returns ""
aller aller
la source
1

La fonction suivante permettra de différencier les chaînes vides et les cookies non définis. Les cookies non définis seront correctement renvoyés undefinedet non une chaîne vide contrairement à certaines des autres réponses ici.

function getCookie(name) {
    return (document.cookie.match('(^|;) *'+name+'=([^;]*)')||[])[1];
}

Ce qui précède a bien fonctionné pour moi sur tous les navigateurs que j'ai vérifiés, mais comme mentionné par @vanovm dans les commentaires, selon la spécification, la clé / valeur peut être entourée d'espaces blancs. Par conséquent, ce qui suit est plus conforme aux normes.

function getCookie(name) {
    return (document.cookie.match('(?:^|;)\\s*'+name.trim()+'\\s*=\\s*([^;]*?)\\s*(?:;|$)')||[])[1];
}
Joyce Babu
la source
Pourquoi renvoyez-vous le troisième élément de la liste?
nasch
Parce que je n'ai pas fait le premier groupe de non-capture. J'ai mis à jour le code.
Joyce Babu
et quel est l'intérêt de ne pas capturer le premier groupe? il n'a ajouté que 2 octets inutiles. De plus, les espaces avant le nom du cookie ainsi qu'avant et après =sont acceptables mais facultatifs (notez que ce sont des espaces, pas seulement des espaces)
vanowm
@vanowm Merci pour le commentaire, j'ai ajouté une version de plainte aux normes.
Joyce Babu
1
@vanowm J'ai essayé de trouver s'il y avait des avantages à utiliser un groupe sans capture par rapport à un groupe de capture. Mais je n'ai pas pu en trouver. En théorie, cela pourrait être plus rapide, mais la différence est négligeable par rapport au test que j'ai effectué. Par conséquent, je l'ai supprimé de la version plus courte. De plus, les espaces blancs de début / de fin dans le nom du cookie sont coupés, c'est pourquoi j'ai mis à jour la version plus longue pour couper le nom.
Joyce Babu
0

(modifier: a d'abord publié la mauvaise version .. et une non fonctionnelle à cela. Mis à jour à la version actuelle, qui utilise une fonction unparam qui ressemble beaucoup au deuxième exemple.)

Belle idée dans le premier exemple cwolves. J'ai construit sur les deux pour une fonction de lecture / écriture de cookies assez compacte qui fonctionne sur plusieurs sous-domaines. Je pensais que je partagerais au cas où quelqu'un d'autre rencontrerait ce fil à la recherche de cela.

(function(s){
  s.strToObj = function (x,splitter) {
    for ( var y = {},p,a = x.split (splitter),L = a.length;L;) {
      p = a[ --L].split ('=');
      y[p[0]] = p[1]
    }
    return y
  };
  s.rwCookie = function (n,v,e) {
    var d=document,
        c= s.cookies||s.strToObj(d.cookie,'; '),
        h=location.hostname,
        domain;
    if(v){
      domain = h.slice(h.lastIndexOf('.',(h.lastIndexOf('.')-1))+1);
      d.cookie = n + '=' + (c[n]=v) + (e ? '; expires=' + e : '') + '; domain=.' + domain + '; path=/'
    }
    return c[n]||c
  };
})(some_global_namespace)
  • Si vous ne passez rien à rwCookie, tous les cookies seront stockés dans le stockage des cookies
  • Passé rwCookie un nom de cookie, il obtient la valeur de ce cookie du stockage
  • Passé une valeur de cookie, il écrit le cookie et place la valeur dans le stockage
  • L'expiration est définie par défaut sur session sauf si vous en spécifiez une
Adam
la source
0

En utilisant la réponse de cwolves, mais sans utiliser de fermeture ni de hachage pré-calculé:

// Golfed it a bit, too...
function readCookie(n){
  var c = document.cookie.split('; '),
      i = c.length,
      C;

  for(; i>0; i--){
     C = c[i].split('=');
     if(C[0] == n) return C[1];
  }
}

... et minifier ...

function readCookie(n){var c=document.cookie.split('; '),i=c.length,C;for(;i>0;i--){C=c[i].split('=');if(C[0]==n)return C[1];}}

... équivaut à 127 octets.

Félix Saparelli
la source
0

Voici la solution la plus simple utilisant des fonctions de chaîne javascript.

document.cookie.substring(document.cookie.indexOf("COOKIE_NAME"), 
                          document.cookie.indexOf(";", 
                          document.cookie.indexOf("COOKIE_NAME"))).
  substr(COOKIE_NAME.length);
Kumar
la source
Est-ce COOKIE_NAMEune chaîne ou une variable? Votre exemple n'a pas de sens ...
vanowm
0

Juste pour jeter mon chapeau dans la course, voici ma proposition:

function getCookie(name) {
   const cookieDict = document.cookie.split(';')
        .map((x)=>x.split('='))
        .reduce((accum,current) => { accum[current[0]]=current[1]; return accum;}, Object());
    return cookieDict[name];
}

Le code ci-dessus génère un dict qui stocke les cookies sous forme de paires clé-valeur (c'est-à-dire cookieDict), et accède ensuite à la propriété namepour récupérer le cookie.

Cela pourrait effectivement être exprimé comme un one-liner, mais ce n'est que pour les courageux:

document.cookie.split(';').map((x)=>x.split('=')).reduce((accum,current) => { accum[current[0]]=current[1]; return accum;}, {})[name]

La meilleure approche absolue serait de générer cookieDictau chargement de la page, puis tout au long du cycle de vie de la page, d'accéder simplement aux cookies individuels en appelant cookieDict['cookiename'].

RAM
la source