Categories
JavaScript APIs

Getting Browser User Permission with the Permissions API

We have to get user consent when we do anything that changes the user experience. For example, we need to get user permission to show notifications or access their hardware.

Modern browsers are standardized in how permissions are obtained. It’s all in the Permissions API.

API that relies on the Permissions API for permissions include the Clipboard API, Notifications API, Push API, and Web MIDI API.

More and more APIs will use the Permissions API to obtain user consent in the future.

The navigator object has the permissions property in both the standard browser and worker contexts. It gives us a way to get the permissions.

The way for requesting permissions is different for each API.

This API is experimental so check if the browsers we want to support are supported before using this API.

Querying Permissions

The navigation.permissions object lets us query permissions set with the query method.

It returns a promise which gets us the state of the permission. For example, we can write the following to check for permission for the geolocation API:

(async () => {
  const result = await navigator.permissions.query({
    name: 'geolocation'
  });
  console.log(result.state);
})();

The possible values for result.state are 'granted', 'denied', or 'prompt'.

There’s also one for Web Workers that do the same thing.

The object that we passed into the query method is the permission descriptor object, which has the following properties:

  • name — name of the API which we want to get the permission for. Firefox only supports geolocation, notifications, push, and persistent-storage
  • userVisibleOnly — indicates whether we want to show notification for every message or be able to send silent push notifications. Default value is false .
  • sysex — applies to MIDI only. Indicates whether we need to receive system exclusive messages. Default value is false .

Requesting Permission

Requesting permission is different between APIs. For example, the Geolocation API will show a prompt for permission when we call getCurrentPosition() as follows:

navigator.geolocation.getCurrentPosition((position) => {
  console.log('Geolocation permissions granted');
  console.log(`Latitude: ${position.coords.latitude}`);
  console.log(`Longitude: ${position.coords.longitude}`);
});

The Notifications API has a requestPermission method to request permissions:

Notification.requestPermission((result) => {
  console.log(result);
});

Watching for Permission Changes

The navigator.permissions.query promise resolves to a PermissionStatus object. We can use it to set an onchange event handler function to watch for permission changes.

For example, we can expand on this example to add an event handler function to watch for changes for the permission of the Geolocation API:

(async () => {
  const result = await navigator.permissions.query({
    name: 'geolocation'
  });
  console.log(result.state);
})();

We can write:

(async () => {
  const permissionStatus = await navigator.permissions.query({
    name: 'geolocation'
  });
  permissionStatus.onchange = () => {
    console.log(permissionStatus.state);
  }
})();

The permissionStatus.state will have the permission for the Geolocation API.

It’ll log whenever the permission changes, such as when we change the permission setting in the browser.

Revoking Permissions

We can revoke permission by using the revoke method.

To call it, we can access it from the navigator.permissions object as follows:

navigator.permissions.revoke(descriptor);

Where descriptor is the permission descriptor which we described above.

It returns a Promise that resolves with the permission status object indicating the result of the request.

This method throws a TypeError if getting the permission descriptor failed or the permission doesn’t exist or is unsupported by browsers.

The name property of the permission descriptor can accept one of the following values 'geolocation', 'midi', 'notifications', and 'push'.

Other properties remain the same as the query method.

For example, we can use it by writing:

(async () => {
  const permissionStatus = await navigator.permissions.revoke({
    name: 'geolocation'
  });
  console.log(permissionStatus);
})();

Note that we’ve to set dom.permissions.revoke.enable to true in Firefox 51 or later to use this method. It’s not enabled in Chrome.

The Permissions API lets us check and revoke browser permissions in a uniform way.

Requesting permissions are different for each API. This isn’t changing any time soon.

This is an experimental API, so using it in production is probably premature.

Categories
JavaScript

Introducing the JavaScript Window Object — Location Property Introduction

The window object is a global object that has the properties pertaining to the current DOM document, which is the things that are in the tab of a browser. The document property of the window object has the DOM document and associated nodes and methods that we can use to manipulate the DOM nodes and listen to events for each node. Since the window object is global, it’s available in every part of the application. When a variable is declared without the var , let or const keywords, they’re automatically attached to the window object, making them available to every part of your web app. This is only applicable when strict mode is disabled. If it’s enabled, then declaring variables without var , let , or const will be stopped an error since it’s not a good idea to let us declare global variables accidentally. The window object has many properties. They include constructors, value properties and methods. There’re methods to manipulate the current browser tab like opening and closing new popup windows, etc.

In a tabbed browser, each tab has its own window object, so the window object always represent the state of the currently opened tab in which the code is running. However, some properties still apply to all tabs of the browser like the resizeTo method and the innerHeight and innerWidth properties.

Note that we don’t need to reference the window object directly for invoking methods and object properties. For example, if we want to use the window.Image constructor, we can just write new Image() . In this article, we continue to look at what’s in the window object. In the previous sections, we looked at the constructors and some of the properties of the window object, including using the customElements to build a Web Component, and the crypto object to do cryptography on the client using symmetric and asymmetric encryption algorithms. In this part, we will look at properties of the window.document object, including the properties of the window.document.location object.

window.document.location

The document.location is a read only property returns a Location object, which is information about the URL of the current document and gives us methods for changing URLs and loading a different URL. Even though it’s a read only Location object, we assign a string to it, to load a different URL than the URL of current page. For example, if we want to load 'https://medium.com' , we can assign it straight to the document.location property like we do with the following code:

document.location = '[https://medium.com'](https://medium.com%27);

This is the same as assigning the same URL to the document.location.href property:

document.location.href = '[https://medium.com'](https://medium.com%27);

Both pieces of code will load https://medium.com. The Location object has the following properties, they are the following:

  • Location.href
  • Location.protocol
  • Location.host
  • Location.hostname
  • Location.port
  • Location.pathname
  • Location.search
  • Location.hash
  • Location.username
  • Location.password
  • Location.origin

Location.href

The location.href property is a string that has the whole URL. We can both use it to get the URL of the current page and set the URL so that we can go to the next page. For example, if we have:

console.log(location.href);

Then we get the full URL, which is something like:

[https://fiddle.jshell.net/_display/](https://fiddle.jshell.net/_display/)

We can also use it to go to a different page. For example, we can write:

document.location.href = '[https://medium.com'](https://medium.com%27);

to go to the Medium website. It does the same thing as:

document.location = '[https://medium.com'](https://medium.com%27);

If the current document isn’t in a browsing context, then the value of this property is null .

Location.protocol

We can use the protocol property to get the protocol portion of the URL, which is the very first part of the URL before the first colon (:). For example, we can use it like in the following code:

console.log(document.location.protocol);

Then we get https: for an HTTPS web page and http: for HTTP pages. We can also set the protocol like in the following code:

document.location.protocol = 'http';

If the code above is run, the browser will attempt to go to the HTTP version of the current page.

Location.host

The host property has the string of the host name. If there’s a port with the host name, that will also be included. For example, we can retrieve host name like in the following:

console.log(document.location.host);

Then we get something like fiddle.jshell.net from the console.log statement. We can also set the host property. If we write something like the following code:

document.location.host = 'medium.com';

Then the browser will switch the host name for the current page with the new one and attempt to load the URL with the new host name.

Location.hostname

The hostname property is a string property that contains the domain of the URL. For example, we can get domain by running:

console.log(document.location.hostname);

Then we get something like fiddle.jshell.net from the console.log statement. We can also set the host property. If we write something like the following code:

document.location.hostname = 'medium.com';

Then the browser will switch the domain for the current page with the new one and attempt to load the URL with the new host name.

Location.port

The port property lets us get and set the port of the URL. It is a string property. If the URL doesn’t have a port number then it’ll be set to an empty string. For example, if we have:

console.log(document.location.port);

Then we get something like “3000” if the port is 3000. We can also set the host property. If we write something like the following code:

document.location.port = '3001';

Then the browser will switch the port for the current page with the new one and attempt to load the URL with the new port number.

Location.pathname

The pathname property is a string property that has the slash followed by the path of the URL, which is everything after the domain. The value will be an empty string if there’s no path. For example, if we have:

console.log(document.location.`pathname`);

Then we get something like “/_display/” . We can also set the pathname property. If we write something like the following code:

document.location.pathname = '3001';

Then the browser will switch the path for the current page with the new one and attempt to load the URL with the new path.

Location.search

The search property is a string property that gets us the query string. The query string is the part of the URL after the ? . For example, we can get the query string part of the URL of the currently loaded page by running:

console.log(document.location.search);

Then we get something like:

"?key=value"

if we have a URL like http://localhost:3000/?key=value. To parse and manipulate the query string we can convert it to a URLSearchParams object. If we want to go to a URL with a different query string, we can assign a new query string to the document.location.search property like we do in the following code:

document.location.search = '?newKey=newValue';

Then the new URL for our browser tab will be http://localhost:3000/?newKey=newValue.

Location.hash

The hash property let us get and set the part of the URL after the pound sign (#). The hash isn’t percent decoded. If there’s no hash fragment, then the value will be an empty string. For example, we can get the query string part of the URL of the currently loaded page by running:

console.log(document.location.hash);

Then we get something like:

"#hash"

if we have a URL like http://localhost:3000/?key=value. If we want to go to a URL with a different query string, we can assign a new query string to the document.location.hash property like we do in the following code:

document.location.hash= '#newHash';

Then the new URL for our browser tab will be http://localhost:3000/?newKey=newValue.

Location.username

The username property gets us the username portion of the URL returned as a string, which is the part between the protocol and the colon. For example, if we went to http://username:password@localhost:3000/, then document.location.username will get us 'username' . If we assign to it like wit the following code:

document.location.username = 'newUsername'

Then the new page will be http://newUsername:password@localhost:3000/.

Location.password

The password property gets us the username portion of the URL returned as a string, which is the part between the protocol and the colon. For example, if we went to http://username:password@localhost:3000/, then document.location.passwordwill get us 'password' . If we assign to it like wit the following code:

document.location.password= 'newPassword'

Then the new page will be http://username:newPassword@localhost:3000/.

Location.origin

The origin property is a string read only property that has the origin of the represented URL. For URLs that are with http or https , then it’ll include the protocol followed by :// , followed by the domain, followed by a colon, then followed by the port if it’s explicitly specified. For URL that has the file: scheme, then the value is browser dependent. For blob URLs, then the origin of the URL will be the part following blob: . For example, we can log the origin property like in the following code:

console.log(document.location.origin);

to get something like:

[https://fiddle.jshell.net](https://fiddle.jshell.net)

The window.document.location object is an object that has the URL of the current page. The location object let us various parts of the URL of the current page, and also set them so that the browser will switch out the part that’s designated by the property name and then go to the page with the new URL. There are also methods to do various things to the currently loaded page, so stayed tuned for the next part of this series.

Categories
JavaScript

Introducing the JavaScript Window Object — Pixels and Document

The window object is a global object that has the properties pertaining to the current DOM document, which is the things that are in the tab of a browser. The document property of the window object has the DOM document and associated nodes and methods that we can use to manipulate the DOM nodes and listen to events for each node. Since the window object is global, it’s available in every part of the application. When a variable is declared without the var , let or const keywords, they’re automatically attached to the window object, making them available to every part of your web app. This is only applicable when strict mode is disabled. If it’s enabled, then declaring variables without var , let , or const will be stopped an error since it’s not a good idea to let us declare global variables accidentally. The window object has many properties. They include constructors, value properties and methods. There’re methods to manipulate the current browser tab like opening and closing new popup windows, etc.

In a tabbed browser, each tab has its own window object, so the window object always represent the state of the currently opened tab in which the code is running. However, some properties still apply to all tabs of the browser like the resizeTo method and the innerHeight and innerWidth properties.

Note that we don’t need to reference the window object directly for invoking methods and object properties. For example, if we want to use the window.Image constructor, we can just write new Image() . In this article, we continue to look at what’s in the window object. In the previous sections, we looked at the constructors and some of the properties of the window object, including using the customElemnts to build a Web Component, and the crypto object to do cryptography on the client using symmetric and asymmetric encryption algorithms. In this part, we will continue to look at more properties of the window object like the devicePixelRatio property and begin to look at the properties of the document property.

window.devicePixelRatio

We can use the devicePixelRatio propety to get the ratio of the resolution in physical pixels to the resolution of CSS pixels of the display device. That is, this ratio of the size of one CSS pixel to the size of the one physical pixel, or how many actual pixels should be used to draw one CSS pixel. This comes in handy because there’re various kinds of displays out there. High resolution displays like 4K or Retina displays need scaling to display things so that they won’t be too small. Therefore, more than one pixels are needed to display a CSS pixel on the screen to show a sharper image on these screens that aren’t too small. This is a read only property, and there’s no way to be notified when the value is changed, which may happen if the user drags the window to a different display with different pixel density than the original. Therefore, if you want to check for changes to this property, you have to check it manually once in a while with the setInterval function for example.

We can access it by writing the following code:

console.log(`window.devicePixelRatio)`

By running the code above, we should see the number of physical pixels per CSS pixel logged with the console.log statement.

window.document

The document property has all the properties and methods for the currently opened document. This means that it has all the DOM node objects and all the associated method parsed into the tree. It also has many properties for listening to events and get various pieces of information about the document opened in the current browser tab. The document object has one constructor, the Document constructor which takes no arguments and creates a new Document object.

window.document.body

The document object also has many properties. The document.body property let us get the body or the frameset node of the current document and everything inside it. For example, if we run console.log statement with the document.body passed in as an argument, we should get something like the following from the console.log output:

<body data-n-head="">
    <div id="__nuxt"><!----><div id="__layout"><div><nav class="navbar navbar-expand-lg navbar-light bg-light" data-v-a3aba82c=""><a href="/" class="navbar-brand nuxt-link-exact-active nuxt-link-active" data-v-a3aba82c="">John Au-Yeung's Portfolio</a> <button type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation" class="navbar-toggler" data-v-a3aba82c=""><span class="navbar-toggler-icon" data-v-a3aba82c=""></span></button> <div id="navbarSupportedContent" class="collapse navbar-collapse" data-v-a3aba82c=""><ul class="navbar-nav mr-auto" data-v-a3aba82c=""><li class="nav-item active" data-v-a3aba82c=""><a href="/" class="nav-link nuxt-link-exact-active nuxt-link-active" data-v-a3aba82c="">Home</a></li> <li class="nav-item" data-v-a3aba82c=""><a href="/portfolio" class="nav-link" data-v-a3aba82c="">Portfolio</a></li> <li class="nav-item" data-v-a3aba82c=""><a href="/resume" class="nav-link" data-v-a3aba82c="">Resume</a></li> <li class="nav-item" data-v-a3aba82c=""><a href="/whyhireme" class="nav-link" data-v-a3aba82c="">Why Hire Me</a></li></ul></div></nav> <div data-v-88e6fdf8=""><div id="home" data-v-88e6fdf8=""><div class="jumbotron jumbotron-fluid text-center" data-v-88e6fdf8=""></div> <div class="cont" data-v-88e6fdf8=""><div class="col-md-12" data-v-88e6fdf8=""><h1 data-v-88e6fdf8="">About John</h1> <p data-v-88e6fdf8="">Hire me to build a business web and mobile apps affordably.</p></div> <div class="col-md-12" data-v-88e6fdf8=""><p data-v-88e6fdf8="">My expertise include:</p> <ul data-v-88e6fdf8=""><li data-v-88e6fdf8="">Web app development</li> <li data-v-88e6fdf8="">Mobile app development</li></ul></div> <div class="col-md-12" data-v-88e6fdf8=""><h1 data-v-88e6fdf8="">Social Media</h1> <ul data-v-88e6fdf8=""><li data-v-88e6fdf8=""><a href="https://medium.com/@hohanga" target="_blank" data-v-88e6fdf8="">
  </footer></div></div></div></div><script>window.__NUXT__={layout:"default",data:[{}],error:null,state:{},serverRendered:!0}</script><script src="/_nuxt/aa95a776e06b06b1de50.js" defer=""></script><script src="/_nuxt/278a2a8cea1aa4220b7f.js" defer=""></script><script src="/_nuxt/49479c32fdfc8cc26830.js" defer=""></script><script src="/_nuxt/a006571538a8da07f921.js" defer=""></script><script src="/_nuxt/92eb89f167a526d23bc9.js" defer=""></script>

</body>

The output has body node and all the DOM nodes inside it. We can also set the body property. However, if we do that, we will overwrite everything inside the body with what you set it with. To do this we can write something like the following code:

const body = document.createElement('body');
const hello = document.createElement('p');
hello.innerHTML = 'hello';
body.append(hello);
document.body = body;

In the code above, we first create the body element. This is a required step since we can only set document.body with the body or frameset element. Otherwise, we will get an error stating that you didn’t set document.body with of those elements. With the body element created, then we can create anything we want inside and append it to the body. In the example above, we created a p element and then set the innerHTML property of it to 'hello' . After that we appended the created p element to the body and then assigned it to the document.body property. After that, we should see the word ‘hello’ on the screen.

window.document.characterSet

The characterSet property is a read only property that returns the character encoding of the document that it’s currently rendered with. Character encoding is the set of characters and how the bytes that made up the web page are interpreted into those characters. The character encoding can be overridden inside the Content-Type header or put it inline with the meta tag. For example, with <meta charset=”utf-8"> to set the web page to be rendered with the utf-8 encoding. It can also be overridden with the browser option like setting the character encoding by right clicking the Internet Explorer browser window for example, click on Encoding, then picking the character encoding that you want to render the page with manually.

For example, we can look at the character encoding that the page you’re looking at is encoded with by writing:

const charSet = document.characterSet;
console.log(charSet);

We should get something like ‘UTF-8’ returned from the console.log output.

In this article, we looked at the document property and the devicePixelRatio property. The devicePixelRatio property lets see the number of physical pixels to render a single CSS pixel, and the document property is an object that has many properties that gets us information about the currently opened document and allows us to attach event handlers to it and manipulate it. We set the body element to the content of our choice with the document.body property as well as using the same property to view its properties. Also, we got the character sets of the webpage that we’re viewing with the document.characterSet property. There are many more properties in the document object, so stay tuned for the next part of this series to look at more properties of the window and the window.document objects.

Categories
JavaScript

Introducing the JavaScript Window Object — Image and Workers

The window object is a global object that has the properties pertaining to the current DOM document, which is the things that are in the tab of a browser. The document property of the window object has the DOM document and associated nodes and methods that we can use to manipulate the DOM nodes and listen to events for each node. Since the window object is global, it’s available in every part of the application. When a variable is declared without the var , let or const keywords, they’re automatically attached to the window object, making them available to every part of your web app. This is only applicable when strict mode is disabled. If it’s enabled, then declaring variables without var , let , or const will be stopped an error since it’s not a good idea to let us declare global variables accidentally. The window object has many properties. They include constructors, value properties and methods. There’re methods to manipulate the current browser tab like opening and closing new popup windows, etc.

In a tabbed browser, each tab has its own window object, so the window object always represents the state of the currently opened tab in which the code is running. However, some properties still apply to all tabs of the browser like the resizeTo method and the innerHeight and innerWidth properties.

Note that we don’t need to reference the window object directly for invoking methods and object properties. For example, if we want to use the window.Image constructor, we can just write new Image() .

Constructors

The window object has a few constructor properties. They include objects to parse XML and HTML strings into DOM elements, creating new HTML DOM elements, and object to create new Web workers.

DOMParser

The DOMParser constructor let us create a DOM parser object to parse XML or HTML strings into a DOM tree object. For the case of HTML, we can also set the innerHTML or outerHTML elements of the DOM node object returned from the DOM parser to add to the DOM tree. These properties can also be used to fetch HTML fragments corresponding to the DOM subtree. The constructor takes no arguments. The created object has the parseFroMString method to parse HTML or XML strings into the DOM tree object according to the given string. It takes 2 arguments. The first argument is the string containing the XML or HTML code that we want to parse. The string must be HTML, XML, XHTML+XML or an SVG document. The second argument is a string that has MIME the type of code string that we passed in as the first argument. The MIME type can be one of the following:

  • text/html
  • text/xml
  • application/xml
  • application/xhtml+xml
  • image/svg+xml

We can use it as in the following code:

const xmlString = `
<note>
 <to>John</to>
 <from>Jane</from>
 <heading>Greeting</heading>
 <body>How are you?</body>
</note>
`;
const parser = new DOMParser();
const xmlDoc = parser.parseFromString(xmlString, "application/xml");
console.log(xmlDoc);

When we run the code above, we get the DOM object with the DOM nodes. We can also use the parseFromString method with HTML like in the following code:

const htmlString = `
<div>
 <p>From: John</p>
 <p>To: Jane</p>
 <p>Greeting</p>
 <p>How are you?</p>
</div>
`;
const parser = new DOMParser();
const htmlDoc = parser.parseFromString(htmlString, "text/html");
console.log(htmlDoc);
for (const p of htmlDoc.body.children[0].children) {
  console.log(p.innerHTML);
}

When we run the code above, we get the DOM object with the DOM nodes with the first console.log statement, which gets us:

<html>
  <head></head>
  <body>
    <div>
      <p>From: John</p>
      <p>To: Jane</p>
      <p>Greeting</p>
      <p>How are you?</p>
    </div>
  </body>
</html>

When we loop through the loop with the bottom of the example code above, we get:

From: John
To: Jane
Greeting
How are you?

This is because htmlDoc.body.children[0].children gets us the DOM tree object which has a body property to get us the body element and everything inside it. The element has a children object, which has the div element as the first element of the DOM tree array-like object, and then we get the children property of that object, which has the p elements. With that, we can iterate through them and get the elements. In the example above, we accessed the content inside the p tags using the innerHTML property.

Likewise, we can use the parseFromString with SVG element since SVG graphics are vector graphics that are generated from an XML file. Therefore, we can use the same method to parse SVG strings.

Image

The Image constructor creates a new HTML image element. It’s exactly the same as using document.createElement('img') , which also creates an HTML element. The Image constructor takes 2 arguments. The first argument is a number that is the width of the img element in pixels, and the second argument takes a number which is the height of the image element in pixels. For example, we can use it like the following:

const image = new Image(100, 200);
image.src = '[https://images.unsplash.com/photo-1572315831029-5d6f20e0035d?ixlib=rb-1.2.1&auto=format&fit=crop&w=750&q=80'](https://images.unsplash.com/photo-1572315831029-5d6f20e0035d?ixlib=rb-1.2.1&auto=format&fit=crop&w=750&q=80%27);
document.body.appendChild(image);

The code above will create an img element that is 100 pixels wide and 200 pixels high. Then the created image element will be attached to the body element of the HTML document. If we run the code above, we should see an image with the dimensions given by the arguments of the constructor. It’s equivalent to:

<img width="100" height="200" src="[https://images.unsplash.com/photo-1572315831029-5d6f20e0035d?ixlib=rb-1.2.1&amp;auto=format&amp;fit=crop&amp;w=750&amp;q=80](https://images.unsplash.com/photo-1572315831029-5d6f20e0035d?ixlib=rb-1.2.1&amp;auto=format&amp;fit=crop&amp;w=750&amp;q=80)">

which is also what’s outputted by the Image constructor.

Option

The Option constructor creates an HTMLOptionElement which is an HTML element with the option tag. The constructor takes 4 arguments. The first argument is the text of the option. It’s an optional string argument. If it’s not specified, then an empty string is used as the default value. The second argument is the value of the option element, which corresponds to the value of the value attribute of the HTML option element. If it’s not specified then the value of the text will be used as the value of the value attribute. The third argument is the boolean which sets the selected attribute value when this element is first loaded. If it’s not specified, then false is the value that’s used. Setting the value of true doesn’t set the option to selected if it’s not already selected. The fourth argument is a boolean that sets the value of the selected attribute. The default it false which means it isn’t selected by default. If this argument is omitted, even if the third argument is true , this option isn’t selected. All the arguments of this constructor are optional.

For example, we can use it by adding the HTML code for the select element:

<select id='select'></select>

Then we can add the JavaScript code for getting the select element, then create the option elements with the Option constructor and then append them to the select element, like in the following code:

const select = document.getElementById('select');
const options = ['one', 'two', 'three'];

for (const o of options) {
  select.append(new Option(o, o, o === 'one', o === 'one'));
}

In the example above, we set the third and fourth arguments of the constructor to true if the option value and text is 'one' , so the first option will be the default selected option. If we run the code above, we should get a drop-down box with the first option selected by default.

We can also set different values of the option element to different attributes. For example, we can write:

const select = document.getElementById('select');
const options = ['one', 'two', 'three'];

for (const o of options) {
  if (o === 'one') {
    select.append(new Option(o, o, true, false));
  } else if (o === 'two') {
    select.append(new Option(o, o, false, true));
  } else {
    select.append(new Option(o, o, false, false));
  }
}

If we run the code above, we get that the second option is selected by default when the page loads instead of the first.

Worker

The Worker constructor let us create a Web Worker that lets us to background tasks in a web app. It can easily be created and it can be used to send a message back to its creator. Creator a Worker is as simple as calling the Worker constructor and specifying a script to run the worker thread. Workers can spawn new workers as long as the new workers as hosted with the same origin as the original worker. Workers may use XMLHttpRequest for HTTP requests as long as the responseXML and channel attributes on the XMLHttpRequest always returns null .

To create a worker, we use the Worker constructor, which takes 2 arguments. The first is a string with the URL with the script that the worker will run on. It must be in the same origin or domain as the script that spawns the worker. The second argument is an optional argument that is an object with the following properties. The type property is a string that specifies the type of worker to create. The values can either be classic or module . The default value is classic . The credentials property is a string that specifies the type of credentials to use the worker. The possible values are omit , same-origin or include . If it’s not specified or the type is classic , then the default value is omit , which means no credentials required. Lastly, the name property is a string property that lets us specify the identifying name of the DedicatedWorkerGlobalScope which represents the scope of the worker. This is mainly used for debugging purposes.

A Worker object throws exceptions. It can throw a SecurityError if the document isn’t allowed to start workers. This can happen if the URL is invalid or the same-origin policy isn’t followed. A NetworkError can be raised if the MIME type of the worker script isn’t correct. It should always be text/javasceipr . A SyntaxError is raised when the URL for the Worker can’t be parsed.

For example, we can construct a Worker object by sending a message from one script to a worker script. Then the worker script can send back messages to the main script. In the example below, we will make a simple calculator with the workers by writing some code. First, we create the HTML code for the input like the following:

<!DOCTYPE html>
<html>
  <head>
    <title>Add Worker</title>
  </head>
  <body>
    <form>
      <div>
        <label for="number1">First Number</label>
        <input type="text" id="number1" value="0" />
      </div>
      <div>
        <label for="number2">Second Number</label>
        <input type="text" id="number2" value="0" />
      </div>
    </form>
    <p id="result">Result</p>
    <script src="main.js"></script>
  </body>
</html>

Create a folder and save the code above to an HTML file. Then in the same folder, create a main.js file and put in the following code:

const worker = new Worker("worker.js");
const first = document.getElementById("number1");
const second = document.getElementById("number2");
const result = document.getElementById("result");
first.onkeyup = () => {
  worker.postMessage([first.value, second.value]);
};

second.onkeyup = () => {
  worker.postMessage([first.value, second.value]);
};

worker.onmessage = e => {
  result.textContent = e.data;
};

The code above will get the values of the inputs from the HTML file and then send messages whenever the first or second input has something entered. Listening to the keyup event to each input let us achieve that. Then in the same folder, create a worker.js file and then put in the following code:

onmessage = e => {
  console.log("Worker received message");
  const [first, second] = e.data;
  let sum = +first + +second;
  if (isNaN(sum)) {
    postMessage("Both inputs should be numbers");
  } else {
    let workerResult = `Result: ${sum} `;
    console.log("Send message back to main scriot");
    postMessage(workerResult);
  }
};

Note that the worker.js file has an onmessage handler. This function is required and it should have that name. The parameter e has the message sent from the main.js file. We can get the message like it was sent. So the e parameter should have the array that was sent from the main.js . Then we can compute the sum in this function and then send the computed result back to the main.js which spawned this worker with the postMessage function. In the main.js file, we have the worker.onmessage handler to listen to the messages sent back from this file. In this file, we sent back the result, so that’s what we get, and we used it to set the result on the element with the ID result .

In this article, we barely scratched the surface of the window object. We only went through the few constructors which may come in handy in various situations. We used the DOMParser to parse HTML and XML strings into a DOM tree object. Also, we used Image constructor to create an img element, and used Option constructor to create an option element. Finally, we used the Worker constructor to create a Web Worker which let us run code on the background and send messages between one script to the worker script and vice versa. The script that spawns the worker and the worker script must be hosted in the same domain so that external scripts can’t be spawning workers in our app and send messages with malicious data to our app. The window object has a lot more properties than a few constructors, which we will look at in later parts of this series.

Categories
Quasar

Developing Vue Apps with the Quasar Library — Split Screen Options

Quasar is a popular Vue UI library for developing good looking Vue apps.

In this article, we’ll take a look at how to create Vue apps with the Quasar UI library.

Horizontal Split Screen

We can make a split-screen that’s split horizontally by adding the horizontal prop.

For example, we can write:

<!DOCTYPE html>
<html>
  <head>
    <link
      href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material+Icons"
      rel="stylesheet"
      type="text/css"
    />
    <link
      href="https://cdn.jsdelivr.net/npm/quasar@1.12.13/dist/quasar.min.css"
      rel="stylesheet"
      type="text/css"
    />
  </head>
  <body class="body--dark">
    <script src="https://cdn.jsdelivr.net/npm/vue@^2.0.0/dist/vue.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/quasar@1.12.13/dist/quasar.umd.min.js"></script>
    <div id="q-app">
      <q-layout
        view="lHh Lpr lFf"
        container
        style="height: 100vh;"
        class="shadow-2 rounded-borders"
      >
        <div class="q-pa-md">
          <q-splitter horizontal v-model="splitterModel" style="height: 400px;">
            <template v-slot:before>
              <div class="q-pa-md">
                <div class="text-h4 q-mb-md">Before</div>
                <div v-for="n in 20" :key="n" class="q-my-md">
                  {{ n }}. Lorem ipsum
                </div>
              </div>
            </template>

            <template v-slot:after>
              <div class="q-pa-md">
                <div class="text-h4 q-mb-md">After</div>
                <div v-for="n in 20" :key="n" class="q-my-md">
                  {{ n }}. Quis praesentium
                </div>
              </div>
            </template>
          </q-splitter>
        </div>
      </q-layout>
    </div>
    <script>
      new Vue({
        el: "#q-app",
        data: {
          splitterModel: 50
        }
      });
    </script>
  </body>
</html>

v-model is bound to the splitterModel reactive property is the height of the top panel as a percentage.

Drag Limit

We can set the min and max value for the split-screen by setting the limit prop:

<!DOCTYPE html>
<html>
  <head>
    <link
      href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material+Icons"
      rel="stylesheet"
      type="text/css"
    />
    <link
      href="https://cdn.jsdelivr.net/npm/quasar@1.12.13/dist/quasar.min.css"
      rel="stylesheet"
      type="text/css"
    />
  </head>
  <body class="body--dark">
    <script src="https://cdn.jsdelivr.net/npm/vue@^2.0.0/dist/vue.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/quasar@1.12.13/dist/quasar.umd.min.js"></script>
    <div id="q-app">
      <q-layout
        view="lHh Lpr lFf"
        container
        style="height: 100vh;"
        class="shadow-2 rounded-borders"
      >
        <div class="q-pa-md">
          <q-splitter
            :limits="[50, 100]"
            v-model="splitterModel"
            style="height: 400px;"
          >
            <template v-slot:before>
              <div class="q-pa-md">
                <div class="text-h4 q-mb-md">Before</div>
                <div v-for="n in 20" :key="n" class="q-my-md">
                  {{ n }}. Lorem ipsum
                </div>
              </div>
            </template>

            <template v-slot:after>
              <div class="q-pa-md">
                <div class="text-h4 q-mb-md">After</div>
                <div v-for="n in 20" :key="n" class="q-my-md">
                  {{ n }}. Quis praesentium
                </div>
              </div>
            </template>
          </q-splitter>
        </div>
      </q-layout>
    </div>
    <script>
      new Vue({
        el: "#q-app",
        data: {
          splitterModel: 50
        }
      });
    </script>
  </body>
</html>

We set the limits prop to an array with the min and max percentage of the screen that the left panel takes up.

Split Screen Panel Units

We can change the unit of the size for the panels by setting the unit prop:

<!DOCTYPE html>
<html>
  <head>
    <link
      href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material+Icons"
      rel="stylesheet"
      type="text/css"
    />
    <link
      href="https://cdn.jsdelivr.net/npm/quasar@1.12.13/dist/quasar.min.css"
      rel="stylesheet"
      type="text/css"
    />
  </head>
  <body class="body--dark">
    <script src="https://cdn.jsdelivr.net/npm/vue@^2.0.0/dist/vue.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/quasar@1.12.13/dist/quasar.umd.min.js"></script>
    <div id="q-app">
      <q-layout
        view="lHh Lpr lFf"
        container
        style="height: 100vh;"
        class="shadow-2 rounded-borders"
      >
        <div class="q-pa-md">
          <q-splitter unit="px" v-model="splitterModel" style="height: 400px;">
            <template v-slot:before>
              <div class="q-pa-md">
                <div class="text-h4 q-mb-md">Before</div>
                <div v-for="n in 20" :key="n" class="q-my-md">
                  {{ n }}. Lorem ipsum
                </div>
              </div>
            </template>

            <template v-slot:after>
              <div class="q-pa-md">
                <div class="text-h4 q-mb-md">After</div>
                <div v-for="n in 20" :key="n" class="q-my-md">
                  {{ n }}. Quis praesentium
                </div>
              </div>
            </template>
          </q-splitter>
        </div>
      </q-layout>
    </div>
    <script>
      new Vue({
        el: "#q-app",
        data: {
          splitterModel: 150
        }
      });
    </script>
  </body>
</html>

The unit prop is set to 'px' to change the unit of splitterModel to pixels.

Conclusion

We can add split-screen panels with various options into our Vue app with Quasar.