La mia webapp ha errori javascript nella navigazione privata di ios safari:
JavaScript: Errore
- Come differire le sessioni nelle tabs del browser?
- Quando non dovrebbe essere utilizzato un servizio web?
- Chi imposta il tipo di contenuto della risposta in Spring MVC (@ResponseBody)
- Cross Domain Resource Sharing GET: 'rifiutato di ottenere un'intestazione non sicura "etag"' dalla risposta
- java.lang.UnsatisfiedLinkError no *****. dll in java.library.path
non definito
QUOTA_EXCEEDED_ERR: DOM Exception 22: tentativo di aggiungere qualcosa allo storage …
il mio codice:
localStorage.setItem('test',1)
Apparentemente questo è di design. Quando Safari (OS X o iOS) è in modalità di navigazione privata, sembra che localStorage
sia disponibile, ma provare a chiamare setItem
genera un’eccezione.
store.js line 73 "QUOTA_EXCEEDED_ERR: DOM Exception 22: An attempt was made to add something to storage that exceeded the quota."
Quello che succede è che l’object window espone ancora localStorage
nello spazio dei nomi globale, ma quando si chiama setItem
, viene generata questa eccezione. Qualsiasi chiamata a removeItem
viene ignorata.
Credo che la soluzione più semplice (sebbene non abbia ancora provato questo cross browser) sarebbe quella di alterare la funzione isLocalStorageNameSupported()
per verificare che si possa anche impostare un valore.
https://github.com/marcuswestin/store.js/issues/42
function isLocalStorageNameSupported() { var testKey = 'test', storage = window.sessionStorage; try { storage.setItem(testKey, '1'); storage.removeItem(testKey); return localStorageName in win && win[localStorageName]; } catch (error) { return false; } }
La correzione pubblicata sul link sopra non ha funzionato per me. Questo ha fatto:
function isLocalStorageNameSupported() { var testKey = 'test', storage = window.localStorage; try { storage.setItem(testKey, '1'); storage.removeItem(testKey); return true; } catch (error) { return false; } }
Derivato da http://m.cg/post/13095478393/detect-private-browsing-mode-in-mobile-safari-on-ios5
Come accennato in altre risposte, otterrete sempre il QuotaExceededError nella modalità Browser privato di Safari su iOS e OS X quando viene chiamato localStorage.setItem
(o sessionStorage.setItem
).
Una soluzione è eseguire un controllo try / catch o Modernizr in ogni istanza di utilizzo di setItem
.
Tuttavia, se vuoi uno spessore che blocchi globalmente questo errore, per evitare che il resto del tuo JavaScript si rompa, puoi usare questo:
https://gist.github.com/philfreo/68ea3cd980d72383c951
// Safari, in Private Browsing Mode, looks like it supports localStorage but all calls to setItem // throw QuotaExceededError. We're going to detect this and just silently drop any calls to setItem // to avoid the entire page breaking, without having to do a check at each usage of Storage. if (typeof localStorage === 'object') { try { localStorage.setItem('localStorage', 1); localStorage.removeItem('localStorage'); } catch (e) { Storage.prototype._setItem = Storage.prototype.setItem; Storage.prototype.setItem = function() {}; alert('Your web browser does not support storing settings locally. In Safari, the most common cause of this is using "Private Browsing Mode". Some settings may not save or some features may not work properly for you.'); } }
Nel mio contesto, ho appena sviluppato un’astrazione di class. Quando viene avviata la mia applicazione, controllo se localStorage funziona chiamando getStorage () . Anche questa funzione restituisce:
Nel mio codice non ho mai chiamato localStorage direttamente. Chiamo cusSto global var, l’ho inizializzato chiamando getStorage () .
In questo modo, funziona con la navigazione privata o specifiche versioni di Safari
function getStorage() { var storageImpl; try { localStorage.setItem("storage", ""); localStorage.removeItem("storage"); storageImpl = localStorage; } catch (err) { storageImpl = new LocalStorageAlternative(); } return storageImpl; } function LocalStorageAlternative() { var structureLocalStorage = {}; this.setItem = function (key, value) { structureLocalStorage[key] = value; } this.getItem = function (key) { if(typeof structureLocalStorage[key] != 'undefined' ) { return structureLocalStorage[key]; } else { return null; } } this.removeItem = function (key) { structureLocalStorage[key] = undefined; } } cusSto = getStorage();
Per espandere le risposte degli altri, ecco una soluzione compatta che non espone / aggiunge nuove variabili. Non copre tutte le basi, ma dovrebbe essere adatto alla maggior parte delle persone che desiderano solo un’app per pagina singola per rimanere funzionale (nonostante la persistenza dei dati dopo la ricarica).
(function(){ try { localStorage.setItem('_storage_test', 'test'); localStorage.removeItem('_storage_test'); } catch (exc){ var tmp_storage = {}; var p = '__unique__'; // Prefix all keys to avoid matching built-ins Storage.prototype.setItem = function(k, v){ tmp_storage[p + k] = v; }; Storage.prototype.getItem = function(k){ return tmp_storage[p + k] === undefined ? null : tmp_storage[p + k]; }; Storage.prototype.removeItem = function(k){ delete tmp_storage[p + k]; }; Storage.prototype.clear = function(){ tmp_storage = {}; }; } })();
Ho avuto lo stesso problema usando la struttura ionica (Angular + Cordova). So che questo non risolve il problema, ma è il codice per le app angolari basato sulle risposte sopra. Avrai una soluzione effimera per localStorage nella versione iOS di Safari.
Ecco il codice:
angular.module('myApp.factories', []) .factory('$fakeStorage', [ function(){ function FakeStorage() {}; FakeStorage.prototype.setItem = function (key, value) { this[key] = value; }; FakeStorage.prototype.getItem = function (key) { return typeof this[key] == 'undefined' ? null : this[key]; } FakeStorage.prototype.removeItem = function (key) { this[key] = undefined; }; FakeStorage.prototype.clear = function(){ for (var key in this) { if( this.hasOwnProperty(key) ) { this.removeItem(key); } } }; FakeStorage.prototype.key = function(index){ return Object.keys(this)[index]; }; return new FakeStorage(); } ]) .factory('$localstorage', [ '$window', '$fakeStorage', function($window, $fakeStorage) { function isStorageSupported(storageName) { var testKey = 'test', storage = $window[storageName]; try { storage.setItem(testKey, '1'); storage.removeItem(testKey); return true; } catch (error) { return false; } } var storage = isStorageSupported('localStorage') ? $window.localStorage : $fakeStorage; return { set: function(key, value) { storage.setItem(key, value); }, get: function(key, defaultValue) { return storage.getItem(key) || defaultValue; }, setObject: function(key, value) { storage.setItem(key, JSON.stringify(value)); }, getObject: function(key) { return JSON.parse(storage.getItem(key) || '{}'); }, remove: function(key){ storage.removeItem(key); }, clear: function() { storage.clear(); }, key: function(index){ storage.key(index); } } } ]);
Fonte: https://gist.github.com/jorgecasar/61fda6590dc2bb17e871
Buona lettura!
Ecco una soluzione per AngularJS che utilizza un IIFE e fa leva sul fatto che i servizi sono singleton .
Ciò comporta che isLocalStorageAvailable
sia impostato immediatamente quando il servizio viene iniettato per la prima volta ed evita di eseguire inutilmente il controllo ogni volta che è necessario accedere all’archivio locale.
angular.module('app.auth.services', []).service('Session', ['$log', '$window', function Session($log, $window) { var isLocalStorageAvailable = (function() { try { $window.localStorage.world = 'hello'; delete $window.localStorage.world; return true; } catch (ex) { return false; } })(); this.store = function(key, value) { if (isLocalStorageAvailable) { $window.localStorage[key] = value; } else { $log.warn('Local Storage is not available'); } }; } ]);
Ho appena creato questo repository per fornire le sessionStorage
e localStorage
per i browser non supportati o disabilitati.
Browser supportati
Come funziona
Rileva la funzione con il tipo di archiviazione.
function(type) { var testKey = '__isSupported', storage = window[type]; try { storage.setItem(testKey, '1'); storage.removeItem(testKey); return true; } catch (error) { return false; } };
Imposta window.localStorage
su window.localStorage
se è supportato o crea una memorizzazione dei cookie. Imposta StorageService.sessionStorage
su window.sessionStorage
se è supportato o crea una memoria in memoria per SPA, memorizzazione dei cookie con funzioni sesion per non SPA.
Sembra che Safari 11 cambi il comportamento e ora l’archiviazione locale funzioni in una finestra del browser privata. Evviva!
La nostra app Web che prima falliva nella navigazione privata di Safari ora funziona perfettamente. Ha sempre funzionato bene nella modalità di navigazione privata di Chrome, che ha sempre consentito la scrittura su memoria locale.
Questo è documentato nelle note sulla versione di Safari Technology Preview di Apple – e nelle note di rilascio di WebKit – per la release 29, che era in maggio 2017.
In particolare:
condivisione in Es6 full read e write localStorage Esempio con controllo dell’assistenza
const LOCAL_STORAGE_KEY = 'tds_app_localdata'; const isSupported = () => { try { localStorage.setItem('supported', '1'); localStorage.removeItem('supported'); return true; } catch (error) { return false; } }; const writeToLocalStorage = components => (isSupported ? localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(components)) : components); const isEmpty = component => (!component || Object.keys(component).length === 0); const readFromLocalStorage = () => (isSupported ? JSON.parse(localStorage.getItem(LOCAL_STORAGE_KEY)) || {} : null);
Ciò assicurerà che le tue chiavi siano impostate e recuperate correttamente su tutti i browser.
Il seguente script ha risolto il mio problema:
// Fake localStorage implementation. // Mimics localStorage, including events. // It will work just like localStorage, except for the persistant storage part. var fakeLocalStorage = function() { var fakeLocalStorage = {}; var storage; // If Storage exists we modify it to write to our fakeLocalStorage object instead. // If Storage does not exist we create an empty object. if (window.Storage && window.localStorage) { storage = window.Storage.prototype; } else { // We don't bother implementing a fake Storage object window.localStorage = {}; storage = window.localStorage; } // For older IE if (!window.location.origin) { window.location.origin = window.location.protocol + "//" + window.location.hostname + (window.location.port ? ':' + window.location.port: ''); } var dispatchStorageEvent = function(key, newValue) { var oldValue = (key == null) ? null : storage.getItem(key); // `==` to match both null and undefined var url = location.href.substr(location.origin.length); var storageEvent = document.createEvent('StorageEvent'); // For IE, http://stackoverflow.com/a/25514935/1214183 storageEvent.initStorageEvent('storage', false, false, key, oldValue, newValue, url, null); window.dispatchEvent(storageEvent); }; storage.key = function(i) { var key = Object.keys(fakeLocalStorage)[i]; return typeof key === 'string' ? key : null; }; storage.getItem = function(key) { return typeof fakeLocalStorage[key] === 'string' ? fakeLocalStorage[key] : null; }; storage.setItem = function(key, value) { dispatchStorageEvent(key, value); fakeLocalStorage[key] = String(value); }; storage.removeItem = function(key) { dispatchStorageEvent(key, null); delete fakeLocalStorage[key]; }; storage.clear = function() { dispatchStorageEvent(null, null); fakeLocalStorage = {}; }; }; // Example of how to use it if (typeof window.localStorage === 'object') { // Safari will throw a fit if we try to use localStorage.setItem in private browsing mode. try { localStorage.setItem('localStorageTest', 1); localStorage.removeItem('localStorageTest'); } catch (e) { fakeLocalStorage(); } } else { // Use fake localStorage for any browser that does not support it. fakeLocalStorage(); }
Controlla se esiste localStorage e può essere utilizzato e, in caso negativo, crea una memoria locale falsa e la utilizza al posto del localStorage originale. Per favore fatemi sapere se avete bisogno di ulteriori informazioni.