Categories
JavaScript

A Guide to the JavaScript window.crypto Object

The window object is a global object that has that provides JavaScript access to the DOM. It also contains a standard library of functions that can we access at any location in our web apps.

In this article, we look at the window.cryoto object.

window.crypto

The window.crypto property returns a Crypto object which is associated with the global object. This object allows web pages to run various cryptographic operations on the browser side. It has one property, which is the subtle property.

The Crypto.subtle property returns a SubtleCrypto object which allows us to do subtle cryptography on the client-side. The SubtleCrypto object has 5 methods for scrambling and unscrambling data. The sign method is for creating digital signatures.

A verify method exists to verify the digital signatures created by the sign method.

The encrypt method is used for encrypting data, and the decrypt method is used for decryption the scrambled data generated by the encrypt method. The digest method is used to create a fixed-length, collision-resistant digest of some data.

We can also use the SubtleCrypto object to generate and derive cryptographic keys with the generateKey and deriveKey methods respectively.

The generateKey method generates a new distinct key value each time we call it, while the deriveKey method derives a key from some initial material. If we provide the same material to 2 separate calls to deriveKey , we will get the same underlying value.

The deriveKey method is useful for deriving the same key for encryption and decryption. We can also use the importKey and exportKey methods to import and export cryptographic keys respectively.

There’s also a wrapKey method that exports the key and then encrypts it with another key.

An unwrapKey method is also provided to decrypt the encrypted key done by the wrapKey method and import the decrypted key.

For example, we can use the sign method to create a digital signature. It takes 3 arguments. The first is the algorithm, which is a string or an object that specifies the signature algorithm to use for creating the digital signature. Possible values are:

  • RSASSA-PKCS1-v1_5 — pass in the string “RSASSA-PKCS1-v1_5” or an object of the form { “name”: “RSASSA-PKCS1-v1_5” }
  • RSA-PSS — pass an RsaPssParams object. An RsaPssParams object has the name property which should be RSA-PSS , and saltLength which is the length of the random salt to use measured in bytes. The maximum value of saltLength is Math.ceil((keySizeInBits - 1)/8) - digestSizeInBytes - 2
  • ECDSA — pass an EcdsaParams object. An EcdsaParams object has the name property, which should be the string 'ECDSA' and the hash property, which is string that can have the possible values of SHA-256, SHA-384 or SHA-512
  • HMAC — pass in the string “HMAC” or an object of the form { “name”: “HMAC” }

The second argument is the key which is a CryptoKey object that has the private key to be used for creating the signature. The third argument is the data which is an ArrayBuffer or ArrayBufferView object that has the data to be signed.

The sign method returns a promise that’s fulfilled with an ArrayBuffer object that has the signature.

Likewise, the verify method takes in the same first algorithm , key , and data argument as the sign method as the first, second and fourth arguments. The signature generated from the sign method is the third argument. It returns a promise that fulfills with the value true if the signature is valid and false otherwise.

To use the sign and verify methods, we can write something like the following code:

const enc = new TextEncoder();
const encodedMessage = enc.encode('hello');
const keyPair = window.crypto.subtle.generateKey({
    name: "RSASSA-PKCS1-v1_5",
    modulusLength: 4096,
    publicExponent: new Uint8Array([1, 0, 1]),
    hash: "SHA-256"
  },
  true,
  ["sign", "verify"]
);

(async () => {
  const {
    privateKey,
    publicKey
  } = await keyPair;
  const signature = await window.crypto.subtle.sign(
    "RSASSA-PKCS1-v1_5",
    privateKey,
    encodedMessage
  );
  const signatureValid = await window.crypto.subtle.verify("RSASSA-PKCS1-v1_5", publicKey, signature, encodedMessage);
  console.log(signatureValid);
})()

We first generate the key pair with the generateKey since we’re using the asymmetric RSA algorithm which has a private and public key. The generateKey method takes the algorithm as the first argument, where the possible values are:

  • To use RSASSA-PKCS1-v1_5, RSA-PSS, or RSA-OAEP we pass an [RsaHashedKeyGenParams](https://developer.mozilla.org/en-US/docs/Web/API/RsaHashedKeyGenParams) object.
  • To use ECDSA or ECDH we pass an [EcKeyGenParams](https://developer.mozilla.org/en-US/docs/Web/API/EcKeyGenParams) object.
  • To use HMAC we pass an [HmacKeyGenParams](https://developer.mozilla.org/en-US/docs/Web/API/HmacKeyGenParams) object.
  • To use AES-CTR, AES-CBC, AES-GCM, or AES-KW we pass an [AesKeyGenParams](https://developer.mozilla.org/en-US/docs/Web/API/AesKeyGenParams) object.

The second argument is the boolean extractable property which indicates whether it’s possible to export a key using the SubtleCrypto.exportKey() or SubtleCrypto.wrapKey() methods. The third argument is the keyUsages array which indicates which methods can be used with the keys generated:

  • encrypt
  • decrypt
  • sign
  • verify
  • deriveKey
  • deriveBits
  • wrapKey
  • unwrapKey

Then we call the sign method with the algorithm name, private key, and the encoded message we generated from the TextEncoder . This generates the signature. Then we call the verify method with the algorithm name, private key, the generated signature fulfilled from the sign method and the same encoded message. If we run the code above, we should get console.log logging true .

The encrypt method takes 3 arguments. The first is the algorithm , which is an object with the following possible values:

The second argument is the CryptoKey object which we use to do the encryption. The third argument is a BufferSource object that has the data to be encrypted, also known as plain text. It returns a promise that’s resolved with an ArrayBuffer object containing the encrypted plain text.

Likewise, the decrypt method takes the same first 2 arguments as the encrypt method, except that the third argument is a BufferSource that has the data to be decrypted. It returns a promise that’s resolved with the ArrayBuffer object which has the plain text.

For example, we can use the encrypt and dercrypt methods like in the following code:

const enc = new TextEncoder();
const dec = new TextDecoder();
const keyPair = window.crypto.subtle.generateKey({
    name: "RSA-OAEP",
    modulusLength: 4096,
    publicExponent: new Uint8Array([1, 0, 1]),
    hash: "SHA-256"
  },
  true,
  ["encrypt", "decrypt"]
);
const encodedMessage = enc.encode('hello');
(async () => {
  const {
    privateKey,
    publicKey
  } = await keyPair;
  const encryptedText = await window.crypto.subtle.encrypt({
      name: "RSA-OAEP"
    },
    publicKey,
    encodedMessage
  )
  console.log(encryptedText);

  const decryptedText = await window.crypto.subtle.decrypt({
      name: "RSA-OAEP"
    },
    privateKey,
    encryptedText
  )
  console.log(decryptedText);
  console.log(dec.decode(decryptedText));

})()

In the code above, we first generate a key or key pair with the generateKey method with depending if the encryption algorithm is symmetric or asymmetric like we did with the sign and verify example. Asymmetric cryptographic algorithms have a public and private key like in the example above. RSA is an asymmetric algorithm.

Then we encode the message with the TextEncoder to encode it to a ArrayBuffer object which can used with the encrypt method.

Then we use the encrypt method with the algorithm, the public key, and the ArrayBuffer object with the encoded text passed int to encrypt the data. Then to decrypt the encrypted text, we use the decrypt method with the algorithm object passed in as the first argument, then we pass in the private key from the key pair, then we pass in the encrypted text as the third argument to the decrypt method.

This will get the decrypted data as an ArrayBuffer , which we will decode with the TextDecoder’s decode method with the decrypted ArrayBuffer to get back the original text. This means that the last console.log statement to get us back 'hello' .

The Crypto object also has one method, which is the getRandomValues method. The method will create a strong random value given a typed array. The method takes one argument. It takes a typed array, which is an Int8Array, a Uint8Array, an Int16Array, a Uint16Array, an Int32Array, or a Uint32Array. To improve performance, this method doesn’t generate numbers with a truly random number generator, but rather it uses a pseudo-random number generator to generate the number. The entries of the typed array passed into the argument will be overwritten by the random numbers generated by this method.

We can use the getRandomValues method like in the following example:

let array = new Uint32Array(10);
window.crypto.getRandomValues(array);

for (const num of array) {
  console.log(num);
}

In the code above, we generated a new Uint32Array, which we pass into the getRandomValues method. Then in the for...of loop, we get the generated values which overwrote whatever entries were in the original array. We should see 10 random numbers from the console.log, and each time we run the code above, we should get different results.

With the window.cryoto object, we can encrypt and decrypt data by using well-know cryptographic algorithms on the browser. It supports both symmetric and asymmetric encryption, which let us encrypt data with different algorithms. Also, we can use it to generate digital signatures and verify them. We can also use it to get random numbers with the getRandomValues method.

Categories
Web Components

Creating Web Components — Lifecycle Callbacks

As web apps get more complex, we need some way to divide the code into manageable chunks. To do this, we can use Web Components to create reusable components that we can use in multiple places.

In this article, we’ll look at the lifecycle hooks of Web Components and how we use them.

Lifecycle Hooks

Web Components have their own lifecycle. The following events happen in a Web Component’s lifecycle:

  • element is inserted into the DOM
  • updates when UI event is being triggered
  • element deleted from the DOM

A Web Component has lifecycle hooks, which are callback functions, to capture these lifecycle events and let us handle them accordingly.

They let us handle these events without creating our own system to do so. Most JavaScript frameworks provide the same functionality, but Web Components is a standard so we don’t need to load extra code to be able to use them.

The following lifecycle hooks are in a web component:

  • constructor()
  • connectedCallback()
  • disconnectedCallback()
  • attributeChangedCallback(name, oldValue, newValue)
  • adoptedCallback()

constructor()

The constructor() is called when the Web Component is created. It’s called when we create the shadow DOM and it’s used for setting up listeners and initialize a component’s state.

However, it’s not recommended that we run things like rendering and fetching resources here. The connectedCallback is better for these kinds of tasks.

Defining a constructor is optional for ES6 classes, but an empty one will be created when it’s undefined.

When creating the constructor, we’ve to call super() to call the class that the Web Component class extends.

We can have return statements in there and we can’t use document.write() or document.open() in there.

Also, we can’t gain attributes or children in the constructor method.

connectedCallback()

connectedCallback() method is called when an element is added to the DOM. We can be sure that the element is available to the DOM when this method is called.

This means that we can safely set attributes, fetch resources, run set up code or render templates.

disconnectedCallback()

This is called when the element is removed from the DOM. Therefore, it’s an ideal place to add cleanup logic and to free up resources. We can also use this callback to:

  • notify another part of an application that the element is removed from the DOM
  • free resources that won’t be garbage collected automatically like unsubscribing from DOM events, stop interval timers, or unregister all registered callbacks

This hook is never called when the user closes the tab and it can be trigger more than once during its lifetime.

attributeChangedCallback(attrName, oldVal, newVal)

We can pass attributes with values to a Web Component like any other attribute:

<custom-element
  foo="foo"
  bar="bar"
  baz="baz">
</custom-element>

In this callback, we can get the value of the attributes as they’re assigned in the code.

We can add a static get observedAttributes() hook to define what attribute values we observe. For example, we can write:

class CustomElement extends HTMLElement {
  constructor() {
    super();
    const shadow = this.attachShadow({
      mode: 'open'
    });
  }

  static get observedAttributes() {
    return ['foo', 'bar', 'baz'];
  }

  attributeChangedCallback(name, oldValue, newValue) {
    console.log(`${name}'s value has been changed from ${oldValue} to ${newValue}`);
  }
}

customElements.define('custom-element', CustomElement);

Then we should the following from the console.log :

foo's value has been changed from null to foo
bar's value has been changed from null to bar
baz's value has been changed from null to baz

given that we have the following HTML:

<custom-element foo="foo" bar="bar" baz="baz">
</custom-element>

We get this because we assigned the values to the foo , bar and baz attributes in the HTML with the values of the same name.

adoptedCallback()

The adoptedCallback is called when we call document.adoptNode with the element passed in. It only occurs when we deal with iframes.

The adoptNode method is used to transfer a node from one document to another. An iframe has another document, so it’s possible to call this with iframe’s document object.

Example

We can use it to create an element with text that blinks as follows:

class BlinkElement extends HTMLElement {
  constructor() {
    super();
  }

  connectedCallback() {
    const shadow = this.attachShadow({
      mode: 'open'
    });
    this.span = document.createElement('span');
    this.span.textContent = this.getAttribute('text');
    const style = document.createElement('style');
    style.textContent = 'span { color: black }';
    this.intervalTimer = setInterval(() => {
      let styleText = this.style.textContent;
      if (style.textContent.includes('red')) {
        style.textContent = 'span { color: black }';
      } else {
        style.textContent = 'span { color: red }';
      }

}, 1000)
    shadow.appendChild(style);
    shadow.appendChild(this.span);
  }

  disconnectedCallback() {
    clearInterval(this.intervalTimer);
  }

  static get observedAttributes() {
    return ['text'];
  }

  attributeChangedCallback(name, oldValue, newValue) {
    if (name === 'text') {
      if (this.span) {
        this.span.textContent = newValue;
      }

    }
  }
}

customElements.define('blink-element', BlinkElement);

In the connectedCallback , we have all the code for initializing the element. It includes adding a span for the text that we passed into the text attribute as its value, and the styles for blinking the text. The style starts with the creation of the style element and then we added the setInterval code to blink the text by changing the color of the span from red to black and vice versa.

Then we attached the nodes to the shadow DOM which we created at the beginning.

We have the observedAttributes static method to set the attributes to watch for. This is used by the attributeChangedCallback to watch for the value of the text attribute and set it accordingly by getting the newValue .

To see the attributeChangedCallback in action, we can create the element dynamically and set its attribute as follows:

const blink2 = document.createElement('blink-element');
document.body.appendChild(blink2);
blink2.setAttribute('text', 'bar');
blink2.setAttribute('text', 'baz');

Then we should bar and baz if we log newValue in the attributeChangedCallback hook.

Finally, we have the disconnectedCallback which is run when the component is removed. For example, when we remove an element with removeChild as in the following code:

const blink2 = document.createElement('blink-element');
document.body.appendChild(blink2);
blink2.setAttribute('text', 'bar');
document.body.removeChild(blink2);

The lifecycle hooks allow us to handle various DOM events inside and outside the shadow DOM. Web Components have hooks for initializing, removing, and attribute changes. We can select which attributes to watch for changes.

There’re also hooks for adopting elements in another document.

Categories
Web Components

Creating Web Components — Templates and Slots

As web apps get more complex, we need some way to divide the code into manageable chunks. To do this, we can use Web Components to create reusable components that we can use in multiple places.

In this article, we’ll look at how to use templates and slots with Web Components.

Templates

We can use the template element to add content that’s not rendered in the DOM. This lets us incorporate them into any other element by writing the following HTML:

<template>
  <p>Foo</p>
</template>

Then we can write the following JavaScript to incorporate it into the page:

const template = document.querySelector('template');
const templateContent = template.content;
document.body.appendChild(templateContent);

We should see ‘Foo’ on the page when we load the page. The p element should be showing when we inspect the code for ‘Foo’. This confirms that the markup isn’t affected by the template element.

Using Templates with Web Components

We can also use them with Web Components. To use it, we can get the template element in the class for the Web Component just like we do outside the Web Component.

For example, we can write:

customElements.define('foo-paragraph',
  class extends HTMLElement {
    constructor() {
      super();
      let template = document.querySelector('template');
      let templateContent = template.content;
      const shadowRoot = this.attachShadow({
          mode: 'open'
        })
        .appendChild(templateContent.cloneNode(true));
    }
  })

In the code above, we get the template element and the call cloneNode on templateContent which has template.content as the value to get the template’s content.

Then we call cloneNode to clone the template.content node’s children in addition to the node itself. This is indicated by the true argument of the cloneNode function call.

Finally, we attached the cloned templateContent into the shadow root of the Web Component with the attachShadow and appendChild methods.

We can also include styling inside a template element, so we can reuse it as we do with the HTML markup.

For instance, we can change the template element to:

<template>
  <style>
    p {
      color: white;
      background-color: gray;
      padding: 5px;
    }

  </style>
  <p>Foo</p>
</template>

Then we should see:

Slots

To make our templates more flexible, we can add slots to them to display content as we wish instead of static content. With slots, we can pass in content between our Web Component opening and closing tags.

It has more limited support than templates. Slots can be used with Chrome 53 or later, Opera since version 40, Firefox since version 59, and not supported in Edge.

For example, we can add some slots by writing:

<template>
  <slot name="foo">Default text for foo</slot>
  <slot name="bar">Default text for bar</slot>
  <slot name="baz">Default text for baz</slot>
</template>

Then given that we have the same code as before for defining the foo-paragraph element in JavaScript, we can use it as follows:

<foo-paragraph>
  <p slot='foo'>foo</p>
  <p slot='bar'>bar</p>
  <p slot='baz'>baz</p>
</foo-paragraph>

The output then will be:

foo

bar

baz

If we omit the elements inside the foo-paragraph tags, we get the default text:

Default text for foo Default text for bar Default text for baz

In the code above, we name the slots with the name attribute in the template element, then in our Web Component, we fill in the slots that we defined by using the slot attribute.

We can style them like any other template element. To style them, we select the elements to be styled by using the selector for the elements that we’ll fill into the slots.

For example, we can write:

<template>
  <style>
    ::slotted(p) {
      padding: 5px;
      background-color: black;
      color: white;
    }

    ::slotted(p[slot='foo']) {
      background-color: gray;
    }
  </style>
  <slot name="foo">Default text for foo</slot>
  <slot name="bar">Default text for bar</slot>
  <slot name="baz">Default text for baz</slot>
</template>

<foo-paragraph>
  <p slot='foo'>foo</p>
  <p slot='bar'>bar</p>
  <p slot='baz'>baz</p>
</foo-paragraph>

The ::slotted pseudo-element represents elements that has been placed into a slot inside the HTML template. Therefore, we can use it to select the slot elements that we want to style.

In the argument of ::slotted , we can pass in the element that we want to style if they were passed into the slot. This means that only p elements that are passed in will have styles applied to them.

::slotted(p[slot=’foo’]) means that a p element with slot attribute values foo will have styling applied to it.

Then we should get the following after applying the styles:

Templates and slots lets us create reusable components that we can use in Web Components. template element do not show up during render so we can put them anywhere and use them as we wish in any Web Component.

With slots, we have more flexibility when creating Web Components since we can create components that we can insert other elements into to fill the slots.

In any case, we can apply styles to the elements as we wish. We have the ::slotted pseudoelement to apply styles to specific elements in slots.

Categories
TypeScript

Cool New Features Released with TypeScript 3.6

Lots of new features are released with TypeScript 3.6. It includes features for iterables like stricter type checking for generators, more accurate array spreading, allow get and set in declare statements, and more.

In this article, we’ll look at each of them.

Stricter Type Check for Generators

With TypeScript 3.6, the TypeScript compiler has more checks for data types in generators.

Now we have a way to differentiate whether our code yield or return from a generator.

For example, if we have the following generator:

function* bar() {
    yield 1;
    yield 2;
    return "Finished!"
}

let iterator = bar();
let curr = iterator.next();
curr = iterator.next();

if (curr.done) {
    curr.value
}

The TypeScript 3.6 compiler knows automatically that curr.value is a string since we returned a string at the end of the function.

Also, yield isn’t assumed to be of any type when we assign yield to something.

For instance, we have the following code:

function* bar() {
    let x: { foo(): void } = yield;
}

let iterator = bar();
iterator.next();
iterator.next(123);

Now the TypeScript compiler knows that the 123 isn’t assignable to something with the type { foo(): void } , which is the type of x . Whereas in earlier versions, the compiler doesn’t check the type of the code above.

So in TypeScript 3.6 or later, we get the error:

Argument of type '[123]' is not assignable to parameter of type '[] | [{ foo(): void; }]'.

Type '[123]' is not assignable to type '[{ foo(): void; }]'.

Type '123' is not assignable to type '{ foo(): void; }'.

Also, now the type definitions for Generator and Iterator have the return and throw methods present and iterable.

TypeScript 3.6 converts the IteratorResult to the IteratorYieldResult<T> | IteratorReturnResult<TReturn> union type.

It can also infer the value that’s returned from next() from where it’s called.

For example, the following would compile and run since we passed in the right type of value into next() , which is a string:

function* bar() {
    let x: string = yield;
    console.log(x.toUpperCase());
}

let x = bar();
x.next();
x.next('foo');

However, the following would fail to compile because of type mismatch between the argument and the type of x :

function* bar() {
    let x: string = yield;
    console.log(x.toUpperCase());
}

let x = bar();
x.next();
x.next(42);

We would have to pass in a string to fix the that arises from x.next(42); . Also, the TypeScript compiler knows that the first call to next() does nothing.

More Accurate Array Spread

With TypeScript 3.6, the transformation of some array spread operators now produce equivalent results when the code is transpiler to ES5 or earlier targets with the --downlevelIteration on.

The flag’s purpose is to transform ES6 iteration constructs like the spread operator and for...of loop to add support for ES6 iteration constructs into code that’s transpiled to something older than ES6.

For example, if we have:

[...Array(3)]

We should get:

[undefined, undefined, undefined]

However, TypeScript versions earlier than 3.6 changes […Array(3)] to Array(3).slice();

Which gets us an empty array with length property set to 3.

Raise Error with Bad Promise Code

TypeScript 3.6 compiler will let us know if we forget to put await before promises in async functions or forget to call then after promises.

For example, if we have:

interface Person {
    name: string;
}

let promise: Promise<Person> = Promise.resolve(<Person>{ name: 'Joe' });
(async () => {
    const person: Person = promise;
})();

Then we get the error:

Property 'name' is missing in type 'Promise<Person>' but required in type 'Person'.

Putting in await before promise would fix the problem:

interface Person {
    name: string;
}

let promise: Promise<Person> = Promise.resolve(<Person>{ name: 'Joe' });
(async () => {
    const person: Person = await promise;
})();

Something writing something like:

(async () => {
      fetch("https://reddit.com/r/javascript.json")
        .json()
})();

will get us the error:

Property 'json' does not exist on type 'Promise<Response>'.(2339)

input.ts(3, 10): Did you forget to use 'await'?

The following will fix the error:

(async () => {
  const response = await fetch("[https://reddit.com/r/javascript.json](https://reddit.com/r/javascript.json)")
  const responseJson = response.json()
})();

Unicode Character Identifiers

Now we can use Unicode characters for identifiers with TypeScript. For example:

const ? = 'foo';

would work with TypeScript 3.6 compiler or later.

get and set Accessors Are Allowed in Declare Statements

We can add get and set to declare statements now. For example, we can write:

declare class Bar {
  get y(): number;
  set y(val: number);
}

The generated type definitions will also emit get and set accessors in TypeScript 3.7 or later.

Merging Class and Constructor Function Declare Statements

With TypeScript 3.6 or later, the compiler is smart enough to merge function constructors and class declare statements with the same name. For example, we can write:

export declare function Person(name: string, age: number): Person;
export declare class Person {
    name: string;
    age: number;
    constructor(name: string, age: number);
}

It knows that the function is a constructor and the class is the same as the function.

The signatures of the constructors in the function and class constructor don’t have to match, so the following:

export declare function Person(name: string): Person;
export declare class Person {
    name: string;
    age: number;
    constructor(name: string, age: number);
}

still works.

Semicolons

Now TypeScript is smart enough to add semicolons automatically to places that requires it by style conventions instead of adding it automatically to every statement.

TypeScript 3.6 is another feature-packed release. It focuses on improving features like inferring types and type checks in generators, more accurate array spread for code emitted in ES5 or earlier.

Also, bad promise code will raise errors, like the ones that missed await or then .

Merging function constructor and class code in declare statements are also supported now.

Unicode characters are now supported in identifiers, and semicolons won’t be added automatically on every line.

Categories
JavaScript APIs

Checking Network Status with the Network Information API

With the advent of mobile devices like phones and tablets, knowing the connection status is very important since it can change any time, affecting user experience in the process.

We also have to be aware of different kinds of Internet connections since they vary widely in speed.

Fortunately, we have the Network Information API built into browsers to check for Internet connection status.

This API is available for browsing and worker contexts.

In this article, we’ll look at how to use the API to get network connection type changes and connection status.

Detect Connection Changes

Detecting connection changes is simple. We can use the navigation.connection object to listen to network type changes as follows:

const connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection;
let type = connection.effectiveType;

const updateConnectionStatus = () => {
  console.log(`Connection type changed from ${type} to ${connection.effectiveType}`);
  type = connection.effectiveType;
}

connection.addEventListener('change', updateConnectionStatus);

We can then test the connection type changes by going to the Chrome developer console by pressing F12. Then go to the Network tab, and then on the top right, there’s a dropdown to change the connection type.

If we switch between them, we should see something like:

Connection type changed from 4g to 3g
Connection type changed from 3g to 4g
Connection type changed from 4g to 2g

from the console.log output.

The connection object is a NetworkInformation object instance and it has the following properties:

  • downlink — effective bandwidth estimate in megabits per second rounded to the nearest 25 kbps.
  • downlinkMax — maximum downlink speed, in Mbps for the underlying connection technology
  • effectiveType — the type of connection determined by a combination of recently observed round-trip time and downlink values. It can be one of ‘slow-2g’, ‘2g’, ‘3g’, or ‘4g’.
  • rtt — estimated effective round-trip time of the current connection rounded to the nearest multiple of 25 milliseconds.
  • saveData — boolean indicating if reduced data usage optional has been set
  • type — type of connection to communicate with the network. It can be one of bluetooth, cellular, ethernet, none, wifi, wimax, other, unknown

Compatibility

This API is new and experimental, so we’ve to be careful when we use it. Chrome supports most properties out of the box since Chrome 61. Some options like downlinkMax and type are only available for Chrome OS. Firefox and Edge do not support this API.

It’s also available for use in other Chromium-based browsers like Opera, Android Webview, and Chrome for Android.

With the Network Information API, we can get information about the network connection. This is useful for detecting the connection type of mobile devices and lets us change the web page to accommodate slower connections accordingly.