One article to help you get the H5, small program, Taro long list exposure buried point | JD Cloud technical team

For many front-end students, "burying" is often a topic that they don't want to face but can't escape. Why do you say that, I believe that many front-end students have a deep understanding: firstly, the matter of burying points is basically "exclusive" to the front-end, and the server side is basically not involved; secondly, adding burying points often seems simple but is actually very easy to do. Trouble, in many cases, in order to obtain some information needed for buried points, it is even necessary to make painful modifications to the already written code.

Although the front-end burying point is time-consuming and laborious, and there is no sense of accomplishment, as an important way to collect online business data (user purchase behavior, activity conversion, etc.), burying point provides important data support for product strategy adjustment, especially in places like 618. , Double 11 and other major promotional activities, buried point data collection is very important for the strategy formulation, timely adjustment and verification of the final revenue effect of promotional activities, so it is another thing that R&D students must take seriously. Based on the practical experience of various platform projects over the years, this article summarizes the practical experience and skills in the development of buried point requirements. It is hoped that through the sharing of this article, more readers can avoid detours in development, complete the buried point development tasks accurately and efficiently, and ensure that The business is supported by stable data during the big promotion and normal operation.

Closer to home, for various types of burying points, exposure burying points are often the most complex and require the most comprehensive technology. If the implementation method is unreasonable, it may have the greatest impact. Therefore, this article will focus on exposure burying points, especially The idea of ​​realizing the exposure and burying of elements in the long list (or scrolling view) and the skill of avoiding pits;

1. The common method of monitoring the exposure of elements in the list

The exposure buried point of the elements in the long list (or scroll view), the key is how to monitor the "exposure" event of the child elements. "Exposure" means that the element enters the visible area of ​​the screen, that is, it can be seen by the user. This is the intuitive visual experience of human beings, so how to judge it by code? At present, there are roughly three methods: 1. Estimate the visible elements according to the paged data sent by the interface; 2. Listen to the scrolling event of the scroll view and calculate the relative position of the element in real time; 3. Use the browser (or other platforms such as applets, Taro) The standard API listens for changes in the intersection of an element and the visible area. The specific principles, scope of application, advantages and disadvantages of these three methods are introduced respectively below.

1.1 Method 1: Estimate the visible elements according to the paged data delivered by the interface

Implementation ideas: The data in long lists is often loaded through the paging interface. This feature can be used to roughly estimate the visibility of elements based on the dimension returned by a single page of data. Specifically, the data returned by each interface is used as the currently visible element list of;

advantage:

  • The advantage of this method is that it is simple: the element exposure is judged only according to the data requested by the paging interface each time, and the calculation is very simple;

shortcoming:

  • The disadvantage is that the error is too large: on the one hand, the data requested by a single paging interface often exceeds one screen; on the other hand, the heights of elements in the list may also be different, and the number of data returned by paging may also vary. The exposure error of the calculation element is too large;

Due to the obvious disadvantages and the large error, few people implement exposure burying in this way now, but this implementation method can still be seen in many scenes that do not require high precision or in codes that have been around for a long time

1. 2 Method 2: Listen to the scrolling event and calculate the relative position of the element in real time

Implementation idea: monitor the scrolling event of the long list (or scrolling view container), obtain the element coordinates (including position and size information, etc.) state (whether there is overlap) to determine whether the element is "visible";

advantage:

  • Compared with method 1, the accuracy has been greatly improved. If the calculation method is correct, the calculation result can be said to be accurate;
  • In addition, due to the use of the common basic capability interface in the platform, the compatibility is better;

shortcoming:

  • Large amount of calculation and serious performance loss: this calculation method needs to monitor the scroll event of the scroll view, and calculate the position coordinates of all elements in the list in real time within the scroll callback event (obtain the position of all elements and compare them with the current visible area), The amount of calculation this brings is quite large, often causing performance problems on the page (such as sliding freeze);
  • Scattered code and complex logic: In addition to monitoring the scrolling events of the scrolling view, an additional calculation must be performed when the data on the first screen is loaded or refreshed. The overall complexity and performance impact on the page are relatively large;
  • Other problems: It may cause other additional operations, such as frequent calls of getBoundingClientRect() in H5 may also cause browser style recalculation and layout; in iframe, internal elements cannot be directly accessed, etc.;

Although this method has a large amount of calculation, complex logic, and poor performance (of course, some performance optimization can also be performed, at the cost of higher code complexity, which is not conducive to subsequent update and maintenance), but the calculation results are accurate. Before the web-side standard interface (2016) in Method 3 appeared, this seemed to be the only choice in scenarios where calculation accuracy was strictly required;

1.3 Method 3: Use the browser standard API to monitor changes in the visible area of ​​elements

Implementation ideas: Intersection Observer API , as a web standard API interface specially used to monitor the intersecting changes of page elements, was first provided in the Chrone browser in 2016, and was supported by major browsers in the following years; The interface provides the ability to asynchronously query the position of elements relative to other elements or windows, which can efficiently monitor the intersection (visibility) changes of elements in the page;

advantage:

  • Higher performance: The underlying implementation of the browser has been optimized accordingly, and there is no problem with performance: monitoring will not be performed on the main thread (as long as the callback method will be triggered on the main thread)
  • Small amount of calculation: The small amount of calculation here refers to the calculation operations that our web developers need to perform, because most calculation browser APIs have already done calculations for us, and we only need to perform simple calculations based on the required scenarios. processing can meet the demand;
  • The calculation results are more accurate: the calculation results implemented by the browser API are relatively accurate, there is no doubt about this;
  • The code is more elegant: most of the monitoring and calculation logic are implemented inside the API, the developer's code volume will not be too much and too complicated, and the code is more concise to make more use of follow-up maintenance;

shortcoming:

  • New browser support is required (according to the browser compatibility described in the document, it actually meets most usage scenarios), and browsers of too low a version do not support it. If compatibility is required, there is also a solution, which can be solved through the official polyfill (introducing Polyfill, of course, will inevitably bring about an increase in code size. The js file size has increased by 8kb after packaging in the project, which is the only shortcoming); (w3c officially provides the corresponding polyfill)

Based on the above, this method is currently the most recommended way to implement element exposure monitoring. How to use it specifically, the following introduces the usage scenarios of H5, applets, and Taro respectively.

2. The specific implementation of the element exposure event monitoring in the list

2.1 Web (H5) terminal

To put it simply, using the Intersection Observer API to observe the visibility of view elements is mainly divided into several steps: creating observers, adding observations to target elements, processing observation results (callbacks), and stopping observations;

2. 1. 1 Specific usage method:

Step 1: Create an Observer (IntersectionObserver)

First of all, we need to create an observer IntersectionObserverto monitor the change of the intersection state of the target element relative to the root view (which can be the parent view or the current window) (that is, the "visible" state of the element). The specific creation method is to use the Web standard API: IntersectionObserverconstruction method, the specific code is as follows:

let observer = new IntersectionObserver(callback, options);
  • Among them callbackis the callback method triggered when the position of the element is monitored. The specific definition and usage will be introduced in the third step of processing the observation results ;

  • optionsis a parameter for customizing the observation mode, and the parameters are defined as follows

    interface IntersectionObserverInit {
        root?: Element | Document | null;
        rootMargin?: string;
        threshold?: number | number[];
    }
    
    • root: The reference area used to specify the observation, usually the parent view container of the target element or the entire view window (must be the parent element of the target element. If not specified or null, it defaults to the browser window)
    • rootMargin: The outer margin of the reference area (root), similar to the margin attribute in CSS, such as "10px 20px 30px 40px" (top, right, bottom, left), used to adjust the area range of the reference object (shrink or expand );
    • threshold: Intersection ratio threshold, used to customize the critical value of the intersection ratio that needs to be observed; when the intersection of elements (intersection ratio) changes, the callback method will not be executed every time the change occurs, and will only be triggered when the intersection ratio reaches the set threshold Callback ( callback); can be a single value (number) or a group of values; for example, when set to 0.25, the callback will be triggered only when the intersection reaches 0.25 (it will be triggered when it increases to 0.25 or decreases to 0.25); if If it is a set of values, the callback will also be triggered when the intersection ratio reaches any value; (Note: In addition, a callback will also be triggered when the element is added for the first time, regardless of whether it reaches the threshold or not)

For example, the threshold setting state in the above figure will trigger a callback whenever the element slides to the dotted line position and intersects the border of the parent view

Step 2: Add observations to the target element

With the observer, you can observe the target element, the specific code is as follows:

let target = document.querySelector('#listItem');
observer.observe(target);

It is necessary to pay attention to the timing of adding observations, and ensure that the observations are added after the target element is created; if it is a dynamically created element (such as pagination loading data), it is necessary to add observations to the newly added element again after each element is created;

Step 3: Processing Observations

When the proportion of the observed target element intersecting with the reference view (root) reaches the set threshold, the registered callback method ( ) will be triggered. The callbackdefinition of the callback method is as follows:

interface IntersectionObserverCallback {
    (entries: IntersectionObserverEntry[], observer: IntersectionObserver): void;
}

You can see that the callback method can receive two parameters:

  • entries : An array of IntersectionObserverEntry, which is a collection of elements whose intersection state changes. Each IntersectionObserverEntry object has 6 attributes:

    • time: The timestamp of the intersection, in milliseconds. (time of intersection change relative to time of document creation)
    • target: The observed target element is a DOM node object;
    • rootBounds: the rectangular bounds of the root element (reference area)
    • boundingClientRect: The boundary information of the target element. The calculation method of the boundary is the same as that of Element.getBoundingClientRect().
    • intersectionRect: the intersection area between the target element and the root element
    • intersectionRatio: The ratio of the size of the part where the target element intersects with the root element to the overall size of the target element, that is, the ratio of intersectionRect to boundingClientRect;
    • isIntersecting: Whether the target element intersects with the root element (judged according to the set threshold)
  • observer: the current observer;

With this information, you can easily monitor the visible state changes of the target elements, and perform various processing such as subsequent buried point reporting, data recording, and delayed loading;

The registered callback function will be executed in the main thread, so the execution speed of this function should be as fast as possible. If there are some time-consuming operations to be performed, it is recommended to use the Window.requestIdleCallback() method.

Part Four: Stop Watching

If you need to stop the observation, you can release the observation of an element or terminate the observation of all target elements at an appropriate time;

// 停止观察某个目标元素
observer.unobserve(target)

// 终止对所有目标元素可见性变化的观察,关闭监视器
observer.disconnect()

2. 1. 2 Tips

It should be noted that a new  isVisibleattribute has been added to the V2 version of the Intersection Observer API (the new version of Chrome browser already supports it, but not in other browsers such as Safari), which is used to identify whether the element is "visible" (because even if the element is in the visible area Inside, there may be elements that cannot be seen due to being blocked by other elements, style attribute hiden, etc.); in the official description, in order to ensure performance, the value of this field may not be accurate, except for special scenarios, it is not recommended to use this Field, most scenarios isIntersectingare enough;

Those who are interested can view the document description: Intersection Observer V2 Explained

2. 1. 3 Browser Support for Intersection Observer API

browser platform supported version release time
Chrome PC 51 2016-05-25
Chrome Android Android 51 2016-06-08
WebView Android Android 51 2016-06-08
Safari macOS 12.1 2019-03-25
Safari on iOS iOS 12.2 2019-03-25
Edge PC 15 2017-04-05
Firefox PC 55 2017-08-08
Firefox for Android Android 55 2017-08-08
Opera PC 38 2016-06-08
Opera Android Android 41 2016-10-25

Depending on the specific usage scenario (supported browser version), you can decide whether to use the standard API directly or need to add polyfill or other methods to be compatible with lower-version browsers;

2. 2 Mini Program (WeChat Mini Program)

Similar to the interface on the web side, the WeChat applet provides the corresponding API interface of the mini program version. The function is similar to the Intersection Observer API on the web side, and the usage method is basically the same, but there are differences in some details; specific steps:

Step 1: Create an Observer (IntersectionObserver)

IntersectionObserver wx.createIntersectionObserver(Object component, Object options)Observers can be easily created through the methods provided by the WeChat applet framework ;

Page({
  data: {
    appear: false
  },
  onLoad() {
    this._observer = wx.createIntersectionObserver(this,{
      thresholds: [0.2, 0.5]
    })
    //其他处理...
  },
  onUnload() {
    if (this._observer) this._observer.disconnect()
  }
})

Similar to the IntersectionObserver construction method on the web side, the difference is that there is no need to set a callback method in this step in the applet, and the callback method is registered when adding observations later;

Input parameter description: componentgenerally need to pass the current page or component instance; optionsyou can define the trigger threshold, whether to observe multiple target nodes at the same time and other information

Step 2: Specify the reference node (reference area)

Different from the specification at the creation time of the web side, the applet side provides two separate interfaces for specifying the reference node (reference area)

  • IntersectionObserver IntersectionObserver.relativeTo(string selector, Object margins) : Use the selector to specify a node as one of the reference areas, that is, the layout area of ​​the node is used as the reference area.
  • IntersectionObserver IntersectionObserver.relativeToViewport(Object margins): Specify the page display area as one of the reference areas

Example:

this._observer = this._observer.relativeTo('.scroll-view')

The reference area can also marginsbe expanded (or contracted) through ; if there are multiple reference nodes, their layout areas will be taken  交集 as the reference area.

Step 3: Open Observation

After creating the observer through the first two steps, setting the relevant parameters (trigger threshold, multi-target, etc.) and specifying the reference area, the target element can be observed. Here, the target element is specified by means of a selector (the web side is an element instance), and the callback method for intersecting state changes needs to be specified here:IntersectionObserver.observe(string targetSelector, function callback)

Example:

this._observer.observe('.ball', (res) => {
    console.log(res);
    this.setData({
        appear: res.intersectionRatio > 0
    })
})

Step 4: Handle callbacks

When the intersection of the element relative to the reference area changes (same as the web side, the trigger timing is determined by the thresholds threshold set when creating the observer in the first step) the corresponding callback method will be triggered. The parameters accepted in the callback method are basically the same as those on the web side, but there are also differences:

  • The applet side is a single trigger, and the input parameter of the callback method is a single element (compared to the web side, which is multiple callbacks together, and the input parameter is an array of changing elements);
  • The input parameters of the applet include the node ID and custom data of the target node at the same time;

Step 5: Stop listening

IntersectionObserver.disconnect()
Stop listening. The callback function will no longer fire

if (this._observer) this._observer.disconnect()

Tips

Note: In the component, if you add the intersecting change observation of internal sub-elements in the attached component life cycle function, you may not be able to monitor successfully, because the layout of the component has not been completed at this time, and the creation of the nodes in the component has not been completed;

2. 3 Taro framework (Taro3+React)

Taro also provides the corresponding IntersectionObserver API. The definition and usage of the API are basically aligned with the WeChat applet side; Taro itself supports multiple terminals such as H5 and applets, and its IntersectionObserver interface is internally compatible with H5 and WeChat applets. , Jingdong applet and other platforms have been aligned and smoothed. Specifically, on the H5 side, it is packaged according to the format of the WeChat applet side. Its internal implementation is to call the Intersection Observer API on the web side. Basically, it is to bridge the native interface of the corresponding platform applet;

Since the interface definition and usage are aligned with the WeChat applet, the specific usage of the Taro side will not be described here. What needs to be explained is that due to the particularity of the Taro framework (compared with the original method of the applet), Taro is used for When developing the sliding exposure monitor on the applet side, there are several points that are prone to errors or require special handling:

1. The problem that monitoring does not take effect

Due to the Taro runtime mechanism, adding listeners immediately after the execution of the Taro component's data update method (such as setState) may not take effect, because the corresponding data-driven applet element instance has not yet been created or mounted at this time. Add a delay or execute it in the Taro.nextTick callback (the latest version of Taro has added the observe method to Taro.nextTick by default); if you encounter a situation where adding monitoring does not take effect, you can try this method;

Taro.nextTick(() => {
    //将监听添加时机(延迟作到下一个时间片再执行),解决监听添加时元素尚未创建导致的监听无效问题
    expoObjserver.relativeTo('.clpScrollView').observe('.coupon-item', result => {
        console.log(result.intersectionRatio);
        //...
    });
});

2. To create an Observer, you need to pass in a native component instance

When creating an observer, you need to pass in the page or component instance of the applet, and directly use this in the Taro component or page to get the instance of the page or component of the Taro layer, and the two are different;

So how to get the component instance of the applet layer:

  • In the pure Taro project, you can directly use Taro.getCurrentInstance().pagethe instance of getting the applet page;
  • If the Taro component is compiled into a mixed mode of native custom components, you can  props.$scope obtain the custom component object instance of the applet

3. How to get other information of the target element in the callback method?

If the creation and settings are correct, as the list slides or the position of other elements changes, the corresponding callback method should be triggered. In the callback method, we need to receive the input parameters of the callback and process them (such as reporting relevant business information). According to the Taro documentation definition, the input parameter of the callback method is ObserveCallbackResultthe type:

interface ObserveCallbackResult {
      /** 目标边界 */
      boundingClientRect: BoundingClientRectResult
      /** 相交比例 */
      intersectionRatio: number
      /** 相交区域的边界 */
      intersectionRect: IntersectionRectResult
      /** 参照区域的边界 */
      relativeRect: RelativeRectResult
      /** 相交检测时的时间戳 */
      time: number
    }

It can be seen that there are only intersection information such as intersectionRatio and time, but there is no custom data field (as a comparison, the callback method provided by the WeChat applet also includes node ID, node custom data attribute dataset and other information), So how to get other data information on the target element in Taro?

The actual debugging found that although there is no id and dataset in the document, there are these two fields in the actual return value. Haha, do you think there is nothing wrong with seeing this? I thought it was just a little joke made by the Taro document. ;Don't be too happy, although there is a dataset field in the actual return value, but unfortunately the dataset field is always empty, the actual return result (result) example is as follows:

{
    "id": "_n_82",
    "dataset": {},
    "time": 1687763057268,
    "boundingClientRect": {
        "x": 89.109375,
        "y": 19,
        ...
    },
    "intersectionRatio": 1,
    "intersectionRect": {
        "x": 89.109375,
        "y": 19,
        ...
    },
    "relativeRect": {
        "x": 82.109375,
        ...
    }
}

what? why? Seeing this, it is estimated that everyone has the urge to smash the keyboard, so don't worry, let's first analyze why it datasetis empty?
This is because datasetit is a special template attribute of the applet. The main function is to  event obtain  dataset relevant data in the object of the event callback. Taro partially supports these capabilities. Taro has already supported the event callback object through the simulation at the logic layer. passed  event.target.dataset or  event.currentTarget.dataset acquired. But because it is simulated at the logic layer, this property is not actually set in the template. Therefore, when there are some APIs (such as: createIntersectionObserver) in the applet to obtain the nodes of the page, they cannot be obtained dataset.

As mentioned in the previous point, Taro's simulation of the applet dataset is implemented at the logic layer of the applet. This property is not actually set in the template.
However, when there are some APIs (such as: createIntersectionObserver) in the applet to obtain the nodes of the page, they cannot be obtained because there are actually no corresponding attributes on the nodes.

--From Taro official documentation:  Taro-React-dataset

Since the direct value in the callback parameter is empty, how do we get the custom data on the element?

Solution 1: taro-plugin-inject solution

The official solution is to use taro-plugin-injecta plug-in to inject some common attributes into the sub-element; the actual verification found that the corresponding attribute can indeed be seen in the callback dataset after the plug-in is inserted, but the attribute inserted by this method can only be unified The fixed value of the attribute value cannot be dynamically set according to the actual data, so this solution cannot meet the demands.

//项目 config/index.js 中的 plugins 配置:
  plugins: [
    [
      '@tarojs/plugin-inject',
      {
        components: {
          View: {
            'data-index': "'dataIndex'",
            'data-info': "'dateInfo'"
          },
        },
      },
    ]
  ],
  
//实际返回值
{
    "id": "_n_82",
    "dataset": {
        "index": "dataIndex",
        "info": "dateInfo"
    },
    "time": 1687766275879,
    ...
}

Solution 2: Access Taro virtual DOM

According to Taro's official document on the description of the differences in the use of the React framework ( Taro-React-lifecycle trigger mechanism ), Taro3 implements a BOM and DOM API that follows the Web standard on the logic layer of the applet. Through these APIs, the corresponding virtual DOM nodes (TaroElement objects) can be obtained. Since it is implemented by the logic layer, the corresponding datasetinformation should also be visible on the nodes.

This is also confirmed by the TaroElement field description in the Taro DOM Reference documentation.

So how to achieve it? Although there is no custom data field we want in the callback parameter, we can get the node id information. We can use the node id to get the corresponding Taro virtual DOM node through the API provided by Taro , and get the information document.getElementById();we need from this node . datasetcode show as below:

Taro.nextTick(() => {
	//将监听添加时机(延迟作到下一个时间片再执行),解决监听添加时元素尚未创建导致的监听无效问题
	expoObjserver.relativeTo('.clpScrollView').observe('.coupon-item', result => {
		if (!result?.id) return;
		// !!!获取虚拟DOM节点
		const tarTaroElement = document.getElementById(result?.id);
		const dataInfo = tarTaroElement?.dataset; //拿到dataset信息
		console.log(tarTaroElement);
		console.log(dataInfo);
       //...
	});
});

At this point, we have obtained the desired custom data (business data), and the follow-up is to use these data at will according to the requirements. The exposure and embedding of the sliding elements of the list in Taro are done~

To sum up, through the above several common solutions and the introduction of specific implementations in each platform, the exposure monitoring problem of long-list sliding elements should no longer be difficult, and the exposure monitoring of sliding elements has been solved. Based on this, the exposure burying point or other Advanced gameplay (such as long list optimization - lazy loading of resources, infinite loop scrolling, etc.) We can take it easy in the future.

References

Author: Jingdong Retail Ding Xin

Source: JD Cloud Developer Community

iQIYI client "White" TV, the background uploads TIOBE's July list at full speed: C++ is about to surpass C, JavaScript enters Top6 GPT-4 model architecture leak: Contains 1.8 trillion parameters, using Mixed Expert Model (MoE) CURD has been suffering for a long time in the middle and backstage front-end, and Koala Form will be used for 30 years, and the Linux market share has reached 3%. Musk announced the establishment of xAI company ChatGPT traffic dropped by 10 % . SUSE invests $10 million in sweeping data theft , forks RHEL
{{o.name}}
{{m.name}}

Guess you like

Origin my.oschina.net/u/4090830/blog/10088424