Как сохранить / сохранить событие JavaScript и использовать его позже? - PullRequest
0 голосов
/ 22 мая 2018

С 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;
});

Этот код в порядке, но я хочу вызвать спрятанное событие позже, в другом месте.Для этого мне нужно хранить событие не только в переменной, но и где-то еще.

Вопрос: как можно сохранить событие с его методами?

Я пытался использовать Local Storage ссериализация / десериализация объекта:

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

Но этот подход не работает должным образом, поскольку он хранит только значения ключей и теряет все методы событий.

Ответы [ 6 ]

0 голосов
/ 31 мая 2018

После прочтения вашего вопроса несколько раз и ответов еще несколько раз,

Вопрос: как можно сохранить любой объект javascript с его методами?

Ответ: нет, как.

Однако,

Джош правильно объяснил вы можетеизвлеките и сохраните все сериализуемые свойства, скажем, данные, из вашего события.

Я просто добавлю, что может создать событие с каким-либо образом из этих же данных. позже, где бы то ни было, это новое событие будет иметь все методы, которые есть у Event , но к настоящему времени, вероятно, ни к чему не применим.

Очевидно, что даже в ваших данных сериализуются такие свойства, как timeStamp , isTrusted и т. д. ... будут переопределены при создании нового события.

То, что вам просто не хватает / нужно, это EventTarget ,значение свойства event.target , ссылка , которая теряется навсегда при выгрузке document.bodyнавсегда, или при сериализации объекта события.

Но если он все еще жив, или если вы знаете, каким должно быть событие event.target, например элемент DOM или любой объект , вы можете ссылаться на ,откуда бы вы ни воссоздали событие (где?), просто отправьте ваше событие этому объекту, если оно прослушивает этот event.type , ваше новое событие должно быть как минимум услышано.

Простой пример из MDN EventTarget или см. EventTarget.dispatchEvent

Как комментарий к расширенному ответу от cegfault : eval и исходный текстовый код ... может быть исходный текстовый код ... если вашскрипт производит (String) скрипт.Если нет, то вам, вероятно, лучше пойти дальше назад, туда, где ваш сценарий создает несериализуемые вещи, которые появляются в вашем событии, и подумать о воссоздании этих вещей, а не события.

0 голосов
/ 31 мая 2018

TL; DR, чтобы выполнить то, что вы делаете, у вас есть три варианта:

  1. Сохранение ссылки на событие в глобальном значении (что большинство учебных пособий -как ваше ссылочное видео на YouTube - рекомендую вам сделать).Это требует, чтобы событие запускалось в том же контексте (т. Е. Веб-странице), что и при сохранении ссылки
  2. Когда вы сохраняете ссылку на событие в localStorage (например, по имени илипоиск ключа / значения), на странице / контексте, где вы хотите выполнить событие, убедитесь, что соответствующие функции и библиотеки загружены до выполнения события
  3. [настоятельно НЕ рекомендуется]Сохраните исходный код javascript в своем хранилище и eval () его позже [снова, пожалуйста, не делайте этого]

Как упоминали @Josh и @SaurabhRajpal, то, что вы просите, строго говоря, не возможно в JavaScript.То, что вы делаете с JSON.stringify(e), вероятно, вернет undefined или null, поскольку документация MDN для JSON.stringify говорит:

Если значение не определено, функция,или Символ встречается во время преобразования, он либо опускается (когда он найден в объекте), либо подвергается цензуре на ноль (когда он находится в массиве).JSON.stringify также может просто возвращать undefined при передаче «чистых» значений, таких как JSON.stringify (function () {}) или JSON.stringify (undefined).

Короче, тамневозможно сохранить одну функцию в localStorage (или любом другом автономном хранилище) .Чтобы объяснить, почему это невозможно, посмотрите этот пример:

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

function bar() {
    return foo()
}

Как вы можете хранить bar() для дальнейшего использования?Чтобы хранить бар, вы должны также хранить foo().Это становится намного сложнее, когда вы рассматриваете ссылку на функцию, которая находится в большой библиотеке или использует ее (например, jQuery, подчеркивание, D3, библиотеки диаграмм и т. Д.).Имейте в виду, ваш компьютер уже проанализировал исходный код в двоичном , и поэтому не может легко узнать, как читать функцию для всех возможных операторов if, for и switch, чтобы гарантировать все возможные коррелированные функции.и библиотеки сохраняются.

Если бы вы действительно хотели сделать это, вам нужно было бы написать свой собственный анализатор javascript, а вы действительно не хотели бы делатьэто!

Так, каковы ваши варианты?Сначала сделайте все на той же странице и сохраните ссылку на событие в глобальном значении (этот метод используется в видео на YouTube, на которое вы ссылаетесь в комментарии).

Второй вариант - использовать ссылка на событие (не на само событие), и убедитесь, что исходный код для этой ссылки используется позже.Для примера (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>

Наконец, вы можете сохранить исходный код 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 просто не работает таким образом.Лучше всего передавать ссылки в памяти и использовать только хранилище ключей / значений для имен.Все остальное - это разновидность гимнастики кодирования, которую следует избегать, просто включив исходный код в те места, где он не должен быть включен.

0 голосов
/ 30 мая 2018

Вы можете создать глобальную константу и обновлять ее при любых изменениях событий, а не сериализовать и десериализовать, что является дорогостоящим процессом.Так вот как вы можете это сделать - вы можете создать экземпляр окна и клонировать событие в объекте окна, чтобы оно не изменялось (обратите внимание, что это не будет работать на вкладках)

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 секунд

0 голосов
/ 25 мая 2018

Посмотрите, поможет ли это.

Определение прослушивателя событий для события beforeinstallprompt

window.addEventListener('beforeinstallprompt', (e) => {
  e.preventDefault();
  //do all the stufff
  console.log('Event triggered');
});

Когда вы хотите отправить событие вручную.Создайте новую переменную события.

var myevent = new Event('beforeinstallprompt');
window.dispatchEvent(myevent);

Выводит «Событие, инициированное» в консоли.

0 голосов
/ 25 мая 2018

Вы не можете сохранить событие таким способом.Вы хотите сохранить объект.Только сериализуемые свойства могут быть сохранены для такого объекта.Функции не сериализуются в JavaScript.Функции не сериализуются во многих языках.

По сути, это в основном потому, что при десериализации объекта его сигнатура может измениться.Если вы когда-либо программировали на Java, это похоже на ошибку десериализации при чтении в сериализованном объекте и попытке восстановить объект.Поскольку тело метода-функции объекта может изменяться в промежутке времени между записью объекта в какое-либо хранилище и последующим чтением, методы не сериализуются.Это связано с тем, что при сериализации объекта он не сериализует определение интерфейса, в котором определены методы.Он просто хранит данные.

По той же причине, когда вы сериализуетесь в строку json, он удаляет функции.

Вместо сохранения события сохраняйте полезную информацию из события в объекте (илипусть вещи неявно отбрасываются с помощью stringify и напрямую используют событие).

Какой метод хранения вы используете, зависит только от вещей, не упомянутых в вашем вопросе.Например, как долго он должен храниться, должен ли он быть доступен за пределами источника вашего сайта, сколько данных обычно будет храниться, есть ли более одного объекта для хранения и т. Д. На основании ограниченной информации, представленной в вашем вопросе,Вы, вероятно, в порядке, просто используя localStorage или массив в памяти.

Если вы обнаружите необходимость хранить сотни объектов, тогда indexedDB станет более подходящим.Но выбор другого носителя данных никак не повлияет на возможность хранения функций.Вы не можете хранить функции.

0 голосов
/ 23 мая 2018

Вокруг этого было много разговоров, как только I / O 2018 упомянул об обработке события A2HS, которое теперь зависит от разработчика.Это также отражено в официальном документе и вдохновлено этим, есть красивая статья , подробно объясняющая, как именно достичь этого сценария.Несмотря на то, что я бы посоветовал ознакомиться с полной статьей для правильного понимания обновленной динамики потока A2HS, не стесняйтесь перейти к разделу «Новый поток добавления на домашний экран» для своих требований.

ВВ двух словах, выполните следующие действия:

  1. Создайте переменную вне области действия обработчика события beforeinstallprompt.
  2. Сохраните ссылку на объект события beforeinstallprompt в вышеупомянутом обработчике.
  3. Используйте это позже, чтобы вызвать запрос на добавление к домашнему экрану по требованию.

Статья содержит полные фрагменты кода, которые вы можете ссылаться / использовать повторно.

Edit : Я прочитал ваш вопрос еще раз и понял один важный аспект, который вы, возможно, специально искали, а именно, использование его «где-то еще».Если это означает, что вы имеете в виду использовать его на другой странице, то я бы предложил сохранить объект события в:

  1. IndexedDB, который является коллекцией «хранилищ объектов», которые вы можетепросто бросьте объекты в. Недостаток - Может иметь ограничения совместимости браузера.Кроме того, это может привести к большому количеству вложенных обратных вызовов.
  2. Или вы можете использовать «внутрипроцессный кэш» (куча памяти вашего приложения), который также не требует сериализации. Недостаток - Это нельзя использовать для нескольких серверов.

Кроме этого, я не могу предвидеть бесплатное решение в данный момент.Но постараюсь разобраться и возможно обновить ветку.

...