Categories
JavaScript JavaScript Basics

Use the JavaScript Notification API to Display Native Popups

The Notifications API lets us display popups that show up as a native desktop or mobile notification. The functionality varies across platforms but they generally provide a way to asynchronously provide information to the user.

Create a New Notification

We can create a new notification with the Notification constructor. It takes 2 arguments. The first is the title, and the second is an object with a variety of properties and is optional:

  • dir: the direction of the displayed notification. Default value is auto, but it can also be rtl for right to left or ltr for left to right.
  • lang: string value for the language. Possible values are BCP 47 language tags.
  • badge: string which contains the URL for an image used to represent the notification when there isn’t enough space to display it.
  • body: a string with the text of the notification.
  • tag: a string with the identifying tag of the notification
  • icon: URL string with the icon’s URL
  • image: URL string for the image to be displayed.
  • data: data we want to be associated with the notification.
  • vibrate: vibration pattern for devices that vibrate.
  • renotify: boolean value specifying whether the user should be notified after a new notification replaces the old one. Default value is false.
  • requireInteraction: indicates whether the notification should remain active until the user clicks or dismisses it. Default value is false.
  • actions: an array of NotificationAction which have actions that are available to the user when the notification is displayed. It’s an object with a name, title, and icon properties.

We can define a simple notification as follows:

const options = {  
  body: "body",  
  icon:  
    "https://www.iconninja.com/files/926/373/306/link-chain-url-web-permalink-web-address-icon.png"  
};const n = new Notification("title", options);

To see the notification, we have to set Notification to always display in our browser.

We should see the text we set and the icon we specified in the icon property.

Methods of the Notification Object

Requesting Permission

We can request permission with the requestPermission static method. It returns a promise which resolves when the permission for showing the notification is allowed or denied.

It resolves with an object which has the permission data.

The browser will ask for permission to display notifications for the domain when we run this method.

For example, we can use it as follows:

(async () => {  
  try {  
    const permission = await Notification.requestPermission();  
    console.log(permission);  
    const options = {  
      body: "body",  
      icon:  
        "https://www.iconninja.com/files/926/373/306/link-chain-url-web-permalink-web-address-icon.png"  
    };  
    const n = new Notification("title", options);  
  } catch (error) {  
    console.log(error);  
  }  
})();

If permission is granted, the console.log in the try block will log granted. Otherwise, it will log denied from the console.log in the catch block.

Closing the Notification Programmatically

We can close a notification programmatically with the close method, which is an instance method of a Notification object.

For example, we can use it as follows:

(async () => {  
  try {  
    const permission = await Notification.requestPermission();  
    console.log(permission);  
    const options = {  
      body: "body",  
      icon:  
        "https://www.iconninja.com/files/926/373/306/link-chain-url-web-permalink-web-address-icon.png"  
    };  
    const n = new Notification("title", options);  
    await new Promise(resolve => {  
      setTimeout(() => {  
        n.close();  
        resolve();  
      }, 5000);  
    });  
  } catch (error) {  
    console.log(error);  
  }  
})();

In the example above, we called close inside the callback of the setTimeout method. This makes it close automatically after 5 seconds.

Event Handlers

Notification objects also have their own event handlers. They events are onclick, onclose, onerror, and onshow. We can assign our own event handler functions to them.

onclick

We can assign an event handler to the onclick property when we want to do something when the notification is clicked. For example, we can write:

(async () => {  
  try {  
    const permission = await Notification.requestPermission();  
    console.log(permission);  
    const options = {  
      body: "body",  
      icon:  
        "https://www.iconninja.com/files/926/373/306/link-chain-url-web-permalink-web-address-icon.png"  
    };  
    const n = new Notification("title", options);  
    n.onclick = () => {  
      alert("Notification clicked");  
    };  
  } catch (error) {  
    console.log(error);  
  }  
})();

This shows an alert in the browser tab when our notification is clicked. The event handler function can take one parameter, which is the event object.

The default behavior is to move focus to the viewport of the notification’s related browsing context. We can call preventDefault() on the event parameter that we pass in to prevent that as follows:

(async () => {  
  try {  
    const permission = await Notification.requestPermission();  
    console.log(permission);  
    const options = {  
      body: "body",  
      icon:  
        "https://www.iconninja.com/files/926/373/306/link-chain-url-web-permalink-web-address-icon.png"  
    };  
    const n = new Notification("title", options);  
    n.onclick = event => {  
      event.preventDefault();  
      alert("Notification clicked");  
    };  
  } catch (error) {  
    console.log(error);  
  }  
})();

We can make the notification do something when it’s closed by assigning an event handler function to the onclose property.

Likewise, we can do the same for the onerror property to handle errors and the onshow property to handle the show event, which is fired when the notification is displayed.

Conclusion

As we can see, the Notification API is a really simple way to display native notifications from the web apps we write. We ask for permission to display notifications with the static Notification.requestPermission method.

Once the promise is resolved when the user allows notifications to be displayed, then we just create a Notification object with the options we want. Then the notification will be displayed.

Categories
JavaScript JavaScript Basics

Useful DOM Traversal Methods

The main use of client-side JavaScript is to manipulate web pages dynamically. We can do this with the DOM traversal methods and properties available to DOM Node objects.

Adding or changing child and sibling elements is easy for any given Node since there are properties built into DOM Node objects to perform these actions. The following are methods of a DOM Node object for getting parent, child, and sibling nodes or elements.

appendChild

The appendChild methods let us attach a child node to a given HTML element as the last child node of the current node. If the argument referenced an existing node on the DOM tree then the node will be detached from its current position and attached to its new position.

It takes one argument, which is a DOM Node object.

For example, given 2 existing nodes in the following HTML:

<div id='foo'>  
  foo  
</div>  
<div id='bar'>  
  bar  
</div>

We can attach the element with ID bar as a child to the element with ID bar as follows:

const foo = document.querySelector('#foo');  
const bar = document.querySelector('#bar');
foo.appendChild(bar);

Once we ran that, we should get the following structure:

<div id="foo">  
  foo  
  <div id="bar">  
    bar  
  </div>  
</div>

We can also use it to create an element that’s created on the fly. For example, if we have the following HTML:

<div id='foo'>  
  foo  
</div>

Then we can write the following code to attach a new p element to the div with ID foo:

const foo = document.querySelector('#foo');  
const bar = document.createElement('p');  
bar.textContent = 'bar';
foo.appendChild(bar);

In the code above, we used createElement to create a new p element. Then we set the textContent property to add text inside the p element. At the end, we can appendChild on foo with bar as the argument to attach bar as the child of foo .

cloneNode

The cloneNode method clones a Node object and optionally, all of its content. It doesn’t clone all the content of the Node by default.

It takes one argument, which is an optional argument indicate if it’s a deep clone, which means everything will be cloned. true means do a deep clone and false otherwise.

For example, given the following HTML:

<div>  
  foo  
</div>

We can write the following JavaScript code to clone the div and then attach it to the body element as the last child:

const fooDiv = document.querySelector('div');  
const fooClone = fooDiv.cloneNode(true);  
document.body.appendChild(fooClone);

We pass in true to the cloneNode method to clone everything. Then we call appendChild on document.body with the cloned object passed in as the argument to add it as the child of body.

compareDocumentPosition

The compareDocumentPosition method compares the position of the given node against another node in any document. It takes a DOM Node object as its argument.

It returns a bitmask with the following possible values

  • DOCUMENT_POSITION_DISCONNECTED — 1
  • DOCUMENT_POSITION_PRECEDING — 2
  • DOCUMENT_POSITION_FOLLOWING — 4
  • DOCUMENT_POSITION_CONTAINS — 8
  • DOCUMENT_POSITION_CONTAINED_BY — 16
  • DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC — 32

For example, given the following HTML:

<div id='foo'>  
  foo  
</div>  
<div id='bar'>  
  bar  
</div>

We can write the following JavaScript to compare the position of the div with ID foo and the one with ID bar:

const foo = document.querySelector('#foo');  
const bar = document.querySelector('#bar');
const relativePos = foo.compareDocumentPosition(bar);  
console.log(relativePos);

The code above should get us 4 for relativePos, which means the element with ID bar follows the element with ID foo.

contains

The contains method checks if a DOM node is inside the given node. It takes one argument, which is a Node object that we want to check if it’s inside the one that this method is called on.

It returns true if the node in the argument is inside the one that’s called and false otherwise.

For example, given the following HTML:

<div id='foo'>  
  foo  
</div>  
<div id='bar'>  
  bar  
</div>

Then we can write the following JavaScript to check if the div with ID bar is inside the div with ID foo:

const foo = document.querySelector('#foo');  
const bar = document.querySelector('#bar');
const fooContainsBar = foo.contains(bar);  
console.log(fooContainsBar);

Of course, fooContainsBar should be false since the div with ID foo isn’t inside the div with ID bar.

On the other hand, if we have the following HTML instead:

<div id='foo'>  
  foo  
  <div id='bar'>  
    bar  
  </div>
</div>

Then with the same JavaScript code as the first example, fooContainsBar should true since div with ID foo is inside the div with ID bar.

getRootNode

The getRootNode method returns the Node object’s root, which optionally includes the shadow root if it’s available.

It takes an optional argument with an object with the following properties:

  • composed — a boolean that indicates rather the shadow root should be included. It defaults to false

It returns the Node that either returns an element that’s the root of the given Node or the shadow root will be returned for elements inside the shadow DOM.

For example, if we have the following HTML:

<div id='foo'>  
  foo  
</div>

Then we can call the getRootNode method as follows:

const foo = document.querySelector('#foo');const root = foo.getRootNode();  
console.log(root);

We should get the HTML document as the root node since it’s not inside a shadow DOM.

The root will be the shadow root for Web Components. For example, if we have the following web component:

customElements.define('custom-p',  
  class extends HTMLElement {  
    constructor() {  
      super(); const pElem = document.createElement('p');  
      pElem.id = 'p-element';  
      pElem.textContent = 'shadow p' 
      const shadowRoot = this.attachShadow({  
        mode: 'open'  
      });  
      shadowRoot.appendChild(pElem);  
    }  
  }  
);

And we have the Web Component added in the HTML code:

<custom-p></custom-p>

Then we can get the root node of the element with the ID p-element by writing:

const pElement = document.querySelector('custom-p').shadowRoot.querySelector('#p-element');  
const rootNode = pElement.getRootNode();  
console.log(rootNode);

First, we get the custom element, then we get the shadowRoot property which lets us access the shadow DOM of our custom-p web component. Then we can get the p element with the ID p-element with the shadow root.

After that, we get the root node of it by callinggetRootNode on pElement which represents p element with the ID p-element .

The console.log should get us the shadow root of the Web Component.

Conclusion

With these DOM traversal methods, we can set the nodes however we like for simple situations. Also, there’re methods to check whether an element is the child of another one, and also to get the root node of the given element.

Categories
JavaScript JavaScript Basics

Useful Lodash Array Functions — Extraction and Intersection

Lodash is a utility library that has lots of methods for manipulating objects. It has stuff that we use all the time and also things that we don’t use frequently or don’t think of using.

In this article, we’ll look at more useful Lodash array methods, including the head , indexOf , initial , intersection , intersectionBy , and intersectionWith methods.

head

The head method gets the first element of an array and returns it. first is an alias of head .

It takes an array as its only argument.

We can use it as follows:

import * as _ from "lodash";  
const array = [1, 2, 3];  
const result = _.head(array);  
console.log(result);

Then we get 1 for result . head returns undefined is the array passed in is empty.

indexOf

indexOf gets the index of the first occurrence of the item found in the array that’s passed into the argument and returns it.

It takes up to 3 arguments. The first is the array to search. The second argument is the value to look for. The third is an optional argument for the index to start the search from.

For example, we can use it as follows:

import * as _ from "lodash";  
const array = [1, 2, 3];  
const result = _.indexOf(array, 2, 1);  
console.log(result);

Then we get 1 since 2 is in the 2nd position and we search from the start.

initial

The initial method gets all but the last element of an array and returns it.

It takes an array as its only argument.

For example, we can use it as follows:

import * as _ from "lodash";  
const array = [1, 2, 3];  
const result = _.initial(array);  
console.log(result);

Then we get [1, 2] for result .

intersection

The intersection method returns an array of values that are included in all given arrays. Equality comparison is done with the SameValueZero comparison which is the same as === except that NaN is considered equal to itself.

It takes a comma-separated list of arrays to return the intersection from.

For example, we can use it as follows:

import * as _ from "lodash";  
const arr1 = [1, 2, 3];  
const arr2 = [3, 4, 5];  
const result = _.intersection(arr1, arr2);  
console.log(result);

Then we get [3] since only 3 are present in both arr1 and arr2 .

intersectionBy

intersectionBy is like intersection except that it takes a function which is invoked for each element of each array to generate the criterion for which items are compared. The first array determines the order and reference of results.

It takes a comma-separated list of arrays as arguments and a function for the criterion to compare to find the intersection with or a string with the property name to compare.

For example, we can use it as follows:

import * as _ from "lodash";  
const arr1 = [  
  { name: "Joe", age: 10 },  
  { name: "Mary", age: 12 },  
  { name: "Jane", age: 13 }  
];  
const arr2 = [  
  { name: "Joe", age: 10 },  
  { name: "Jerry", age: 12 },  
  { name: "Amy", age: 13 }  
];  
const result = _.intersectionBy(arr1, arr2, a => a.name);  
console.log(result);

Then we get:

[  
  {  
    "name": "Joe",  
    "age": 10  
  }  
]

for result .

We can replace a => a.name with 'name' as follows:

import * as _ from "lodash";  
const arr1 = [  
  { name: "Joe", age: 10 },  
  { name: "Mary", age: 12 },  
  { name: "Jane", age: 13 }  
];  
const arr2 = [  
  { name: "Joe", age: 10 },  
  { name: "Jerry", age: 12 },  
  { name: "Amy", age: 13 }  
];  
const result = _.intersectionBy(arr1, arr2, "name");  
console.log(result);

Then we get the same thing.

intersectionWith

intersectionWith is like intersection except that it takes a comparator function as the last argument. The parameters for the function are 2 entries of from the arrays to compare against.

It takes a comma-separated list of arrays as arguments and the comparator function as the last argument.

For example, we can use it as follows:

import * as _ from "lodash";  
const arr1 = [  
  { name: "Joe", age: 10 },  
  { name: "Mary", age: 12 },  
  { name: "Jane", age: 13 }  
];  
const arr2 = [  
  { name: "Joe", age: 10 },  
  { name: "Jerry", age: 12 },  
  { name: "Amy", age: 13 }  
];  
const result = _.intersectionWith(arr1, arr2, (a, b) => a.name === b.name);  
console.log(result);

Then we get:

[  
  {  
    "name": "Joe",  
    "age": 10  
  }  
]

for result .

The head method gets the first element of an array and returns it. first is an alias of head .

indexOf gets the index of the first occurrence of the item found in the array that’s passed into the argument and returns it. It can take a starting index to search from which defaults to 0.

initial gets all but the last element of an array and returns it.

To find array entries that are in all arrays, we can use the intersection , intersectionBy , and intersectionWith methods. They differ in that they may take functions for the criterion to compare or a comparator method to compare for equality respectively.

Categories
JavaScript JavaScript Basics

Useful DOM Traversal Properties

The main use of client-side JavaScript is to manipulate web pages dynamically. We can do this with the DOM traversal methods and properties available to DOM Node objects.

Getting child and sibling elements is easy for any given Node since there are properties built into DOM Node objects to do so. The following are properties of a DOM Node object for getting parent, child and sibling nodes or elements.

childNodes

The childNodes property let us get the child nodes of a Node object. It returns a NodeList object, which is an array-like object that we can loop through with the for and for...of loop and spread with the spread operator. It’s a read-only property.

For example, we can use it as follows:

const divChildren = document.querySelector('div').childNodes;
for (let c of divChildren) {  
  console.dir(c);  
  console.log(c.textContent);  
}

We get the text nodes as well as the p nodes as the children, so the newline character and the p elements are both includes. The console.log and console.dir statements above should reflect that.

We can change the example above to change the color of the text on the fly. For example, we can write:

const divChildren = document.querySelector('div').childNodes;
for (let c of divChildren) {  
  if (c.style) {  
    c.style.color = 'red';  
  }  
}

to change all the p elements to red. We have to check the style attribute since the text nodes don’t have the style property.

firstChild

The firstChild property gets us the first child of the given Node object. For example, if we have the following HTML:

<div>  
  <p>  
    foo  
  </p>  
  <p>  
    bar  
  </p>  
  <p>  
    baz  
  </p>  
</div>

We get that the first child is a text node with a newline character with the following JavaScript:

const firstChild = document.querySelector('div').firstChild;
console.log(firstChild)

This property is a read-only property. If there’re no child nodes then it’ll return null .

lastChild

The lastChild property gets us the first child of the given Node object. For example, if we have the following HTML:

<div>  
  <p>  
    foo  
  </p>  
  <p>  
    bar  
  </p>  
  <p>  
    baz  
  </p>  
</div>

We get that the last child is a text node with a newline character with the following JavaScript:

const lastChild = document.querySelector('div').lastChild;console.log(lastChild)

This property is a read-only property. If there’re no child nodes then it’ll return null .

nextSibiling

The nextSibling property is a read-only property that represents the next Node in the tree or null if none exists.

For example, if we have the following HTML:

<div>  
  foo  
</div>  
<div>  
  bar  
</div>

Then we can use the nextSibling property as follows:

const nextSibling = document.querySelector('div').nextSibling;  
console.log(nextSibling)

We should get a text node with a new line character as the node from the nextSibling .

To get the second div in the HTML, we can chain the nextSibling property as follows:

const barNode = document.querySelector('div').nextSibling.nextSibling;  
barNode.style.color = 'red';

In the code above, we set the second div’s color to red.

parentNode

The parentNode property is a read-only property that gets us the parent node of the given node or null if it doesn’t exist. Nodes can include HTML elements or other nodes like text nodes or comment nodes.

For example, given the following HTML:

<div>  
  foo  
</div>  
<div>  
  bar  
</div>

We can write the following code to get the parent node:

const parentNode = document.querySelector('div').parentNode;
console.log(parentNode);

The console.log should get us the body node from as the parent.

We can do things to the parent node once we have it. For example, we can write:

const parentNode = document.querySelector('div').parentNode;
parentNode.style.color = 'red';

to change all the text red.

parentElement

The parentElement property is a read-only property that gets us the parent element of the given node. If the node has no parent or the parent node isn’t an HTML element, then it returns null .

For example, given the following HTML:

<div>  
  bar  
  <div id='foo'>  
    foo  
  </div>  
</div>

We can write the following JavaScript to get the parent element of the element with the ID foo :

const parentElement = document.querySelector('#foo').parentElement;
parentElement.style.color = 'red';

In the code above, we get the parent element of foo and set its color style to the color red, so we should see all the text having the color red because of the parent of foo is our outer div.

previousSibling

The previousSibling property is a read-only property that returns the previous node in the DOM tree, or null if it doesn’t exist.

For example, if we have the given HTML:

<div>  
  foo  
</div>  
<div id='bar'>  
  bar  
</div>

Then we can get the previous sibling as follows:

const previousSibling = document.querySelector('#bar').previousSibling;
console.log(previousSibling);

We can see that the previous sibling is the text node with the newline character.

To get the div with the word ‘foo’, we can write:

const foo = document.querySelector('#bar').previousSibling.previousSibling;
foo.style.color = 'red';

The first line gets the div with the word ‘foo’. Then the last line sets it to red.

textContent

The textContent is a readable and writable property that lets us set the textual content of an element and all its descendants. For example, given an empty p element:

<p></p>

We can set its text content to ‘foo’, by writing the following:

const p = document.querySelector('p')  
p.textContent = 'foo';

The DOM Node object methods are very useful for getting the parent, child and sibling elements. Most of the properties are read-only since they’re set somewhere else. For the properties that get all kinds of Node objects, we have to be careful not the wrong type of Node. This means that we can’t assume all Nodes are HTML elements.