CSS & JS Effect – Do something on enter/leave window tab

需求

我在做一個體驗

當用戶 submit enquiry 後會 window.open 開啓 WhatsApp。而當用戶關閉 WhatsApp 回來網站後,會 show 一個 feedback message。

 

實現思路

關鍵就在如何感知到,用戶從 WhatsApp 切換回到了網站。

參考: Detect When Users Switch Tabs using JavaScript

監聽 visibilitychange,然後通過 document.visibilityState 得知是 hidden or visible。

hidden 表示 leave tab,visible 表示 back to tab。

 

Android 鬼

本來是很簡單的東西,但不知道是不是我手機的問題。Android 在開啓 WhatsApp 的時候會觸發 2 次。

Windows:open WhatsApp > hidden > close WhatsApp > visible

Android:open WhatsApp > hidden > visible > hidden > close WhatsApp > visible

顯然 Windows 是正常的,Android 跑了多一次。

Workaround 就是儘可能判斷出 Android 的情況,不要被它搞亂。我沒有太認真去思路 right way,我覺得多半是一個 bug 來的,所以暫時解決就好了。

參考代碼註釋理解吧

const beforeWindowOpenTime = performance.now();
window.open(generateWhatsAppLink(whatsAppNumberBS.value, message), '_blank');

// note 解憂:
// 這裏有一個 user back from WhatsApp 後 show submitted feedback 的體驗。
// 它比較複雜,因爲手機有 bug,
// 正常情況下 visibilitychange 會 dispatch when user leave/enter tab,離開 state = hidden,回來 state = visible。
// so when window open WhatsApp, dispatch lever tab 咯, when user close WhatsApp, dispatch enter tab 咯.
// 但是我的 android 有鬼。
// when window open WhatsApp,它會先 leave + enter 1 次, 緊接着又 leave 1 次, 直到 user back
// 所以下面代碼需要顧慮它第一輪假的 leave + enter
const [visible$, hidden$] = partition(
  fromEvent(window, 'visibilitychange').pipe(share()),
  () => document.visibilityState === 'visible',
);

// 監聽第一次 visible
visible$.pipe(take(1)).subscribe(() => {
  const now = performance.now();
  // 如果第一次 visible 超過 1 秒
  if (now - beforeWindowOpenTime >= 1000) {
    // 直接 show feedback,因爲 android 鬼是很快的,絕對不會超過 1 秒
    showSubmittedFeedback();
    return;
  }

  // 如果小於 1 秒,那可能是 android 鬼,或者 user 真的很快就 close WhatsApp
  // 我們嘗試監聽 hidden,如果是 android 鬼,那麼 2 秒內會觸發 hidden
  // 如果 2 秒後沒有 hidden 那判定是 user close 很快。
  hidden$.pipe(take(1), timeout({ first: 2000 })).subscribe({
    next() {
      // android 鬼的話,等待下一次的 visible 表示 user close
      visible$.pipe(take(1)).subscribe(() => showSubmittedFeedback());
    },
    error() {
      // user close 的話,直接 show feedback
      showSubmittedFeedback();
    },
  });

  function showSubmittedFeedback() {
    setTimeout(() => {
      submitFeedbackReCaptchaContainer.submitFeedbackReCaptchaContainerController.showSubmittedFeedback();
    }, 1000);
  }
});

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章