web-dev-qa-db-ja.com

JavaScriptイベントを保存/隠し、後で再利用するにはどうすればよいですか?

から https://developers.google.com/web/fundamentals/app-install-banners/#trigger-m68

let deferredPrompt;

window.addEventListener('beforeinstallprompt', (e) => {
  e.preventDefault();
  // Stash the event so it can be triggered later.
  deferredPrompt = e;
});

このコードは問題ありませんが、後で別の場所で隠しイベントをトリガーしたいと思います。これを実行するには、イベントを変数だけでなく、別の場所に格納する必要があります。

質問:イベントをそのメソッドとともに保存するにはどうすればよいですか?

オブジェクトのシリアル化/逆シリアル化を使用してローカルストレージを試しました。

> localStorage.setItem('stashed-event', JSON.stringify(e))
>
> JSON.parse(localStorage.getItem('stashed-event'))

ただし、このアプローチは、キー値のみを格納し、すべてのイベントメソッドを失うため、期待どおりに機能しません。

14
Limon Monte

あなたの質問を数回読み、さらに数回答えた後、

質問:JavaScriptオブジェクトをそのメソッドとともにどのように保存できますか?

答え:方法はありません。

ただし、

Josh 適切に explained イベントからすべてのシリアル化可能なプロパティ(データなど)を抽出して保存できます。

追加しますイベントを作成できますなんとかして同じデータが後でどこにでもある場合、この新しいイベントには、 Event が持つすべてのメソッドが含まれますが、現在はおそらく使用されていません。

明らかに、データでシリアル化されていても、 timeStampisTrusted などのプロパティは新しいイベントの作成時にオーバーライドされます。

見逃している/必要なのは EventTargetevent.targetプロパティの値、referenceこれは、document.bodyが永久にアンロードされるとき、またはイベントObjectをシリアル化するときに永久に失われます。

しかし、それがまだ生きている場合、またはDOM要素やオブジェクトのようにevent.targetがどうあるべきかを知っている場合は、どこからでも参照できますイベントを再作成し(どこ?)、そのオブジェクトにイベントをディスパッチするだけです。それがリッスンしている場合 event.type 、少なくとも新しいイベントが聞こえるはずです。

MDNからの簡単な例EventTarget 、または EventTarget.dispatchEvent を参照

広範な回答 by cegfaulteval、およびテキストソースコードに対するコメントとして。 。は<script>テキストソースコード</ script>である可能性があります。 ..スクリプトが(文字列)スクリプトを生成する必要があります。そうでない場合は、スクリプトがイベントに表示されるシリアル化できないものを作成した場所に戻って、イベントではなくそれらを再作成することを検討することをお勧めします。

4
lucchi

この方法でイベントを保存することはできません。オブジェクトを保存したい。このようなオブジェクトでは、シリアル化可能なプロパティのみを保存できます。関数はJavaScriptでシリアル化できません。関数は多くの言語でシリアル化できません。

基本的に、これは基本的に、オブジェクトを逆シリアル化すると、その署名が変更される可能性があるためです。 Javaでプログラミングしたことがある場合、これは、シリアル化されたオブジェクトを読み込んでオブジェクトを再構築しようとしたときの逆シリアル化エラーに似ています。オブジェクトのメソッド関数の本体は、オブジェクトが何らかのストレージに書き込まれてから後で読み取られるまでの間に変更される可能性があるため、メソッドはシリアル化できません。これは、オブジェクトをシリアル化するときに、メソッドが定義されているインターフェイス定義がシリアル化されないためです。データを保存するだけです。

Json文字列にシリアル化する場合も同じ理由で、関数が削除されます。

イベントを格納する代わりに、イベントからの有用な情報をオブジェクトに格納します(または、stringifyによって暗黙的にドロップし、イベントを直接使用します)。

どのストレージ方法を使用するかは、質問に記載されていないものによって異なります。保存期間、サイトのOriginの外部で利用できるかどうか、通常保存されるデータの量、保存するオブジェクトが複数あるかどうかなど。質問で提供された限られた情報に基づいて、 localStorageまたはメモリ内配列のいずれかを使用するだけでおそらく問題ありません。

何百ものオブジェクトを格納する必要がある場合は、indexedDBの方が適切になります。ただし、別の記憶媒体を選択するだけでは、関数を保存できるかどうかにはまったく影響しません。関数を保存することはできません。

9
Josh

I/O 2018が、今後開発者主導のA2HSイベントの処理について言及するとすぐに、これについて多くの話がありました。これも 公式ドキュメント に記録されており、そこからインスピレーションを得て、 美しい記事 このシナリオを正確に実現する方法を徹底的に説明しています。 A2HSフローの周りの更新されたダイナミクスを適切に理解するために完全な記事を読むことをお勧めしますが、要件については「新しいホーム画面への追加フロー」セクションにジャンプしてください。

簡単に言うと、次の手順に従います。

  1. beforeinstallpromptイベントハンドラーのスコープ外に変数を作成します。
  2. 上記のハンドラーにbeforeinstallpromptイベントオブジェクトへの参照を保存します。
  3. 後でこれを使用して、ホーム画面への追加プロンプトをオンデマンドでトリガーします。

この記事には、参照/再利用できる完全なコードスニペットが含まれています。

編集:私はあなたの質問をもう一度読み、あなたが特に探しているかもしれない1つの重要な側面、つまり「どこか別の場所」でそれを使用していることに気づきました。これが別のページでの使用を参照していることを意味する場合、私の提案は、イベントオブジェクトを次の場所に格納することです。

  1. オブジェクトをドロップするだけの「オブジェクトストア」のコレクションであるIndexedDB。 短所-ブラウザの互換性を制限することができます。また、ネストされたコールバックが大量に発生する可能性があります。
  2. または、シリアル化を必要としない「インプロセスキャッシュ」(アプリケーションのヒープメモリ)を使用することもできます。 短所-ただし、これを複数のサーバー間で共有することはできません。

これ以外に、現時点ではコンフリーソリューションを予測することはできません。しかし、それを理解し、おそらくスレッドを更新しようとします。

8
Saurabh Rajpal

TL; DRを実行して実行していることを実行するには、次の3つのオプションがあります。

  1. referenceをグローバル値でイベントに保存します(これは、ほとんどのチュートリアル(参照されているYouTubeビデオなど)が推奨するものです)。これには、参照を保存するときと同じコンテキスト(つまり、Webページ)でイベントを実行する必要があります
  2. referenceをlocalStorageのイベントに保存する場合(名前やキー/値のルックアップなど)、イベントを実行するページ/コンテキストで、適切な関数とライブラリがロードされますイベントの実行
  3. [強くお勧めしません] javascriptソースコードをstorateに保存し、後でeval()します[繰り返しますが、これは行わないでください]

@Joshと@SaurabhRajpalが述べたように、厳密に言えば、JavaScriptでは要求していることは不可能です。 JSON.stringifyのMDNドキュメント にあるように、JSON.stringify(e)で行っていることはおそらくundefinedまたはnullを返します。

未定義の場合、変換中に関数またはシンボルが検出されると、省略されるか(オブジェクトで見つかった場合)、nullに打ち切られます(配列で見つかった場合)。 JSON.stringifyは、JSON.stringify(function(){})やJSON.stringify(undefined)などの「純粋な」値を渡すときに、undefinedを返すこともできます。

つまり、単一の関数をlocalStorage(またはその他のオフラインストレージ)に格納する方法はありません。これが不可能な理由を説明するには、次の例を参照してください。

_function foo() {
    console.log("a")
}

function bar() {
    return foo()
}
_

後で使用するためにbar()をどのように保存できますか?バーを保存するには、alsofoo()を保存する必要があります。大規模なライブラリ(jQuery、アンダースコア、D3、チャートライブラリなど)にある、またはそれを使用する関数を参照することを検討すると、これははるかに複雑になります。 コンピュータはすでにソースコードをバイナリに解析しているので、次の場合に関数を読み取る方法を簡単に知ることはできません。 、for、およびswitchステートメントを使用して、可能なすべての相関関数とライブラリが確実に保存されるようにします。

reallyこれを実行したい場合は、独自のjavascriptパーサーを作成する必要があり、reallyは実行したくないです!

それで、あなたの選択肢は何ですか?まず、同じページですべてを実行し、イベントへの参照をグローバル値に保存します(コメントでリンクするYouTubeビデオはこの方法を使用しています)。

2番目のオプションは、(イベント自体ではなく)イベントに対してreferenceを使用し、その参照のソースコードが後で使用されることを確認することです。 (html)の例の場合:

_// on page #1
<script src="path/to/my/js/library.js"></script>
...
<script>
    window.addEventListener('beforeinstallprompt', (e) => {
        e.preventDefault()
        localStorage.setItem('stashed-event', 'before-install')
    })
</script>

// later, on page #2:
<script src="path/to/my/js/library.js"></script>
...
<script>
    var evt = localStorage.setItem('stashed-event', 'before-install')
    if(evt == 'before-install') {
        dosomething() // which would be a function in path/to/my/js/library.js
    }
    // another option here would be to define window listeners for all possible events
    // in your library.js file, and then simply build and trigger the event here. for
    // more about this, see: this link:
    // https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Creating_and_triggering_events
</script>
_

最後に、can javascriptソースコードを保存し、後でeval()します。お願いします、これをしないでください。それは悪い習慣であり、非常に邪悪なことにつながる可能性があります。しかし、あなたが主張するならば:

_// on page #1
window.addEventListener('beforeinstallprompt', (e) => {
    e.preventDefault()
    SomeAjaxFunction("path/to/my/js/library.js", function(responseText) {
        localStorage.setItem('stashed-event', {
            name: 'before-install',
            call: 'beforeInstallFunction()',
            src:  responseText
        })
    })
})

// later, on page #2:
var evt = localStorage.setItem('stashed-event', 'before-install')
if(evt) {
    console.log("triggering event " + evt.name)
    eval(evt.src)
    eval(evt.call)
}
_

私が言ったように、これは本当に悪い考えですが、それはオプションです。

私見ですが、後のページ/アプリ/その他にライブラリやソースコードを含めないようにしていると思いますが、JavaScriptはこのようには機能しません。参照をメモリ内で受け渡し、名前にはキー/値ストレージのみを使用することをお勧めします。他のすべては、ソースコードを含める必要のある場所に単純に含めることを避けるための一種のコーディング体操です。

2
cegfault

グローバル定数を作成し、イベントが変更されるたびに更新することができます。これは、コストのかかるプロセスであるシリアル化および逆シリアル化ではありません。 SOこれがその方法です-ウィンドウインスタンスを作成し、イベントをウィンドウオブジェクトに複製して、変更されないようにすることができます(これはタブ間では機能しないことに注意してください)。

window.addEventListener('click', (e) => {
  e.preventDefault();
  window.deferredPrompt = Object.assign(e);//Don't mutate
});

let someOtherMethod = ()=>{
  console.log(window.deferredPrompt)
}

window.setInterval(someOtherMethod, 5000);

最後のウィンドウで5秒後にクリックして、5秒後に確認してください

1
karthik