У мого веб-сайту є помилки javascript у приватному перегляді ios safari:
JavaScript: помилка
невизначений
QUOTA_EXCEEDED_ERR: DOM Виняток 22: Була спроба додати щось у сховище ...
мій код:
localStorage.setItem('test',1)
У мого веб-сайту є помилки javascript у приватному перегляді ios safari:
JavaScript: помилка
невизначений
QUOTA_EXCEEDED_ERR: DOM Виняток 22: Була спроба додати щось у сховище ...
мій код:
localStorage.setItem('test',1)
Відповіді:
Мабуть, це задумом. Коли Safari (OS X або iOS) перебуває в режимі приватного перегляду, воно виглядає як би localStorage
доступним, але спроба виклику setItem
кидає виняток.
store.js line 73
"QUOTA_EXCEEDED_ERR: DOM Exception 22: An attempt was made to add something to storage that exceeded the quota."
Що відбувається, так це те, що об’єкт вікна все ще відкривається localStorage
в глобальному просторі імен, але коли ви телефонуєте setItem
, цей виняток кидається. Будь-які дзвінки до removeItem
ігноруються.
Я вважаю, що найпростішим виправленням (хоча я ще не перевіряв цей крос-браузер) було б змінити функцію, isLocalStorageNameSupported()
щоб перевірити, що ви також можете встановити якесь значення.
https://github.com/marcuswestin/store.js/isissue/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];
до return true
. Тоді у вас є функція, яка безпечно повертає значення true чи false в залежності від доступності localStorage. Наприклад:if (isLocalStorageNameSupported()) { /* You can use localStorage.setItem */ } else { /* you can't use localStorage.setItem */ }
Виправлення, розміщене на вище посиланні, не працювало для мене. Це зробило:
function isLocalStorageNameSupported() {
var testKey = 'test', storage = window.localStorage;
try {
storage.setItem(testKey, '1');
storage.removeItem(testKey);
return true;
} catch (error) {
return false;
}
}
Отримано з http://m.cg/post/13095478393/detect-private-browsing-mode-in-mobile-safari-on-ios5
window.sessionStorage
це правильно. Це, безумовно, працює в моєму коді. Фактично вкажіть виправлення проблеми, про яку, здається, знаєте.
isLocalStorageNameSupported
і перевіряв window.sessionStorage
. Кінцевий результат, але трохи заплутаний. Відповідь було відредаговано для уточнення.
Як згадується в інших відповідях, ви завжди отримаєте QuotaExceededError в режимі приватного браузера Safari для iOS та OS X, коли localStorage.setItem
(або sessionStorage.setItem
) викликається.
Одне рішення - зробити спробу / ловити або перевірити Modernizr у кожному випадку використання setItem
.
Однак, якщо ви хочете, щоб ця лайка просто глобально зупиняла скидання цієї помилки, щоб запобігти порушенню решти JavaScript, ви можете скористатися цим:
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.');
}
}
У моєму контексті щойно розвинулася класова абстракція. Коли моя програма запускається, я перевіряю, чи працює localStorage, зателефонувавши на getStorage () . Ця функція також повертає:
У своєму коді я ніколи не телефоную localStorage безпосередньо. Я називаю cusSto global var, я ініціалізувався, викликавши getStorage () .
Таким чином, це працює з приватним переглядом або певними версіями 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();
Здається, що Safari 11 змінює поведінку, і тепер локальне зберігання працює у вікні приватного браузера. Ура!
Наш веб-додаток, який раніше не працював у приватному перегляді Safari, зараз працює бездоганно. Це завжди добре працювало в режимі приватного перегляду Chrome, що завжди дозволяло записувати в локальний сховище.
Це задокументовано в примітках до випуску Safari Technology Preview від Apple - та примітках до випуску WebKit до випуску - до випуску 29, який відбувся у травні 2017 року.
Конкретно:
Щоб розширити відповіді інших людей, ось компактне рішення, яке не відкриває / не додає нових змінних. Він охоплює не всі бази, але він повинен підходити для більшості людей, які просто хочуть, щоб додаток на одній сторінці залишався функціональним (незважаючи на відсутність збереження даних після перезавантаження).
(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 = {};
};
}
})();
У мене була така ж проблема з використанням Ionic Framework (Angular + Cordova). Я знаю, що це не вирішує проблему, але це код для Angular Apps на основі відповідей вище. У вас буде ефемерне рішення для localStorage в iOS версії Safari.
Ось код:
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);
}
}
}
]);
Джерело: https://gist.github.com/jorgecasar/61fda6590dc2bb17e871
Насолоджуйтесь кодуванням!
Ось рішення для AngularJS з використанням IIFE та використання факту, що послуги одинакові .
Це призводить до isLocalStorageAvailable
встановлення негайно, коли послуга вводиться вперше, і уникає зайвого запуску чеку кожного разу, коли потрібно отримати доступ до місцевого зберігання.
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');
}
};
}
]);
Я просто створив це репо, щоб забезпечити sessionStorage
іlocalStorage
функції для непідтримуваних або відключених браузерів.
Підтримувані веб-переглядачі
Як це працює
Він визначає функцію з типом зберігання.
function(type) {
var testKey = '__isSupported',
storage = window[type];
try {
storage.setItem(testKey, '1');
storage.removeItem(testKey);
return true;
} catch (error) {
return false;
}
};
Набори StorageService.localStorage
для , window.localStorage
якщо вона підтримується або створює сховище печива. Набори StorageService.sessionStorage
для , window.sessionStorage
якщо вона підтримується або створює в пам'яті зберігання для SPA, зберігання печива з особливостями Sesion для відсутності SPA.
Ось варіант служби Angular2 + для зберігання пам’яті, який ви можете просто ввести у свої компоненти, грунтуючись на відповіді П’єра Ле Ру.
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);
}
}
обмін в Es6 повноцінним читанням і записом LocalStorage Прикладу з перевіркою підтримки
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);
Це забезпечить правильне встановлення та відновлення ваших ключів у всіх браузерах.
Я створив виправлення для проблеми. Просто я перевіряю, чи підтримує браузер localStorage або sessionStorage чи ні. Якщо ні, то механізмом зберігання буде Cookie. Але негативна сторона полягає в тому, що у Cookie є дуже крихітна пам'ять :(
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')
Прийнята відповідь здається неадекватною у кількох ситуаціях.
Щоб перевірити, чи підтримується localStorage
або sessionStorage
, я використовую наступний фрагмент від 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);
}
}
Використовуйте такий фрагмент, як цей, і запасний файл, наприклад, за допомогою файлу cookie:
if (storageAvailable('localStorage')) {
// Yippee! We can use localStorage awesomeness
}
else {
// Too bad, no localStorage for us
document.cookie = key + "=" + encodeURIComponent(value) + expires + "; path=/";
}
Я створив пакет backbackstorage, який використовує цей фрагмент, щоб перевірити наявність пам’яті та резервне копіювання для вручну реалізованої пам’яті MemoryStorage.
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;
}
Наступний сценарій вирішив мою проблему:
// 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();
}
Він перевіряє, чи існує LocalStorage і чи можна його використовувати, і в негативному випадку він створює підроблений локальний сховище та використовує його замість оригінального localStorage. Будь ласка, повідомте мене, якщо вам потрібна додаткова інформація.