Observing your web app

Eric Bidelman
Aug 16, 2016 · 3 min read

TL;DR: a dozen+ examples of monitoring changes in a web application.

The web has lots of APIs for knowing what’s going on in your app. You can monitor mucho stuff and observe just about any type of change.

Changes range from simple things like DOM mutations and catching client-side errors to more complex notifications like knowing when the user’s battery is about to run out. The thing that remains constant are the ways to deal with them: callbacks, promises, events.

Below are some of use cases that I came up with. By no means is the list exhaustive. They’re mostly examples for observing the structure of an app, its state, and the properties of the device it’s running on.

Listen for DOM events (both native and custom):

// user scrolls the page.
window.addEventListener('scroll', e => { ... });
el.addEventListener('focus', e => { ... }); // el is focused. img.addEventListener('load', e => { ... }); // img is done loading. // user types into input.
input.addEventListener('input', e => { ... });
// catch custom event fired on el.
el.addEventListener('custom-event', e => { ... });

Listen for modifications to the DOM:

const observer = new MutationObserver(mutations => { ... }); observer.observe(document.body, {
childList: true,
subtree: true,
attributes: true,
characterData: true
});

Know when the URL changes:

window.onhashchange = e => console.log(location.hash); window.onpopstate = e => console.log(document.location, e.state);

Know when the app is being viewed fullscreen:

document.addEventListener('fullscreenchange', e => {
console.log(document.fullscreenElement)
});

Read more

Know when someone is sending you a message:

// Cross-domain / window /worker.
window.onmessage = e => { ... };
// WebRTC
const dc = (new RTCPeerConnection()).createDataChannel(); dc.onmessage = e => { ... };

Know about client-side errors:

// Client-size error?
window.onerror = (msg, src, lineno, colno, error) => { ... };
// Unhandled rejected Promise?
window.onunhandledrejection = e => console.log(e.reason);

Read more

Listen for changes to responsiveness:

const media = window.matchMedia('(orientation: portrait)'); media.addListener(mql => console.log(mql.matches)); // Orientation of device changes.
window.addEventListener('orientationchange', e => {
console.log(screen.orientation.angle)
});

Read more

Listen for changes to network connectivity:

// Online/offline events.
window.addEventListener('online', e => {
console.assert(navigator.onLine)
});
window.addEventListener('offline', e => {
console.assert(!navigator.onLine)
});
// Network Information API navigator.connection.addEventListener('change', e => {
console.log(navigator.connection.type,
navigator.connection.downlinkMax);
});

Read more

Listen for changes to the device battery:

navigator.getBattery().then(batt => { 
batt.addEventListener('chargingchange', e => {
console.log(batt.charging);
});
batt.addEventListener('levelchange',e => {
console.log(batt.level);
});
batt.addEventListener('chargingtimechange', e => {
console.log(batt.chargingTime);
});
batt.addEventListener('dischargingtimechange', e => {
console.log(batt.dischargingTime);
});
});

Read more

Know when the tab/page is visible or in focus:

document.addEventListener('visibilitychange', e => {
console.log(document.hidden)
});

Read more

Know when the user’s position changes:

navigator.geolocation.watchPosition(pos => console.log(pos.coords));

Know when the permission of an API changes:

const q = navigator.permissions.query({name: 'geolocation'}); q.then(permission => {
permission.addEventListener('change', e => {
console.log(e.target.state);
});
});

Read more

Know when another tab updates localStorage/sessionStorage:

window.addEventListener('storage', e => alert(e));

Know when an element enters/leaves the viewport (e.g. “Is this element visible?”):

const observer = new IntersectionObserver(changes => {
...
}, {threshold: [0.25]}); observer.observe(document.querySelector('#watchMe'));

Read more

Know when the browser is idle (to perform extra work):

requestIdleCallback(deadline => { ... }, {timeout: 2000});

Read more

Know when the browser fetches a resource, or a User Timing event is recorded/measured:

const observer = new PerformanceObserver(list => {
console.log(list.getEntries());
});
observer.observe({entryTypes: ['resource', 'mark', 'measure']});

Read more

Know when properties of an object change (including DOM properties):

// Observe changes to a DOM node's .textContent.
// From gist.github.com/ebidel/d923001dd7244dbd3fe0d5116050d227
const proxy = new Proxy(document.querySelector('#target'), {
set(target, propKey, value, receiver) {
if (propKey === 'textContent') {
console.log('textContent changed to: ' + value);
}
target[propKey] = value;
}
});
proxy.textContent = 'Updated content!';

Read more

Lastly, if you’re building custom elements (web components), there are several methods, called reactions, that you can define to observe important things in the element’s lifecycle:

class AppDrawer extends HTMLElement {
constructor() {
super(); // always need to call super() first in the ctor.
// Instance of the element is instantiated.
}
connectedCallback() {
// Called every time the element is inserted into the DOM.
}
disconnectedCallback() {
// Called every time the element is removed from the DOM.
}
attributeChangedCallback(attrName, oldVal, newVal) {
// An attribute was added, removed, updated, or replaced.
}
adoptedCallback() {
// Called when the element is moved into a new document.
}
}
window.customElements.define('app-drawer', AppDrawer);

Wowza! What’s crazy is that there’s even more APIs in the works.

I suppose you could classify some of these examples as techniques or patterns (e.g. reacting to DOM events). However, many are completely new APIs designed for a specific purpose: measuring performance, knowing battery status, online/offline connectivity, etc.

Honestly, it’s really impressive how much we have access to these days. There’s basically an API for everything.

Mistake? Something missing?. Leave a comment.

Update 2016–08–17— added custom element reaction example

Originally published at ericbidelman.com.

Dev Channel

Developers Channel - the thoughts, opinions and musings…

Eric Bidelman

Written by

Engineer at Google working on Chrome, web components, and Polymer. Uses Digital Jedi skills to push the boundaries of the web. Go Blue.

Dev Channel

Developers Channel - the thoughts, opinions and musings from members of the Chrome team.

Eric Bidelman

Written by

Engineer at Google working on Chrome, web components, and Polymer. Uses Digital Jedi skills to push the boundaries of the web. Go Blue.

Dev Channel

Developers Channel - the thoughts, opinions and musings from members of the Chrome team.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store