私のwebappには、ios safariプライベートブラウジングでjavascriptエラーがあります:
JavaScript:エラー
undefined
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
(またはsessionStorage.setItem
)が呼び出されると、iOSとOS Xの両方でSafariプライベートブラウザーモードで常にQuotaExceededErrorが発生します。
1つの解決策は、setItem
を使用する各インスタンスでtry/catchまたは Modernizr check を実行することです。
ただし、このエラーがスローされるのを単にグローバルに停止するシムが必要な場合は、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 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();
他の人の答えを拡張するために、新しい変数を公開/追加しないコンパクトなソリューションを以下に示します。すべてのベースを網羅しているわけではありませんが、単一ページのアプリが機能し続けることを望むほとんどの人に適しているはずです(リロード後のデータの永続性はありません)。
(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)を使用しても同じ問題が発生しました。これで問題が解決しないことはわかっていますが、上記の回答に基づくAngular Appsのコードです。 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');
}
};
}
]);
Safari 11が動作を変更し、ローカルストレージがプライベートブラウザウィンドウで機能するようになったようです。やった!
Safariのプライベートブラウジングで失敗していたWebアプリは、今では問題なく動作します。 Chromeのプライベートブラウジングモードでは常に正常に機能し、常にローカルストレージへの書き込みが許可されています。
これは、Appleの Safari Technology Previewリリースノート -および WebKitリリースノート -2017年5月のリリース29に記載されています。
具体的には:
サポートされていないブラウザまたは無効なブラウザにsessionStorage
およびlocalStorage
機能を提供するために、この repo を作成しました。
サポートされているブラウザ
仕組み
ストレージタイプの機能を検出します。
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
に設定するか、Cookieストレージを作成します。サポートされている場合はStorageService.sessionStorage
をwindow.sessionStorage
に設定するか、SPAのメモリストレージ、非SPAのセッション機能を備えたCookieストレージを作成します。
es6での共有読み取りと書き込みサポートlocalcheckの例
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);
これにより、すべてのブラウザでキーが適切に設定および取得されるようになります。
これは、メモリストレージの代替として使用できるAngular2 +サービスバージョンです。PierreLe 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);
}
}
var mod = 'test';
try {
sessionStorage.setItem(mod, mod);
sessionStorage.removeItem(mod);
return true;
} catch (e) {
return false;
}