Make writing a habit together! This is the fifth day of my participation in the “Gold Digging Day New Plan · April More text Challenge”. Click here for more details.

The status quo

In order to accurately analyze the actual attractiveness of each front-end page to users, the exposure data of page elements need to be counted. The meaning of exposure is vague, and the specific statistical method is troublesome. This paper shares an implementation scheme of front-end exposure burying point reporting.

plan

In order to count exposure data, the first step is to define what exposure is and then develop a strategy for reporting it.

According to the actual situation of our business, we can design as follows:

  1. Exposure definition: A DOM element is exposed only when it appears in a screen window and is seen by the user for more than 500ms. Dom elements exit the window and re-enter the window, remaining for another 500ms, which is recorded as the second exposure.
  2. Data reporting: Minimize the number of reports. (1) The timer checks every N seconds and requests the interface to report the data to be reported. (2) If the number of data to be reported exceeds M, the interface directly reports the data without waiting N seconds.

Start operation

The overall implementation

The specific code is as follows:

  1. IntersectionObserver was used to observe whether IntersectionObserver appeared and disappeared in the window, and IntersectionObserver polyfill was used to improve compatibility.
  2. Use vUE instructions to achieve the binding of reported data. In the final use, only need to add V-Treport = “reported data” for the elements to be reported.
  3. During instruction binding, dom elements are bound with report-data and GUID attributes, whose specific values are the data to be reported and the unique ID respectively.
  4. The logic of specific observations and reported exposures is described below.

use

Element after binding directive:

Specific details

Element X enters the window

  1. Element X enters the window and logs to the to-observe queue of sessionStorage (if it already exists, it does not join the queue).
  2. {stime: observed time, ID: GUID, data: data to be reported,hasObserve:false}

Element X exits the window

  1. Obtain the stime of X from the to-observe queue, if (current time-stime)>=500ms and hasObserve is false, push the data of X to the to-report queue of localStorage (with localStorage, browser closed, Next time, you can also report the unreported to-report.
  2. In either case, element X pushes out the to-observe queue.

Exposure timer (check every 500ms)

  1. If X in the To-observe queue (current time-stime)>=500ms, hasObserve is set to true, and the data of the X element is added to the to-report queue

Report timer (check every 1000ms)

  1. If a record exists in the to-Report queue, it is reported and removed from the to-Report queue.
  2. If the length of a to-Report queue is greater than M, the queue is reported directly.

Several cases of observing elements:

  • A: enter the window, exit the window after 500ms, need to report
  • B: Enter window, but no exit window. The number exceeds 500ms and needs to be reported
  • C: enter the window, exit the window within 500ms, do not need to report

Code implementation

require('intersection-observer');

const MIN_OBSERVE_TIME = 500;
const OBSERVE_REPEAT_TIME = 1000;
const REPORT_REPEAT_TIME = 1000;

// Obtain a singleton of IntersectionObserver
class ReportObserver {
  constructor() {
    this.instance = null;
    this.intersectionObserver = new IntersectionObserver((entries) = > {
      entries.forEach((entry) = > {
        if (entry.isIntersecting) {
          handleEnter(entry);
        } else{ handleExit(entry); }}); });// Initializes the timer
    initInterval();
  }
  observe(el, reportData) {
    el.setAttribute('report-data'.JSON.stringify(reportData));
    el.setAttribute('guid', guid());
    this.intersectionObserver.observe(el);
  }
  // Obtain an instance of IntersectionObserver
  static getInstance() {
    if (!this.instance) {
      this.instance = new ReportObserver();
    }
    return this.instance; }}// element X enters the window and records to the to-observe queue of sessionStorage (if it already exists, it will not join the queue).
{stime: observed time, id: element ID, data: data to be reported,hasObserve:false}.
const handleEnter = function (entry) {
  const dom =  entry.target;
  const data = dom.getAttribute('report-data');
  const id = dom.getAttribute('guid');
  const stime = new Date().getTime();
  const hasObserve = false;

  const observeData = { id, data, stime, hasObserve };
  if (!findToObserve(id)) {
    pushToObserve(observeData);
  }
};

// Element X exits the window:
If (current time -stime)>=500ms and hasObserve is false, push the data of X to the to-report queue.
// In either case, element X should push the to-observe queue.
const handleExit = function (entry) {
  const dom =  entry.target;
  const id = dom.getAttribute('guid');
  const etime = new Date().getTime();

  const value = findToObserve(id);
  if(value && etime - value.stime >= MIN_OBSERVE_TIME && ! value.hasObserve) { pushToReport(value); } deleteFromToObserve(id); };// Initializes the timer
const initInterval = function () {
  // Exposure timer
  setInterval(() = > {
    // If X in the To-observe queue has (current time-stime)>=500ms and hasObserve is false, set X's hasObserve to true and add X to the to-report queue
    toObserveList().forEach((value) = > {
      const etime = new Date().getTime();
      if(etime - value.stime >= MIN_OBSERVE_TIME && ! value.hasObserve) { value.hasObserve =true; pushToObserve(value); pushToReport(value); }}); }, OBSERVE_REPEAT_TIME);// Report timer
  setInterval(() = > {
    // If a record exists in the to-Report queue, report it and remove it from the to-Report queue.
    if (toReportList.length) {
      postReportData(toReportList);
      clearToReport();
    }
  }, REPORT_REPEAT_TIME);
};

const postReportData = function (dataList) {
  // Call the background interface to report data
};

export default {
  bind(el, binding) {
    if (
      'IntersectionObserver' in window
        && 'IntersectionObserverEntry' in window
        && 'intersectionRatio' in window.IntersectionObserverEntry.prototype
    ) {
      // Start listeningReportObserver.getInstance().observe(el, binding.value);; }}};Copy the code

reference

Intersection_Observer_API

Past oliver

Say goodbye to Bad Code

2022 Code Specification Best Practices (with Web and applets Best configuration examples)

【 Front-end Exploration 】 Say goodbye to bad code! Encapsulate network requests in the chain of responsibility pattern

【 Front-end Exploration 】 Say goodbye to bad code # 2! Encapsulate sharing components with policy patterns

The code of life

[Thinking on three years of front-end development] How to read requirements effectively?

Front end step pit must see guide

[Front-end exploration] Best practices for image loading optimization

[Front-end exploration] Exploration of mobile terminal H5 to generate screenshot posters

[Front-end Exploration] H5 to obtain user positioning? This one is enough

【 Front-end exploration 】 The exploration of wechat small program jump — Why does open label exist?

VConsole fancy usage