Statistical design scheme for buried point of front landing page

In our daily work, we have more or less received such a demand — to report the user behavior trajectory of the landing page at a buried point, which is convenient for the operation students to analyze and optimize. How to make a general buried point JS-SDK? This is the theme of our sharing this time.

Demand analysis

First of all, let’s think about: from the opening of a page to the interaction, where do we need to do some buried points? Starting from the page, we definitely want to know whether the DOM loading of the page is complete, whether the page resources are loaded, button click, popup exposure, click to jump, and which areas of the user scrolling page are reported in the visual area. Our general approach is to embed the code buried point, the advantage of this is to be able to accurately report in their desired location. The downside is that there is too much coupling to the business code. To reduce strong coupling, we can add reported attributes to tags that need to be buried, and then hand all the reporting logic to our tracer.js.

Burial site design

Since we want to reduce the strong coupling of our code, we completely separate the logic about reporting from the business code.

  • Demand forecast

We used to code the embedding like this:

track({
  id:' '.msg:' '.type:' '
});
Copy the code

Once the project gets big, it’s likely to get messy, so we can bury it in the following format:

<div id="root" trace-name="land_root" trace-id="1">
  <div class="button" trace-name="submit_play" trace-msg="Submit button">Click on the</div>
  <div class="button" trace-name="comp_play" trace-msg="Component button">Click the</div>
 </div>
Copy the code

By comparison, is it found that this can greatly reduce the strong coupling with the business? Well, let’s make it happen!

  • Buried point field

The first step is to define the buried fields according to our business requirements. Here are some fields I have defined:

const types = {
  elEventMap: {
    document: {
      init: 'dt=1000'.ready: 'dt=1001'.exit: 'dt=1002'.winLoad: 'dt=1003',},'[trace-name=land_root]': {
      DOMNodeInserted: 'dt=2000',},'[trace-name=comp_play]': {
      click: 'dt=3000',},'[trace-name=submit_play]': {
      click: 'dt=3001',},'[trace-name=dialog_root]': {
      DOMNodeInserted: 'dt=2001'.click: 'dt=3000',},'[trace-name=observer_area]': {
      view: 'dt=3004',},... }},Copy the code

To explain why I did it this way, the idea is to use the name of the property as the key, and then use the event that the target object needs to listen for as the value; A bridge object can be bound to multiple events, and each event has a corresponding reporting code. All we need to do is walk through it, bind the events and get the corresponding code.

  • Binding of events

Use the type we defined above, and then add it all at once:

let eventsPool = [];
Object.keys(events).forEach((key) = > {
    let dom = document.querySelector(key)
    for (let k in event) {
      function handleEvent(e) {
        const tranceName = trace.getElmAttr(e.target, 'trace-name')
        if(tranceName) { ... }}if (eventsPool.includes(k)) return;
      eventsPool.push(k);
      document.addEventListener(k, ['click'].includes(k) ? debounce(handleEvent) : handleEvent)
    }
})
Copy the code

Here I am using the event broker and there is a check to see if the event is bound to prevent multiple bindings. Here, to prevent multiple clicks, we use an anti-shake function. And we’ll add a trace-${key}-disabled attribute to the current DOM element to see if the event is triggered. Tracer. js also provides a buried point property triggered by multiple clicks, which can be bound to trace-name=comp_play.

  • Checks if the element is loaded

Here we have two schemes, the first is to listen for the DOMNodeInserted event, and the second is to use a timer to listen for the presence of an element and report it if it exists.

  • Detects which areas are visible after the user scrolls

Usage is also simple, we simply add trace-name=”observer_area” to the element we want to detect. Top, bottom, left, and right of the current element relative to the viewport satisfy the following conditions:

  1. The current element relative to the viewporttopGreater than (-element height /2);
  2. The current element relative to the viewportbottomIs less than (viewport height + element height /2);
  3. The current element relative to the viewportleftShould be greater than zero;
  4. The current element relative to the viewportrightShould be smaller than the viewport width;

The specific code is as follows:

function computedIsInner(trace, dom) {
  let clientW = window.innerWidth || document.documentElement.clientWidth
  let clientH = window.innerHeight || document.documentElement.clientHeight
  const getBoundingClientRect = dom.getBoundingClientRect()
  if (getBoundingClientRect) {
    const { left, right, top, bottom, height } = getBoundingClientRect
    const isInner =
      left >= 0 &&
      top >= -height / 2 &&
      bottom <= clientH + height / 2 &&
      right <= clientW
    if (isInner) {
      const isDisabled = trace.getElmAttr(dom, `trace-observe-disabled`);
      if (isDisabled) return;
      trace.setElmAttr(dom, 'observe')
      trace.traceEmit(dom, 'observe', {
        dt:3004})}else {
      trace.removeElmAttr(dom, `trace-observe-disabled`)}}}Copy the code

To ensure that elements already in the visible area are not reported each time, a trace-observe-disabled attribute value is added to the reported area, as long as it is reported only once in the visible area. Note: The code will first check whether the scroll detection function needs to be enabled when running, that is, determine whether the DOM is bound to the attribute trace-name=”observer_area”. If so, the scroll event will be enabled; if not, the scroll event will not be executed. So we have a basic version of the buried point is designed, if more functions can be expanded. That’s all for this share. Thanks for reading. The full project code is available at github.com/774848686/t…