Je cherchais un moyen de communiquer entre plusieurs onglets ou fenêtres d'un navigateur (sur le même domaine, pas CORS) sans laisser de traces. Il y avait plusieurs solutions:
La première solution est probablement la pire des solutions - vous devez ouvrir une fenêtre à partir de votre fenêtre actuelle et vous ne pouvez alors communiquer que si vous maintenez les fenêtres ouvertes. Si vous rechargez la page dans l'une des fenêtres, vous avez probablement perdu la communication.
La deuxième approche, qui utilise postMessage, permet probablement la communication entre plusieurs origines, mais présente le même problème que la première. Vous devez gérer un objet window.
Troisièmement, en utilisant des cookies, stockez des données dans le navigateur, ce qui peut ressembler à envoyer un message à toutes les fenêtres du même domaine, mais le problème est que vous ne pouvez jamais savoir si tous les onglets lisent le "message" déjà ou pas avant. nettoyer. Vous devez mettre en place une sorte de délai d'attente pour lire le cookie périodiquement. De plus, vous êtes limité par la longueur maximale du cookie, qui est de 4 Ko.
La quatrième solution, utilisant localStorage, semblait dépasser les limites des cookies et pouvait même être écoutée en utilisant des événements. Son utilisation est décrite dans la réponse acceptée.
Edit 2018: la réponse acceptée fonctionne toujours, mais il existe une solution plus récente pour les navigateurs modernes, consistant à utiliser BroadcastChannel. Voir l’autre réponse pour un exemple simple décrivant comment transmettre facilement un message entre des onglets à l’aide de BroadcastChannel.
Edit 2018: Vous pouvez mieux utiliser BroadcastChannel à cette fin, voir les réponses ci-dessous. Cependant, si vous préférez toujours utiliser le stockage local pour la communication entre les onglets, procédez comme suit:
Pour être averti lorsqu'un onglet envoie un message à d'autres onglets, il vous suffit de lier l'événement "stockage". Dans tous les onglets, procédez comme suit:
$(window).on('storage', message_receive);
La fonction message_receive
sera appelée chaque fois que vous définissez une valeur de localStorage dans un autre onglet. L'écouteur d'événements contient également les données nouvellement définies sur localStorage. Vous n'avez donc même pas besoin d'analyser l'objet localStorage lui-même. Ceci est très pratique car vous pouvez réinitialiser la valeur juste après son paramétrage pour nettoyer efficacement toutes les traces. Voici les fonctions pour la messagerie:
// use local storage for messaging. Set message in local storage and clear it right away
// This is a safe way how to communicate with other tabs while not leaving any traces
//
function message_broadcast(message)
{
localStorage.setItem('message',JSON.stringify(message));
localStorage.removeItem('message');
}
// receive message
//
function message_receive(ev)
{
if (ev.originalEvent.key!='message') return; // ignore other keys
var message=JSON.parse(ev.originalEvent.newValue);
if (!message) return; // ignore empty msg or msg reset
// here you act on messages.
// you can send objects like { 'command': 'doit', 'data': 'abcd' }
if (message.command == 'doit') alert(message.data);
// etc.
}
Alors maintenant, une fois que vos onglets sont liés à l'événement onstorage et que ces deux fonctions sont implémentées, vous pouvez simplement diffuser un message à d'autres onglets appelant, par exemple:
message_broadcast({'command':'reset'})
N'oubliez pas que l'envoi du même message exactement deux fois ne sera propagé qu'une seule fois. Si vous devez répéter les messages, ajoutez-leur un identificateur unique, comme
message_broadcast({'command':'reset', 'uid': (new Date).getTime()+Math.random()})
Rappelez-vous également que l'onglet actuel qui diffuse le message ne le reçoit pas, mais uniquement les autres onglets ou fenêtres du même domaine.
Vous pouvez demander ce qui se passe si l'utilisateur charge une page Web différente ou ferme son onglet juste après l'appel de setItem () avant le removeItem (). De mon côté, le navigateur met le déchargement en attente jusqu'à la fin de la fonction message_broadcast()
. J'ai essayé de mettre là un très long cycle pour () et il attendait toujours que le cycle se termine avant de se fermer. Si l'utilisateur supprime l'onglet juste entre les deux, le navigateur n'aura pas assez de temps pour enregistrer le message sur le disque. Cette approche me semble donc être un moyen sûr d'envoyer des messages sans aucune trace. Commentaires bienvenus.
Une API moderne est dédiée à cet effet - Broadcast Channel
C'est aussi simple que:
var bc = new BroadcastChannel('test_channel');
bc.postMessage('This is a test message.'); /* send */
bc.onmessage = function (ev) { console.log(ev); } /* receive */
Il n'est pas nécessaire que le message soit simplement une chaîne DOMString, tout type d'objet peut être envoyé.
Mis à part la propreté de l'API, c'est probablement le principal avantage de cette API: pas de stringification d'objet.
Actuellement pris en charge uniquement dans Chrome et Firefox, mais vous pouvez trouver un polyfill qui utilise localStorage.
Pour ceux qui recherchent une solution non basée sur jQuery, voici une version JavaScript simple de la solution fournie par Thomas M:
window.addEventListener("storage", message_receive);
function message_broadcast(message) {
localStorage.setItem('message',JSON.stringify(message));
}
function message_receive(ev) {
if (ev.key == 'message') {
var message=JSON.parse(ev.newValue);
}
}
Checkout AcrossTabs - Communication facile entre les onglets du navigateur cross-Origin. Il utilise une combinaison de postMessage et sessionStorage API pour rendre la communication beaucoup plus simple et fiable.
Il existe différentes approches et chacune a ses avantages et ses inconvénients. Permet de discuter chacun:
Avantages:
Les inconvénients:
Avantages:
Les inconvénients:
Les données sont renvoyées au serveur pour chaque requête HTTP (HTML, images, JavaScript, CSS, etc.), ce qui augmente la quantité de trafic entre le client et le serveur.
En règle générale, les éléments suivants sont autorisés:
Avantages:
localStorage
.Les inconvénients:
localStorage
, tt fonctionne sur politique de même origine . Ainsi, les données stockées ne seront disponibles que sur la même origine.Avantages:
Les inconvénients:
targetOrigin
et un contrôle d'intégrité des données transmises. l'écouteur de messages.Une combinaison de PostMessage + SessionStorage
En utilisant postMessage pour communiquer entre plusieurs onglets et en même temps, en utilisant sessionStorage dans tous les onglets/fenêtres récemment ouverts pour conserver les données transmises. Les données seront conservées tant que les onglets/fenêtres resteront ouverts. Ainsi, même si l'onglet/la fenêtre d'ouverture est fermé, les données entières des onglets/fenêtres ouverts seront conservées même après actualisation.
J'ai écrit une bibliothèque JavaScript pour cela, nommée AcrossTabs , qui utilise l'API postMessage pour communiquer entre les onglets/fenêtres cross-Origin et sessionStorage afin de conserver l'identité des onglets/fenêtres ouverts tant qu'ils sont conservés.
Les travailleurs partagés sont une autre méthode que les utilisateurs devraient envisager d'utiliser. Je sais que le concept est à la pointe de la technologie, mais vous pouvez créer sur un travailleur partagé un relais beaucoup plus rapide que le stockage local et ne nécessitant pas de relation entre la fenêtre parent/enfant, tant que vous êtes sur la même origine.
Voir ma réponse ici pour une discussion que j'ai faite à ce sujet.
Il existe un minuscule composant open-source pour synchroniser/communiquer entre des onglets/fenêtres de la même origine (disclaimer - je suis l'un des contributeurs!) Basé sur localStorage
.
TabUtils.BroadcastMessageToAllTabs("eventName", eventDataString);
TabUtils.OnBroadcastMessage("eventName", function (eventDataString) {
DoSomething();
});
TabUtils.CallOnce("lockname", function () {
alert("I run only once across multiple tabs");
});
https://github.com/jitbit/TabUtils
P.S. J'ai pris la liberté de le recommander ici car la plupart des composants "lock/mutex/sync" échouent sur les connexions websocket lorsque des événements se produisent presque simultanément.
J'ai créé un module qui fonctionne de la même manière que le Broadcastchannel officiel mais comporte des replis basés sur localstorage, indexeddb et unix-sockets. Cela garantit que cela fonctionne toujours, même avec Webworkers ou NodeJS. Voir pubkey: BroadcastChannel
J'ai créé une bibliothèque sysend.js , elle est très petite, vous pouvez vérifier son code source. La bibliothèque n'a pas de dépendances externes.
Vous pouvez l'utiliser pour la communication entre les onglets/fenêtres du même navigateur et du même domaine. La bibliothèque utilise BroadcastChannel, si pris en charge, ou un événement de stockage de localStorage.
L'API est très simple:
sysend.on('foo', function(message) {
console.log(message);
});
sysend.broadcast('foo', {message: 'Hello'});
sysend.broadcast('foo', "hello");
sysend.broadcast('foo'); // empty notification
lorsque votre navigateur prend en charge BroadcastChannel, il envoie un objet littéral et sinon, il est d'abord sérialisé en JSON et désérialisé de l'autre côté.
Les versions récentes ont également une API auxiliaire pour créer un proxy pour la communication entre domaines. (Cela nécessite un fichier HTML unique sur le domaine cible).
Voici demo .
NOTE: Si vous voulez implémenter la même fonctionnalité en utilisant localStorage, il y a un problème dans IE. L'événement de stockage est envoyé à la même fenêtre, ce qui a déclenché l'événement et n'est appelé pour d'autres navigateurs que pour d'autres onglets/fenêtres.
J'ai écrit un article à ce sujet sur mon blog: http://www.ebenmonney.com/blog/how-to-implement-remember-me-functionality-using-token-based-authentication-and-localstorage-in- une application web .
En utilisant une bibliothèque que j'ai créée storageManager
, vous pouvez y parvenir comme suit:
storageManager.savePermanentData('data', 'key'): //saves permanent data
storageManager.saveSyncedSessionData('data', 'key'); //saves session data to all opened tabs
storageManager.saveSessionData('data', 'key'); //saves session data to current tab only
storageManager.getData('key'); //retrieves data
Il existe également d'autres méthodes pratiques pour gérer d'autres scénarios.