We often deal with user interaction in web development. We use addEventListener to add event listeners to listen for various user actions, such as Click, mousedown, Mousemove, input, etc. These are events that are triggered directly by the user.

What about events that are not directly triggered by the user? How do you listen for events like elements going from invisible to visible, changes in element size, changes in element attributes and child nodes?

Browsers provide five types of observers to monitor these changes: MutationObserver, IntersectionObserver, PerformanceObserver, ResizeObserver, ReportingObserver.

Let’s take a look at each:

IntersectionObserver

How do you listen for an element going from invisible to visible, from visible to invisible?

Use IntersectionObserver.

IntersectionObserver can monitor the proportion of intersecting parts between an element and the visible area and trigger a callback if the proportion reaches a certain threshold.

We prepare two elements:

<div id="box1">BOX111</div>
<div id="box2">BOX222</div>
Copy the code

Add style:

#box1.#box2 {
    width: 100px;
    height: 100px;
    background: blue;
    color: #fff;

    position: relative;
}
#box1 {
    top: 500px;
}
#box2 {
    top: 800px;
}
Copy the code

These two elements are at 500 and 800 px, respectively, and we listen for changes in their visibility.

const intersectionObserver = new IntersectionObserver(
    function (entries) {
        console.log('info:');
        entries.forEach(item= > {
            console.log(item.target, item.intersectionRatio)
        })
    }, {
    threshold: [0.5.1]}); intersectionObserver.observe(document.querySelector('#box1'));
intersectionObserver.observe( document.querySelector('#box2'));
Copy the code

A IntersectionObserver object is created to monitor box1 and Box2 and trigger a callback when the visibility ratio reaches 0.5 and 1.

Browser run:

You can see that the elements box1 and box2 trigger callbacks at half (0.5) and full (1) visibility, respectively.

What’s the point?

This is very useful, when we do some data collection, we want to know if an element is visible and when it is visible, we can use this API to listen, and when we do lazy loading of images, we can trigger loading when the visual ratio reaches a certain scale.

In addition to listening for element visibility, you can also listen for changes to elements’ attributes and child nodes:

MutationObserver

To listen for changes to a normal JS Object, we use Object.defineProperty or Proxy:

To monitor changes in elements’ attributes and child nodes, we can use MutationObserver:

MutationObserver can listen for changes to an element’s attributes, additions, deletions, and changes to its child nodes.

We prepare a box like this:

<div id="box"><button>light</button></div>
Copy the code

Add style:

 #box {
    width: 100px;
    height: 100px;
    background: blue;

    position: relative;
}
Copy the code

Here it goes:

We modify it periodically:

setTimeout(() = > {
    box.style.background = 'red';
},2000);

setTimeout(() = > {
    const dom = document.createElement('button');
    dom.textContent = 'Dong dong dong dong';
    box.appendChild(dom);
},3000);

setTimeout(() = > {
   document.querySelectorAll('button') [0].remove();
},5000);
Copy the code

Change the background color to red for 2s, add a button child for 3s, and delete the first button for 5s.

Then listen for it:

const mutationObserver = new MutationObserver((mutationsList) = > {
    console.log(mutationsList)
});

mutationObserver.observe(box, {
    attributes: true.childList: true
});
Copy the code

Create a MutationObserver object that listens for changes to the box’s properties and child nodes.

Browser run:

You can see that some information is listened and printed for all three changes:

The first change was made to attributes, which is style:

The second change to childList is to add a node:

The third childList, also changed, removed a node:

We got it all!

What can you do with this? For example, if an article watermark is removed by DevTools, you can listen for the change through the MutationObserver and then add it again, keeping the watermark in place.

Of course, there are many other uses, but this is just an introduction to features.

In addition to listening for changes in element visibility, attributes, and child nodes, you can also listen for size changes:

ResizeObserver

Window we can use addEventListener to listen for resize events, what about elements?

The element can use ResizeObserver to listen for size changes and trigger a callback when width and height are changed.

We prepare an element like this:

<div id="box"></div>
Copy the code

Add styles:

#box {
    width: 100px;
    height: 100px;
    background: blue;
}
Copy the code

Modify its height at 2s:

const box = document.querySelector('#box');

setTimeout(() = > {
    box.style.width = '200px';
}, 3000);
Copy the code

Then we use ResizeObserver to monitor its changes:

const resizeObserver = new ResizeObserver(entries= > {
    console.log('Current size', entries)
});
resizeObserver.observe(box);
Copy the code

Run in the browser:

The size change is being monitored. Look at the printed message:

You can get the element and its position and size.

This allows us to listen for the element’s resize.

In addition to monitoring changes in element size, visibility, attribute child nodes, etc., it also supports monitoring of performance recording behavior:

PerformanceObserver

Browsers provide performance apis to record points in time, time segments, and resource loading time.

We want to report performance as soon as it is recorded, but how do we know when performance data will be recorded?

Use PeformanceObserver.

PerformanceObserver is used to listen for recording performance data and once recorded it triggers a callback so that we can report the data in a callback.

For example, performance can record a point in time using the mark method:

performance.mark('registered-observer');
Copy the code

To record a time period by measure:

performance.measure('button clicked'.'from'.'to');
Copy the code

The last two parameters are points in time, not from the beginning to the present.

We can listen for them with PerformanceObserver:

<html>
<body>
  <button onclick="measureClick()">Measure</button>

  <img src="https://p9-passport.byteacctimg.com/img/user-avatar/4e9e751e2b32fb8afbbf559a296ccbf2~300x300.image" />

  <script>
    const performanceObserver = new PerformanceObserver(list= > {
      list.getEntries().forEach(entry= > {
        console.log(entry);/ / report})}); performanceObserver.observe({entryTypes: ['resource'.'mark'.'measure']});

    performance.mark('registered-observer');

    function measureClick() {
      performance.measure('button clicked');
    }
  </script>
</body>
</html>
Copy the code

Create PerformanceObserver to listen for mark, Measure, and Resource to record time.

Then we used Mark to record a certain point in time. When we clicked the button, we used measure to record the data of a certain period and loaded a picture.

When these recording actions occur, it is expected to trigger a callback in which it can be reported.

Let’s run it in the browser:

You can see that mark’s point in time records, resource loading time records, and button clicking time period records are all monitored.

The data of the three recording behaviors are printed respectively:

Mark:

Image loading:

Measure:

Using this data, it can be reported for performance analysis.

In addition to elements and performance, browsers also have a reporting listener:

ReportingObserver

When the browser runs a deprecated API, it prints a deprecated report on the console:

The browser will also make some intervention on the behavior of the web page in some cases, such as deleting the iframe of the advertisement that consumes too much CPU:

When the network is slow, the image will be replaced by a placeholder image, click will load:

These interventions are done by the browser, which prints a report on the console:

These interventions or outdated apis do not report errors, so they cannot be picked up by bug listening, but they may also be important for web apps:

For example, my web page was designed to display ads, but the browser intervened and removed the ads from my page, and I didn’t know it. If I knew, I might be able to optimize iframe.

For example, the image of my web page is very important, but the browser intervenes and changes it to a placeholder, which I don’t know. If I knew, I would probably optimize the size of the image.

So naturally listening, so the browser provides the ReportingObserver API to listen for the printing of these reports, so we can get these reports and upload them.

const reportingObserver = new ReportingObserver((reports, observer) = > {
    for (const report of reports) {
        console.log(report.body);/ / report}}, {types: ['intervention'.'deprecation']});

reportingObserver.observe();
Copy the code

ReportingObserver can listen for reports of outdated apis, browser interventions, etc., printed and reported in callbacks. These are data that bug monitors cannot listen to but are useful for understanding the health of the web page.

The code is uploaded to github: github.com/QuarkGluonP…

conclusion

To listen for user interactions, we use addEventListener to listen for events like Click, mouseDown, KeyDown, input, etc. However, the XxxObserver API is used for non-user interaction events such as element changes, performance logging, and browser interventions.

Browsers provide these five types of Observers:

  • IntersectionObserver: monitors changes in the visibility of elements and is often used for data collection of element display and lazy image loading
  • MutationObserver: Listens for element attributes and child node changes, such as a watermark that can be used to make an unremovable watermark
  • ResizeObserver: Monitors element size changes

There are two more that have nothing to do with elements:

  • PerformanceObserver: Monitor the performance record to report data
  • ReportingObserver: Reports on outdated apis, browser interference, and a more complete picture of web app performance

These apis are less useful than the interaction events added by addEventListener, but can be useful in specific scenarios.

How many of the five Observer types of browsers have you used? Under what circumstances has it been used? Let’s talk about it.