Observers in JS – four kinds of Observers

The original link

Today we will learn a few Observer apis in JS

  • Intersection Observer
  • Mutation Observer
  • Resize Observer
  • Performance Observer

Intersection Observer

When you want to listen for an element and want to be notified when it’s visible in the viewport, this API is the perfect choice. Traditionally we have bound the container’s Scroll event or set a timer to call getBoundingClientRect() repeatedly to get the position of the element. This would have poor performance because every time we get the position of the element, the entire layout would be recalculated. Another scenario is that if your elements are placed in an iframe, like some ads, it’s almost impossible to know when they will appear.

At this point, we can turn over all of this work to IntersectionObserver.

How does it work?

Instantiate an observer:

var observer = new IntersectionObserver(callback[, options]);
Copy the code
  • Callback is a callback function that returns an array of real-time data listening to the target element
    • Time timestamp
    • RootBounds The location of the root element
    • BoundingClientRect Specifies the location of the target element
    • Location information of intersectionRect
    • See the figure for the visible ratio of intersectionRatio target element
    • Target, etc.
  • Options are configurations
    • Root Is the ancestor of the target element, that is, it must be the direct or indirect parent of the target element
    • RootMargin A set of offsets added to root’s bounding box when calculating crossover values, written like CSS margins
    • Threshold specifies the ratio of a listening target to the boundary box cross area. It can be a specific value or an array of values between 0.0 and 1.0

Start listening on elements:

observer.observe(target)
Copy the code

After these two steps, you can complete the business code in the callback.

In addition, there are two methods:

  • Stop listening on a target
observer.unobserve(target)
Copy the code
  • Terminate listening on all targets
observer.disconnect()
Copy the code

The demo 🌰 left

Make a simple demo of the video stream, play it when the video has been rolled to the full screen, and pause other videos that have been “rolled out” of the screen or haven’t been “rolled in” yet.

(Scroll to see the effect. Brackets show the visible scale of each element in the viewport.)

  • (1)

    Big cat, big cat, big cat, big cat, big cat, big cat, big cat

  • (2)

    The little turtle, the little turtle, the little turtle, the little turtle, the little turtle, the little turtle, the little turtle, the little turtle, the little turtle, the little turtle, the little turtle

  • (3)

    Ha ha ha ha ha ha ha ha

  • (4)

    Hey hey hey hey hey hey hey hey hey hey

  • (5)

    Hehe hehe hehe

  • (6)

    Ha ha ha ha ha ha ha ha

  • (7)

    Ha ha ha ha ha ha ha ha

Main code:

methods: { reserveCallback (entries) { let ratio = entries[0].intersectionRatio, $target = entries[0].target; If (1 - ratio <= 0.03) {this.onPlay($target); }}, onPlay ($target) {// play video}, onPause ($target) {// addObserver ($targets) {for(let I = 0, len = $targets.length; i < len; Observe ($targets[I]) {// Start monitoring this.observer.observe($targets[I]); } } }, mounted () { let $referenceBox = document.querySelector('.js-content'), $targets = document.querySelectorAll('.js-item'); // Instantiate observer this.observer = new IntersectionObserver(this.reserveCallback, {root: $referenceBox, rootMargin: '0px', threshold: [0.7, 0.8, 0.9, 1]}); this.addObserver($targets); }Copy the code

The complete code

The demo above also has major drawbacks: it listens for all video elements and does not turn off the viewer for “missing” videos, which can cause performance problems as the number of videos increases. Further optimization is needed if it is really applied in business.

Other applications

In addition to the AD display mentioned above, you can also do lazy loading: set a listening element at the bottom of the list, load more content when it appears, and change the position of the listening element at the bottom to continue listening.

What else can you do?

The resources

  • Intersection Observer
  • IntersectionObserver ‘s “Coming into View
  • Observing Intersection Observers
  • IntersectionObserver
  • IntersectionObserver polyfill

Mutation Observer

MutationObserver is a good choice when we want to know exactly what has changed at some point in time.

How to use

Instantiate an observer:

var observer = new MutationObserver(callback);
Copy the code

Start listening:

observer.observe(target, config);
Copy the code
  • Config Requires listening properties
    • Attributes Changes to a Boolean type attribute
    • ChildList Changes to children of Boolean type (new, deleted, or changed)
    • CharacterData Changes to the content or text of a Boolean node.
    • Subtree Boolean whether the observer is applied to all descendants of the node
    • AttributeOldValue A Boolean type that observes whether an attribute value needs to be recorded before the change
    • CharacterDataOldValue Boolean Type Whether to record the previous value of characterData when it is changed
    • An attributeFilter array looks at specific attributes (such as [‘class’,’ SRC ‘])

Here is a simple demo where any operation on a node is notified by the MutationObserver API.

The demo 🌰 left

Main code:

methods: {
    observerCallBack (mutations) {
    	//do log
    },

    onAddAttr () {
    	// toggle attribute 'd'
    }
},

mounted () {
    this.$list = document.querySelector('.js-list');

    let config = {
    	attributes: true, 
    	childList: true, 
    	characterData: true,
    	subtree: true
    };
    let observer = new MutationObserver(this.observerCallBack);
    observer.observe(this.$list, config);
}
Copy the code

The complete code

The resources

  • MutationObserver
  • Detect DOM changes with Mutation Observers
  • Mutation Observer API
  • Getting to Know Mutation Observers

Resize Observer

The name tells you what the API does: it listens for element size changes.

Previously, listeners were attached to the resize event in the window to listen for changes in the size of elements. This is not so simple for elements that are unaffected by window changes. Now we can easily implement this using the API.

How to use

Again, it only takes two steps:

var observer = new ResizeObserver(callback);
observer.observe(target);
Copy the code

But its trigger is also conditional, the following are the trigger and not trigger conditions:

  • The trigger

    • 1. Triggered when an element is inserted or removed
    • 2. Emitted when the display element changes from display to None or vice versa
  • Don’t trigger

    • 1. Does not fire for non-replaceable inline elements
    • 2. The CSS transform operation is not triggered

The demo 🌰 left

width:px

height:px

Drag the lower right corner to change the size of the element or click the random button to set the random size, you will receive a notification (inside the small star rotation speed change). A transform changes the size visually without notice.

The complete code

Unfortunately, the API is still experimental and not implemented in many browsers.

However, since MutationObserver is already supported by most browsers and is supported by Polyfill, we can easily use it to replace ResizeObserver.

Recommended reading

  • In JavaScript ResizeObserver
  • Resize Observer 1
  • A Look at the Resize Observer JavaScript API
  • THE RESIZE OBSERVER EXPLAINED

Performance Observer

The PerformanceObserver is a relatively complex API for monitoring various performance-related metrics. The API consists of a series of apis:

  • Performance Timeline Level 2
  • Paint Timing 1
  • Navigation Timing Level 2
  • User Timing Level 3
  • Resource Timing Level 2
  • Long Tasks API 1

If really fine study rise thing still a lot of, here simply introduce (because I also did not make too clear 😂).

How to use

var observer = new PerformanceObserver(callback);
observer.observe({ entryTypes: [entryTypes] });
Copy the code

entryTypes: Name of an indicator to be monitored. These indicators can be obtained from performance.getentries (). In addition can also through the performance. GetEntriesByName (), performance. GetEntriesByType () for the name and entryType to filter respectively.

  • Mark gets all tokens made with performance.mark(markName)
  • Measure Retrieves all measurement values obtained by performance. Measure (measureName, markName_start, markName_end)
  • Longtask listens for long tasks (tasks that exceed 50ms).
  • “First-paint” and “first-contentful-paint” are two types of performance metrics for drawing.
  • Navigation various times associated with the page can be obtained through performance. Timing
  • Resource Various information related to resource loading

Instead of doing everything before, now our code just needs to look like this

const observer = new PerformanceObserver((list) => { let output; For (const item of list.getentries ()) {// business code}}); Observer.observe ({// Enter entryTypes as required: ['mark', 'measure', 'longTask ', 'paint', 'navigation', 'resource']});Copy the code

The demo 🌰 left

show in codepen

Due to the limited time, energy, if there is any error in the above content, welcome to 🙏.

The resources

  • PerformanceObserver()
  • Different Types Of Observers Supported By Modern Browsers