Categories
JavaScript

Introducing the JavaScript Window Object — Image and Workers

Spread the love

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.

By John Au-Yeung

Web developer specializing in React, Vue, and front end development.

Leave a Reply

Your email address will not be published. Required fields are marked *