Mi aplicación web tiene errores de JavaScript en la navegación privada de ios safari:
JavaScript: error
indefinido
QUOTA_EXCEEDED_ERR: DOM Excepción 22: se intentó agregar algo al almacenamiento ...
mi código:
localStorage.setItem('test',1)
Mi aplicación web tiene errores de JavaScript en la navegación privada de ios safari:
JavaScript: error
indefinido
QUOTA_EXCEEDED_ERR: DOM Excepción 22: se intentó agregar algo al almacenamiento ...
mi código:
localStorage.setItem('test',1)
Respuestas:
Aparentemente esto es por diseño. Cuando Safari (OS X o iOS) está en modo de navegación privada, parece que localStorage
está disponible, pero intentar llamar setItem
arroja una excepción.
store.js line 73
"QUOTA_EXCEEDED_ERR: DOM Exception 22: An attempt was made to add something to storage that exceeded the quota."
Lo que sucede es que el objeto de la ventana aún se expone localStorage
en el espacio de nombres global, pero cuando llama setItem
, se produce esta excepción. Cualquier llamada aremoveItem
se ignora.
Creo que la solución más simple (aunque todavía no he probado este navegador cruzado) sería alterar la función isLocalStorageNameSupported()
para probar que también puede establecer algún valor.
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;
}
}
return localStorageName in win && win[localStorageName];
a return true
. Entonces tiene una función que devuelve con seguridad verdadero o falso dependiendo de la disponibilidad localStorage. Por ejemplo:if (isLocalStorageNameSupported()) { /* You can use localStorage.setItem */ } else { /* you can't use localStorage.setItem */ }
La solución publicada en el enlace anterior no funcionó para mí. Esto hizo:
function isLocalStorageNameSupported() {
var testKey = 'test', storage = window.localStorage;
try {
storage.setItem(testKey, '1');
storage.removeItem(testKey);
return true;
} catch (error) {
return false;
}
}
Derivado de http://m.cg/post/13095478393/detect-private-browsing-mode-in-mobile-safari-on-ios5
window.sessionStorage
es correcto. Ciertamente funciona en mi código. En realidad, señale la solución al problema que parece conocer.
isLocalStorageNameSupported
y estaba comprobando window.sessionStorage
. El mismo resultado final pero fue un poco confuso. La respuesta fue editada para aclarar.
Como se menciona en otras respuestas, siempre obtendrá QuotaExceededError en el modo de navegador privado Safari tanto en iOS como en OS X cuando localStorage.setItem
(osessionStorage.setItem
se llama ).
Una solución es hacer una prueba try / catch o Modernizr en cada instancia de usosetItem
.
Sin embargo, si desea una cuña que simplemente detenga globalmente este error, para evitar que el resto de su JavaScript se rompa, puede usar esto:
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.');
}
}
En mi contexto, acabo de desarrollar una abstracción de clase. Cuando se inicia mi aplicación, compruebo si localStorage funciona llamando a getStorage () . Esta función también devuelve:
En mi código, nunca llamo a localStorage directamente. Llamo a cusSto global var, lo había inicializado llamando a getStorage () .
De esta manera, funciona con navegación privada o versiones específicas de 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();
Parece que Safari 11 cambia el comportamiento, y ahora el almacenamiento local funciona en una ventana privada del navegador. ¡Hurra!
Nuestra aplicación web que solía fallar en la navegación privada de Safari ahora funciona perfectamente. Siempre funcionó bien en el modo de navegación privada de Chrome, que siempre ha permitido escribir en el almacenamiento local.
Esto está documentado en las notas de lanzamiento de Safari Technology Preview de Apple, y en las notas de lanzamiento de WebKit , para el lanzamiento 29, que fue en mayo de 2017.
Específicamente:
Para ampliar las respuestas de otros, aquí hay una solución compacta que no expone / agrega ninguna variable nueva. No cubre todas las bases, pero debería adaptarse a la mayoría de las personas que solo desean que una aplicación de una sola página permanezca funcional (a pesar de que no persiste la información después de la recarga).
(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 = {};
};
}
})();
Tuve el mismo problema usando Ionic Framework (Angular + Cordova). Sé que esto no resuelve el problema, pero es el código para Angular Apps basado en las respuestas anteriores. Tendrá una solución efímera para localStorage en la versión iOS de Safari.
Aquí está el código:
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);
}
}
}
]);
Fuente: https://gist.github.com/jorgecasar/61fda6590dc2bb17e871
¡Disfruta tu codificación!
Aquí hay una solución para AngularJS utilizando un IIFE y aprovechando el hecho de que los servicios son únicos .
Esto isLocalStorageAvailable
se configura inmediatamente cuando se inyecta el servicio por primera vez y evita ejecutar innecesariamente la verificación cada vez que se necesita acceder al almacenamiento local.
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');
}
};
}
]);
He creado esta cesión temporal para proveer sessionStorage
y localStorage
características de los navegadores no compatibles o discapacitados.
Navegadores compatibles
Cómo funciona
Detecta la función con el tipo de almacenamiento.
function(type) {
var testKey = '__isSupported',
storage = window[type];
try {
storage.setItem(testKey, '1');
storage.removeItem(testKey);
return true;
} catch (error) {
return false;
}
};
Conjuntos StorageService.localStorage
a window.localStorage
si es compatible o crea un almacenamiento de cookies. Conjuntos StorageService.sessionStorage
a window.sessionStorage
si es compatible o crea un almacenamiento en memoria para SPA, el almacenamiento de cookies con características sesion para no SPA.
Aquí hay una versión de servicio Angular2 + para la alternativa de almacenamiento de memoria, puede simplemente inyectar en sus componentes, según la respuesta de Pierre Le Roux.
import { Injectable } from '@angular/core';
// Alternative to localstorage, memory
// storage for certain browsers in private mode
export class LocalStorageAlternative {
private structureLocalStorage = {};
setItem(key: string, value: string): void {
this.structureLocalStorage[key] = value;
}
getItem(key: string): string {
if (typeof this.structureLocalStorage[key] !== 'undefined' ) {
return this.structureLocalStorage[key];
}
return null;
}
removeItem(key: string): void {
this.structureLocalStorage[key] = undefined;
}
}
@Injectable()
export class StorageService {
private storageEngine;
constructor() {
try {
localStorage.setItem('storage_test', '');
localStorage.removeItem('storage_test');
this.storageEngine = localStorage;
} catch (err) {
this.storageEngine = new LocalStorageAlternative();
}
}
setItem(key: string, value: string): void {
this.storageEngine.setItem(key, value);
}
getItem(key: string): string {
return this.storageEngine.getItem(key);
}
removeItem(key: string): void {
this.storageEngine.removeItem(key);
}
}
compartir en Es6 ejemplo completo de lectura y escritura localStorage con verificación de soporte
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);
Esto asegurará que sus claves estén configuradas y recuperadas correctamente en todos los navegadores.
He creado un parche para el problema. Simplemente estoy comprobando si el navegador admite localStorage o sessionStorage o no. De lo contrario, el motor de almacenamiento será Cookie. Pero el lado negativo es que las cookies tienen una memoria de almacenamiento muy pequeña :(
function StorageEngine(engine) {
this.engine = engine || 'localStorage';
if(!this.checkStorageApi(this.engine)) {
// Default engine would be alway cooke
// Safari private browsing issue with localStorage / sessionStorage
this.engine = 'cookie';
}
}
StorageEngine.prototype.checkStorageApi = function(name) {
if(!window[name]) return false;
try {
var tempKey = '__temp_'+Date.now();
window[name].setItem(tempKey, 'hi')
window[name].removeItem(tempKey);
return true;
} catch(e) {
return false;
}
}
StorageEngine.prototype.getItem = function(key) {
if(['sessionStorage', 'localStorage'].includes(this.engine)) {
return window[this.engine].getItem(key);
} else if('cookie') {
var name = key+"=";
var allCookie = decodeURIComponent(document.cookie).split(';');
var cval = [];
for(var i=0; i < allCookie.length; i++) {
if (allCookie[i].trim().indexOf(name) == 0) {
cval = allCookie[i].trim().split("=");
}
}
return (cval.length > 0) ? cval[1] : null;
}
return null;
}
StorageEngine.prototype.setItem = function(key, val, exdays) {
if(['sessionStorage', 'localStorage'].includes(this.engine)) {
window[this.engine].setItem(key, val);
} else if('cookie') {
var d = new Date();
var exdays = exdays || 1;
d.setTime(d.getTime() + (exdays*24*36E5));
var expires = "expires="+ d.toUTCString();
document.cookie = key + "=" + val + ";" + expires + ";path=/";
}
return true;
}
// ------------------------
var StorageEngine = new StorageEngine(); // new StorageEngine('localStorage');
// If your current browser (IOS safary or any) does not support localStorage/sessionStorage, then the default engine will be "cookie"
StorageEngine.setItem('keyName', 'val')
var expireDay = 1; // for cookie only
StorageEngine.setItem('keyName', 'val', expireDay)
StorageEngine.getItem('keyName')
La respuesta aceptada parece no adecuada en varias situaciones.
Para verificar si el localStorage
o sessionStorage
son compatibles, utilizo el siguiente fragmento de MDN .
function storageAvailable(type) {
var storage;
try {
storage = window[type];
var x = '__storage_test__';
storage.setItem(x, x);
storage.removeItem(x);
return true;
}
catch(e) {
return e instanceof DOMException && (
// everything except Firefox
e.code === 22 ||
// Firefox
e.code === 1014 ||
// test name field too, because code might not be present
// everything except Firefox
e.name === 'QuotaExceededError' ||
// Firefox
e.name === 'NS_ERROR_DOM_QUOTA_REACHED') &&
// acknowledge QuotaExceededError only if there's something already stored
(storage && storage.length !== 0);
}
}
Utilice este fragmento como este y recurra a, por ejemplo, el uso de cookies:
if (storageAvailable('localStorage')) {
// Yippee! We can use localStorage awesomeness
}
else {
// Too bad, no localStorage for us
document.cookie = key + "=" + encodeURIComponent(value) + expires + "; path=/";
}
He creado el paquete fallbackstorage que utiliza este fragmento para verificar la disponibilidad de almacenamiento y el respaldo a un MemoryStorage implementado manualmente.
import {getSafeStorage} from 'fallbackstorage'
getSafeStorage().setItem('test', '1') // always work
var mod = 'test';
try {
sessionStorage.setItem(mod, mod);
sessionStorage.removeItem(mod);
return true;
} catch (e) {
return false;
}
El siguiente script resolvió mi 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();
}
Comprueba si localStorage existe y puede usarse y, en el caso negativo, crea un almacenamiento local falso y lo usa en lugar del localStorage original. Avíseme si necesita más información.