Реализуйте простую и мощную функцию в JavaScript, основанную на объекте окна браузера, который называется postMessage.

Привет народ! В этом посте мы собираемся изучить и реализовать одну из самых простых, но мощных функций в JavaScript, полученных из объекта окна браузера, который называется postMessage. Прежде чем исследовать это дальше, позвольте мне представить пример использования, в котором эта прекрасная функция очень полезна.

Вариант использования

Предположим, что после того, как пользователь входит в ваше приложение (например, example.com) и остается бездействующим (для простоты давайте будем считать пользователя бездействующим, когда вызов API не запущен) непрерывно в течение 10 минут, мы хотим, чтобы пользователь принудительно вышел из системы, иначе мы можем продолжить сессию как обычно.

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

Теперь, после постоянной активности (вызовы API) на новой вкладке, пользователь выходит из исходной вкладки через 10 минут, и, следовательно, сеанс истекает и на других открытых вкладках.

Короче говоря, активность на вновь открытой вкладке не считается активностью на исходной вкладке/приложении. Однако каким-то образом исходная вкладка не уведомляется о действиях, выполняемых на новой вкладке.

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

Метод window.postMessage() безопасно обеспечивает связь между Window объектами из разных источников; например, между страницей и всплывающим окном, которое она породила, или между страницей и встроенным в нее iframe.

синтаксис:
postMessage(сообщение, targetOrigin)

Отправитель активности: недавно открытая вкладка

Мы собираемся использовать window.opener для получения postMessageметода. Итак, мы можем определить, как показано ниже:

const winRef = window.opener;
// NOTE: if you want to communicate from an iframe and winRef is coming as undefined, try below-
// const winRef = top.window.opener;

Теперь, когда у нас есть объект ссылки на окно, мы можем использовать функцию postMessage для отправки данных:

winRef.postMessage("hello from nTab!", "http://example.com");
// where, http://example.com is targetOrigin where the message will be sent, our original tab (lets call it oTab)

Обратите внимание, что предоставление определенного targetOrigin очень важно по соображениям безопасности, так как postMessage отправит сообщение на все открытые вкладки в браузере, и поэтому любой может прослушать отправленные данные (не так безопасно!).

В нашем случае мы открыли nTab из окна oTab, поэтому мы также можем получить значение targetOrigin из documentobject:

const targetOrigin  = document.referrer;
winRef.postMessage("hello from nTab!", targetOrigin);

Теперь наша цель — узнать, произошло ли действие на новой вкладке (назовем ее nTab с доменом random.com). Поскольку мы считаем, что пользователь активен только при запуске API, нам необходимо перехватывать любой вызов API, происходящий на nTab. Мы можем добиться этого, используя XMLHttpRequest, как показано ниже:

XMLHttpRequest.prototype.send = function() { 
// intercept the request here 
}

Итак, теперь мы можем создать счетчик, сбрасывать его всякий раз, когда API запускается, и
затем мы можем проверить в setInterval, сбрасывается ли значение счетчика, чтобы отправлять отчет об активности в oTab каждый интервал, скажем, 30 секунд, а затем увеличивать счетчик. . Вот код:

const winRef = window.opener;
const targetOrigin  = document.referrer;
const counter = 0;
setInterval(function() {
    winRef.postMessage({tab: "nTab", isActive: counter === 0}, targetOrigin);
   couter++;
  }, 30000);
XMLHttpRequest.prototype.send = function() { 
   counter = 0; 
}

Приемник активности: исходная вкладка

Теперь на нашей исходной вкладке oTab мы можем непрерывно прослушивать события из nTab и выполнять необходимые действия:

window.addEventListener("message", (event) => {
  if (event.origin === "http://random.com") {
      if (event.data.isActive) {
         // activity happened in nTab
  }
 }    
}, false);

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

const winRef = window.opener;
const counter = 0;
setInterval(function() {
  window.addEventListener("message", (event) => {
      if (event.origin === "http://random.com" && event.data.isActive) {
         counter = 0;
   } else {
      couter++;
     }   
}, false);
  }, 60000);
XMLHttpRequest.prototype.send = function() { 
   counter = 0;
}

Теперь, основываясь на значении счетчика, мы можем выйти из системы пользователя или возобновить/продолжить сеанс.

if(counter > 9) { // 10min 
   window.location.href = '/logout'; 
   // we can also ask the user to re-login via a popup
  } else { 
      // session continues 
  }

Теперь у нас есть решение нашей проблемы!

Бонусный раздел: идентификация недавно открытых вкладок с помощью ссылки window.open

Когда мы открываем ссылку из oTab, мы, скорее всего, используем метод window.open. Таким образом, мы можем сравнить эту ссылку с полученным источником event и идентифицировать только что открытую вкладку. Итак, теперь код нашего получателя выглядит так:

document.getElementsByClassName('goto-nTab').onclick = function () {
        const ref = window.open('http://random.com');
    }

Теперь мы можем сравнить ref и event.source, чтобы определить, что это nTab. Здесь:

const winRef = window.opener;
const counter = 0;
let ref = null;
document.getElementsByClassName('goto-nTab').onclick = function () {
        ref = window.open('http://random.com');
    }
setInterval(function() {
  window.addEventListener("message", (event) => {
      if (ref == event.source && event.data.isActive) {
         counter = 0;
   } else {
      couter++;
     }   
}, false);
  }, 60000);
XMLHttpRequest.prototype.send = function() { 
   counter = 0;
}

Вы также можете дополнительно изучить windowobject, чтобы глубже изучить и раскрыть то, что он содержит (используйте отладчики для помощи!).

Спасибо за прочтение! Если у вас есть какие-либо проблемы/предложения, пожалуйста, добавьте их в комментариях.

Больше контента на plainenglish.io. Подпишитесь на нашу бесплатную еженедельную рассылку новостей. Получите эксклюзивный доступ к возможностям написания и советам в нашем сообществе Discord.