如何監聽頁面關閉或重新整理動作

fengxianqi發表於2019-03-04

原文:如何監聽頁面關閉或重新整理動作-fengxianqi

其實這篇文章也可以叫如何監聽並上傳使用者觀看的視訊時長。最近有需求做到,監聽使用者播放視訊的動作並上報播放事件,需要上報的是使用者觀看視訊的時長。這裡就有幾種情況了,一是使用者點選播放,然後直到視訊播放完畢,觸發video.ended事件,此時上報使用者的觀看時長,這個比較好處理。第二種情況是使用者點選播放後離開或重新整理頁面,在使用者離開前需要把事件上報,這裡主要會觸發window.onunload事件,看起來可以做到,其實有坑。

由於公司採用的技術方案是.m3u8(hls),而這種格式目前PC瀏覽器只有Safari才支援,chrome、Firefox或移動端安卓其他的大部分瀏覽器都不支援播放這種格式。因此,用原生video標籤的方案是不行了,得找一些外掛或播放器。最終選擇了DPlayer這個播放器,一個比較不錯的開源播放器,簡單易用。
說回正事,上報使用者觀看視訊的時長。

重要更新

經評論區 @音客 指點,使用Navigator.sendBeacon()將會更好的支援上報事件,這是一個專門處理資料埋點的api,但是使用時請注意瀏覽器相容性問題,目前(2018.06)移動端Android 5以上支援,ios需要11.3版本以上,具體請看CanIUse
本篇使用new Image()方法可以作為sendBeacon()的不相容時的降級處理方法,更多請參考:使用 navigator.sendBeacon() 上報資料

目標

使用者觀看視訊結束時上報觀看時長。

準備工作

    const dp = new DPlayer({
        container: document.getElementById(`dplayer`),
        screenshot: true,
        video: {
            url: `xx.m3u8`,
            pic: `xx.jpg`,
            type: `customHls`,
            customType: {
                `customHls`: function (video, player) {
                    const hls = new Hls();
                    hls.loadSource(video.src);
                    hls.attachMedia(video);
                }
            }
        }
    });

    // 是否播放的標記
    var isPlay = false;
    dp.on(`play`, function () {
        console.log(`player start`);
        isPlay = true;
    });
複製程式碼

第一種常規的情況,使用者完整地看完了視訊。

    dp.on(`ended`, function () {
      console.log(`player ended`);
      sendVideoPlayEvent(); //上報事件方法
      isPlay = false;
    });
複製程式碼

第二種情況,關閉瀏覽器或重新整理頁面

使用者關閉頁面或重新整理瀏覽器時會觸發onunload事件,使用者開始觀看視訊到離開頁面,這一段時間就是使用者觀看視訊的時長。

    window.onunload = function () {
      sendVideoPlayEvent();
      isPlay = false;
      console.log(`onunload`);
    };
複製程式碼

坑點

經測試發現,sendVideoPlayEvent時,如果採用ajax,無論是post或get方式,將無效。這裡的原因可能是頁面關閉太快,ajax無法執行。
查了很多解決方案,最後發現,需要通過巧妙的方法來發起這個事件。就是利用img標籤的src屬性,將img插入到body中時會發起一個請求來獲取資源,而服務端可以對這個路由進行處理,最後發現這種方法是可行的,成功地在onunload中上報事件了。

    function sendVideoPlayEvent() {
      if (isPlay) {
        var videoId = getQueryString(`videoId`);
        var duration = dp.video.currentTime; // 視訊播放時間可以直接通過currentTime獲得
        var img = new Image();
        img.style.display = `none`;
        img.src = `/api/video/play?duration=${duration}&videoId=${videoId}`; // 服務端處理介面
        document.body.appendChild(img);
      }
    }
    function getQueryString(name) {
        var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i");
        var r = window.location.search.substr(1).match(reg);
        if (r != null) return unescape(r[2]); return null;
    }
複製程式碼

總結

  1. 後來想想,script標籤的src應該也可以實現,沒去驗證。
  2. 坑總是很多,多動腦筋。。。
  3. 思路有點侷限,應該還有其他解決方案,歡迎建議。

相關文章