私のウェブアプリでは、iOSサファリのプライベートブラウジングでJavaScriptエラーが発生します:
JavaScript:エラー
未定義
QUOTA_EXCEEDED_ERR:DOM例外22:ストレージに何かを追加しようとしました...
私のコード:
localStorage.setItem('test',1)
私のウェブアプリでは、iOSサファリのプライベートブラウジングでJavaScriptエラーが発生します:
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/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;
}
}
上記のリンクに投稿された修正は私には機能しませんでした。これはしました:
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から派生
localStorage.setItem
他の回答で述べたように、iOSとOS Xの両方で(またはsessionStorage.setItem
)が呼び出されると、SafariプライベートブラウザモードでQuotaExceededErrorが常に発生します。
1つの解決策は、を使用する各インスタンスでtry/catchまたは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.');
}
}
私の文脈では、クラスの抽象化を開発しました。アプリケーションを起動したら、 getStorage()を呼び出してlocalStorageが機能しているかどうかを確認します。この関数も戻ります:
私のコードでは、localStorageを直接呼び出すことはありません。cusStoグローバル変数を呼び出します。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のプライベートブラウジングで失敗していたWebアプリが、問題なく動作するようになりました。Chromeのプライベートブラウジングモードでは常に正常に機能し、ローカルストレージへの書き込みが常に許可されていました。
これは、2017年5月にリリースされたAppleのSafari Technology Previewリリースノート(およびWebKitリリースノート)に記載されています。
具体的には:
他の人の答えを拡張するために、これは新しい変数を公開/追加しないコンパクトなソリューションです。すべてのベースを網羅しているわけではありませんが、単一ページのアプリを機能させたままにしたいほとんどの人に適しているはずです(リロード後のデータの永続性はありません)。
(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フレームワーク(Angular + Cordova)を使用しても同じ問題が発生しました。これで問題が解決しないことはわかっていますが、上記の回答に基づくAngularAppsのコードです。iOSバージョンのSafariでのlocalStorageの一時的なソリューションがあります。
コードは次のとおりです。
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
コーディングをお楽しみください!
IIFEを使用し、サービスがシングルトンであるという事実を活用したAngularJSのソリューションを次に示します。
これにより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');
}
};
}
]);
受け入れられた答えは、いくつかの状況では適切ではないようです。
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=/";
}
このスニペットを使用してストレージの可用性を確認し、手動で実装されたMemoryStorageにフォールバックするfallbackstorageパッケージを作成しました。
import {getSafeStorage} from 'fallbackstorage'
getSafeStorage().setItem('test', '1') // always work
サポートされていない、または無効になっているブラウザに機能を提供するために、このリポジトリを作成しました。sessionStorage
localStorage
サポートされているブラウザ
使い方
ストレージタイプで機能を検出します。
function(type) {
var testKey = '__isSupported',
storage = window[type];
try {
storage.setItem(testKey, '1');
storage.removeItem(testKey);
return true;
} catch (error) {
return false;
}
};
サポートされているか、Cookieストレージを作成するかを設定StorageService.localStorage
します。サポートされているか、SPAのメモリ内ストレージ、非SPAのセッション機能を備えたCookieストレージを作成するかをwindow.localStorage
設定StorageService.sessionStorage
します。window.sessionStorage
これは、メモリストレージの代替としてAngular2 +サービスバージョンです。PierreLeRouxの回答に基づいて、コンポーネントに挿入するだけです。
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')
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の代わりに使用します。さらに詳しい情報が必要な場合はお知らせください。