Learn JavaScript: Novice to Ninja

Chapter 9: The Window Object

Every JavaScript environment has aglobal object. Any variables that are created in the global scope are actually properties of this object, and any functions are methods of it. In a browser environment the global object is the window object, which represents the browser window that contains a web page.

In this chapter, we’ll cover these topics:

  • The Browser Object Model
  • Finding out browser information
  • Browser history
  • Controlling windows
  • Cookies
  • Timing functions
  • Our project ― we’ll add a countdown timer to our quiz

The Browser Object Model

The Browser Object Model (or BOM for short) is a collection of properties and methods that contain information about the browser and computer screen. For example, we can find out which browser is being used to view a page (though, this method is unreliable). We can also find out the dimensions of the screen it is viewed on, and which pages have been visited before the current page. It can also be used for the rather dubious practice of creating pop-up windows, if you’re into annoying your users.

There is no official standard for the BOM, although there are a number of properties and methods that are supported by all the major browsers, making a sort of de facto standard. These properties and methods are made available through the window object. Every browser window, tab, popup, frame, and iframe has a window object.

The BOM Only Makes Sense in a Browser Environment

Remember that JavaScript can be run in different environments. The BOM only makes sense in a browser environment. This means that other environments (such as Node.js) probably won’t have a window object, although they will still have a global object; for example, Node.js has an object called global .

If you don’t know the name of the global object, you can also refer to it using the keyword this in the global scope. The following code provides a quick way of assigning the variable global to the global object:

// from within the global scopeconst global = this;

Going Global

All the way back in Chapter 2, we introduced the concept of global variables. These are variables that are created without using the constlet or var keywords. Global variables can be accessed in all parts of the program.

Global variables are actual properties of a global object. In a browser environment, the global object is the window object. This means that any global variable created is actually a property of the window object, as can be seen in the example below:

x = 6;  // global variable created<< 6window.x // same variable can be accessed as a property of the window object<< 6// both variables are exactly the samewindow.x === x;<< true

In general, you should refer to global variables without using the window object; it’s less typing and your code will be more portable between environments. An exception is if you need to check whether a global variable has been defined. For example, the following code will throw a ReferenceError if x has not been defined:

if (x) { // do something}

However, if the variable is accessed as a property of the window object, then the code will still work, as window.x will simply return false , meaning the block of code will not be evaluated:

if (window.x) {// do something}

Some functions we’ve already met, such as parseInt() and isNaN() , are actually methods of the global object, which in a browser environment makes them methods of the window object:

window.parseInt(4.2);<< 4window.isNaN(4.2);<< false

Like variables, it’s customary to omit accessing them through the window object.

Changes in ES6

ES6 made parseInt() and isNaN() methods of the Number object, so they can be both be called using the following code:

Number.parseInt(4.2);<< 4Number.isNaN(4.2);<< false

Dialogs

In Chapter 1, we introduced three functions that produced dialogs in the browsers: alert()confirm() and prompt() . These are not part of the ECMAScript standard, although all major browsers support them as methods of the window object.

The window.alert() method will pause the execution of the program and display a message in a dialog box. The message is provided as an argument to the method, and undefined is always returned:

window.alert('Hello');<< undefined
Alert dialog

The window.confirm() method will stop the execution of the program and display a confirmation dialog that shows the message provided as an argument, and giving the options of OK or Cancel. It returns the boolean values of true if the user clicks OK, and false if the user clicks Cancel:

window.confirm('Do you wish to continue?');<< undefined
Confirm dialog

The window.prompt() method will stop the execution of the program. It displays a dialog that shows a message provided as an argument, as well as an input field that allows the user to enter text. This text is then returned as a string when the user clicks OK. If the user clicks Cancel, null is returned:

window.prompt('Please enter your name:');
Prompt dialog

Use With Care

It’s worth reiterating again that these methods will stop the execution of a program in its tracks. This means that everything will stop processing at the point the method is called, until the user clicksOKorCancel. This can cause problems if the program needs to process something else at the same time or the program is waiting for a callback function.

There are some occasions when this functionality can be used as an advantage, for example, a window.confirm() dialog can be used as a final check to see if a user wants to delete a resource. This will stop the program from going ahead and deleting the resource while the user decides what to do.

It’s also worth keeping in mind that most browsers allow users to disable any dialogs from repeatedly appearing, meaning they are not a feature to be relied upon.

Browser Information

The window object has a number of properties and methods that provide information about the user’s browser.

Which Browser?

The window object has a navigator property that returns a reference to the Navigator object. The Navigator object contains information about the browser being used. Its userAgent property will return information about the browser and operating system being used. For example, if I run the following line of code, it shows that I am using Safari version 10 on Mac OS:

window.navigator.userAgent<< "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/602.4.8 (KHTML, like Gecko) Version/10.0.3 Safari/602.4.8"

Don’t rely on this information though, as it can be modified by a user to masquerade as a different browser. It can also be difficult to make any sense of the string returned, because all browsers pretend to be others to some degree. For example, every browser will include the string “Mozilla” in its userAgent property, for reasons of legacy Netscape compatibility. The userAgent property has been deprecated from the official specification, but it remains well supported in all major browsers.

Location, Location, Location

The window.location property is an object that contains information about the URL of the current page. It contains a number of properties that provide information about different fragments of the URL.

The href property returns the full URL as a string:

window.location.href << "https://www.sitepoint.com/premium/books/javascript-novice-to-ninja"

This property (as well as most of the others in this section) is a read/write property, which means it can also be changed by assignment. If this is done, the page will be reloaded using the new property. For example, entering the following line into the browser console will redirect the page to the SitePoint JavaScript channel:

window.location.href = 'https://www.sitepoint.com/javascript/'<< "https://www.sitepoint.com/javascript/"

The protocol property returns a string describing the protocol used (such as httphttpspop2ftp etc.). Note that there is a colon ( : ) at the end:

window.location.protocol << "https:"

The host property returns a string describing the domain of the current URLandthe port number (this is often omitted if the default port 80 is used):

window.location.host<< "www.sitepoint.com"

The hostname property returns a string describing the domain of the current URL:

window.location.hostname<< "www.sitepoint.com"

The port property returns a string describing the port number, although it will return an empty string if the port is not explicitly stated in the URL:

window.location.port<< ""

The pathname property returns a string of the path that follows the domain:

window.location.pathname<< "/premium/books/javascript-novice-to-ninja"

The search property returns a string that starts with a “?” followed by the query string parameters. It returns an empty string if there are no query string parameters. This is what I get when I search for “JavaScript” on SitePoint:

window.location.search<< "?q=javascript&limit=24&offset=0&page=1&content_types[]=All&slugs[]=all&states[]=available&order="

The hash property returns a string that starts with a “#” followed by the fragment identifier. It returns an empty string if there is no fragment identifier:

window.location.hash<< ""

The origin property returns a string that shows the protocol and domain where the current page originated from. This property is read-only, so cannot be changed:

window.location.origin<< "https://www.sitepoint.com"

The window.location object also has the following methods:

  • The reload() method can be used to force a reload of the current page. If it’s given a parameter of true , it will force the browser to reload the page from the server, instead of using a cached page.
  • The assign() method can be used to load another resource from a URL provided as a parameter, for example:
window.location.assign('https://www.sitepoint.com/')
  • The replace() method is almost the same as the assign() method, except the current page will not be stored in the session history, so the user will be unable to navigate back to it using the back button.
  • The toString() method returns a string containing the whole URL:window.location.toString();<< "https://www.sitepoint.com/javascript/"

The Browser History

The window.history property can be used to access information about any previously visited pages in the current browser session. Avoid confusing this with the new HTML5 History API. (See http://www.sitepoint.com/javascript-history-pushstate/ post for details.)

The window.history.length property shows how many pages have been visited before arriving at the current page.

The window.history.go() method can be used to go to a specific page, where 0 is the current page:

window.history.go(1); // goes forward 1 pagewindow.history.go(0); // reloads the current pagewindow.history.go(-1); // goes back 1 page

There are also the window.history.forward() and window.history.back() methods that can be used to navigate forwards and backwards by one page respectively, just like using the browser’s forward and back buttons.

Controlling Windows

A new window can be opened using the window.open() method. This takes the URL of the page to be opened as its first parameter, the window title as its second parameter, and a list of attributes as the third parameter. This can also be assigned to a variable, so the window can then be referenced later in the code:

const popup = window.open('https://sitepoint.com','SitePoint','width=400,height=400,resizable=yes');
A popup window

The close() method can be used to close a window, assuming you have a reference to it:

popup.close();

It is also possible to move a window using the window.moveTo() method. This takes two parameters that are the X and Y coordinates of the screen that the window is to be moved to:

window.moveTo(0,0); // will move the window to the top-left corner of the screen

You can resize a window using the window.resizeTo() method. This takes two parameters that specify the width and height of the resized window’s dimensions:

window.resizeTo(600,400);

Annoying Popups

These methods were largely responsible for giving JavaScript a bad name, as they were used for creating annoying pop-up windows that usually contained intrusive advertisements. It’s also a bad idea from a usability standpoint to resize or move a user’s window.

Many browsers block pop-up windows and disallow some of these methods to be called in certain cases. For example, you can’t resize a window if more than one tab is open. You also can’t move or resize a window that wasn’t created using window.open() .

It’s rare that it would be sensible to use any of these methods, so think very carefully before using them. There will almost always be a better alternative, and a ninja programmer will endeavor to find it.

Screen Information

The window.screen object contains information about the screen the browser is displayed on. You can find out the height and width of the screen in pixels using the height and width properties respectively:

window.screen.height<< 1024window.screen.width<< 1280

The availHeight and availWidth can be used to find the height and width of the screen, excluding any operating system menus:

window.screen.availWidth<< 1280window.screen.availHeight<< 995

The colorDepth property can be used to find the color bit depth of the user’s monitor, although there are few use cases for doing this other than collecting user statistics:

window.screen.colorDepth;<< 24

More Useful on Mobile

The Screen object has more uses for mobile devices. It also allows you to do things like turn off the device’s screen, detect a change in its orientation or lock it in a specific orientation.

Use With Care

Many of the methods and properties covered in the previous section were abused in the past for dubious activities such as user-agent sniffing, or detecting screen dimensions to decide whether or not to display certain elements. These practices have (thankfully) now been superseded by better practices, such as media queries and feature detection, which is covered in the next chapter.

The Document Object

Each window object contains a document object. This object has properties and methods that deal with the page that has been loaded into the window. In Chapter 6, we covered the Document Object Model and the properties and methods used to manipulate items on the page. The document object contains a few other methods that are worth looking at.

 document.write()

The write() method simply writes a string of text to the page. If a page has already loaded, it will completely replace the current document:

document.write('Hello, world!');

This would replace the whole document with the string Hello, world! . It is possible to include HTML in the string and this will become part of the DOM tree. For example, the following piece of code will create an <h1> tag node and a child text node:

document.write('<h1>Hello, world!</h1>');

The document.write() method can also be used within a document inside <script> tags to inject a string into the markup. This will not overwrite the rest of the HTML on the page. The following example will place the text "Hello, world!" inside the <h1> tags and the rest of the page will display as normal:

<h1>    <script>document.write("Hello, world!")</script></h1>

The use of document.write() is heavily frowned upon as it can only be realistically used by mixing JavaScript within an HTML document. There are still some extremely rare legitimate uses of it, but a ninja programmer will hardly ever need to use it.

Cookies

Cookies are small files that are saved locally on a user’s computer. They were invented by Netscape as a way of getting round HTTP being a stateless protocol. This means that a browser does not remember anything from one request to another. So every time a user visits a page, nothing about any previous visits is remembered. Cookies can be used to sidestep this problem by storing information that can then be retrieved between requests.

A restriction of cookies is that they can only be read by a web page from the same domain that set them. This is to stop sites being able to access information about users, such as other sites they have visited. Cookies are also limited to storing up to 4KB of data, although 20 cookies are allowed per domain, which can add up to quite a lot of data.

Cookies can be used for personalizing a user’s browsing experience, storing user preferences, keeping track of user choices (such as a shopping cart), authentication and tracking users. The use of cookies for tracking purposes has been much maligned in recent years. Their use for data storage is starting to be replaced in many cases by the new HTML5 localStorage API as it allows more data to be stored. This is covered in Chapter 14. Cookies are still useful for retaining state information (such as if a user is logged in) because they’re passed between the client and server on every HTTP request.

Cookies take the form of a text file that contain a list of name/value pairs separated by semicolons. For example, a cookie file might contain the following information:

"name=Superman; hero=true; city=Metropolis"

EU Cookie Directive

The EU Cookie Directive is a piece of legislation that requires websites based in an EU country to ask for permission before setting any cookies. It’s possible, however, that in the future this requirement may be part of a global browser setting, rather than the onus being on each individual website to ask for permission to set cookies.

Creating Cookies

To create a cookie, you assign it to JavaScript’s “cookie jar”, using the document.cookie property, like so:

document.cookie = 'name=Superman';<< "name=Superman"

The document.cookie property acts like a special type of string. Assigning another cookie to it won’t overwrite the entire property, it will just append it to the end of the string. So we can add more cookies by assigning them to document.cookie :

document.cookie = 'hero=true';<< "hero=true"document.cookie = 'city=Metropolis';<< "city=Metropolis"

Changing Cookie Values

A cookie’s value can be changed by reassigning it to document.cookie using the same name but a different value. The following code will update the value of two of the cookies that we set in the previous section:

document.cookie = 'name=Batman'<< "name=Batman"document.cookie = 'city=Gotham'<< "city=Gotham"

Reading Cookies

To see the current contents of the cookie jar, simply enter document.cookie :

document.cookie:<< "name=Batman; hero=true; city=Gotham"

We can use the split method to break the string into an array containing each name/value pair, then use a for of loop to iterate through the array:

const cookies = document.cookie.split("; ");for (crumb of cookies){    const [key,value] = crumb.split("=");    console.log(`The value of ${key} is ${value}`);}<< The value of name is BatmanThe value of hero is trueThe value of city is Gotham

To see an example of cookies used in the wild, you can visit almost any website, open the browser console, and type document.cookie .

Cookie Expiry Dates

Cookies are session cookies by default. This means they are deleted once a browser session is finished (when the user closes the browser tab or window). Cookies can be made persistent ― that is, lasting beyond the browser session ― by adding "; expires=date" to the end of the cookie when it’s set, where date is a date value in the UTC String format Day, DD-Mon-YYYY HH:MM:SS GMT . The following example sets a cookie to expire in one day’s time:

const expiryDate = new Date(); const tomorrow = expiryDate.getTime() + 1000 * 60 * 60 * 24;expiryDate.setTime(tomorrow);document.cookie = `name=Batman; expires=${ expiryDate.toUTCString()}`;

An alternative is to set the max-age value. This takes a value in seconds, but it wasn’t supported in Internet Explorer before version 10:

document.cookie = 'name=Batman; max-age=86400' // 86400 secs = 1 day

Don’t Rely On Cookie Expiry

Applications that contain sensitive information shouldn’t rely on cookies expiring using these methods. Browsers can sometimes hold on to information stored in a cookie that should have expired when the “session restore” feature is used after a crash.

The Path and Domain of Cookies

By default, cookies can only be read by pages inside the same directory and domain as the file was set. This is for security reasons so that access to the cookie is limited.

The path can be changed so that any page in the root directory can read the cookie. It’s done by adding the string ; path=/ to the end of the cookie when it is set:

document.cookie = 'name=Batman; path=/'

It’s also possible to set the domain by adding "; domain=domainName" to the end of the cookie:

document.cookie = 'name=Batman; domain=sitepoint.com';

A cookie can only be read by the domain that created it anyway, but doing this will allow all subdomains of sitepoint.com (such as javascript.sitepoint.com and books.sitepoint.com) to read it.

Secure Cookies

Adding the string ; secure to the end of a cookie will ensure it’s only transmitted over a secure HTTPS network:

document.cookie = 'name=Batman; secure';

Deleting Cookies

To remove a cookie, you need to set it to expire at a time in the past:

document.cookie = 'name=Batman; expires=Thu, 01 Jan 1970 00:00:01 GMT';

If a cookie is a session cookie, it will expire when the tab or window is closed.

Cumbersome Cookies

JavaScript’s cookie handling is quite basic and can also be quite cumbersome. Many developers use a library such asCookies.jsorjsCookie. You could even have a go at developing your own set of functions to make dealing with cookies easier.

Timing Functions

 setTimeout()

The window object provides some useful methods for scheduling the execution of a function, and for repeatedly executing functions at regular intervals.

The window.setTimeout() method accepts a callback to a function as its first parameter and a number of milliseconds as its second parameter. Try entering the following example into a console. It should show an alert dialog after three seconds (that’s 3000 milliseconds):

window.setTimeout( () => alert("Time's Up!"), 3000);<< 4

Notice that the method returns an integer. This is an ID used to reference that particular timeout. It can also cancel the timeout using the window.clearTimeout() method. Try calling the code again and make a note of the number that is returned:

window.setTimeout( () => alert("Time's Up!"), 3000);<< 5

Now quickly enter the following code before the alert pops up, making sure that you enter the number that was returned previously (it might not be 5 in your case!):

window.clearTimeout(5);<< undefined

If you were quick enough, and used the correct ID, the alert was prevented from happening.

 setInterval()

The window.setInterval() method works in a similar way to window.setTimeout() , except that it will repeatedly invoke the callback function after every given number of milliseconds.

The previous example used an anonymous function, but it is also possible to use a named function like so:

function chant(){ console.log('Beetlejuice'); }

Now we can set up the interval and assign it to a variable:

const summon = window.setInterval(chant,1000);<< 6

This should show the message “Beetlejuice” in the console every second (1,000 milliseconds).

To stop this, we can use the window.clearInterval() method and the variable repeat as an argument (this is because the window.setInterval() method returns its ID, so this will be assigned to the variable repeat ):

window.clearInterval(summon);

Using the this Keyword

Be careful when using a method that uses the this keyword with either of these timing methods. The binding of this is set to the window object, rather than the method’s object, so it can get some unexpected results:

const person = {    name: 'Superman',    introduce() {         console.log(`Hi, I'm ${this.name}`);    }};                setTimeout(person.introduce, 50);<< Hi, I'm

In the example above, the value of this.name is undefined because the code is looking for a property of the window object called name , which doesn’t exist.

There are ways to bind this to the object instead, and these are discussed in Chapter 12.

Animation

The setTimeOut() and setInterval() methods can be used to animate elements on a web page. As an example, let’s create a web page that shows a colored square, and make it rotate. Create a folder called animation that contains files called index.htmlstyles.css and main.js . Place the following code inside index.html :

<!doctype html><html lang='en'><head><meta charset='utf-8'><title>Animation Example</title><link rel='stylesheet' href='styles.css'></head><body><div id='square'></div><script src='main.js'></script></body></html>

This places a div on the page with an ID of square .

Next, add the following styles.css :

#square {    margin: 100px;    width: 100px;    height: 100px;    background: #d16;}

This will set the position, dimensions and color of the div. Now for the animation ― add the following code to main.js :

const squareElement = document.getElementById('square');let angle = 0;setInterval( () => {    angle = (angle + 2) % 360;    squareElement.style.transform = `rotate(${angle}deg)`}, 1000/60);

This code receives a reference to our square div, then sets a variable called angle to 0. We then use the setInterval() method to increase the value of angle by 2 (we also use the % operator so that it resets to 0 at 360), then set the transform CSS3 property to rotate that number of degrees. The second argument is 1000/60 , which equates to a frame speed of 60 frames per second.

Open animation.html in your browser and you should see a rotating square, although it will probably be quite slow and not very smooth. This was the only way to achieve animation using JavaScript until the window.requestAnimationFrame() method was developed.

 requestAnimationFrame

This method of the window object works in much the same way as the window.setInterval() method, although it has a number of improvements to optimize its performance. These include making the most of the browser’s built-in graphics-handling capabilities, and not running the animation when the tab is inactive, resulting in a much smoother performance. It’s supported in all major browsers, including Internet Explorer from version 10 onwards. Change the code in main.js to the following:

const squareElement = document.getElementById('square');let angle = 0;function rotate() {    angle = (angle + 2)%360;    squareElement.style.transform = `rotate(${angle}deg)`    window.requestAnimationFrame(rotate);}const id = requestAnimationFrame(rotate);

This is similar to the earlier code, but this time we place the rotation code inside a function called rotate . The last line of this function uses the window.requestAnimationFrame() method and takes the rotate() function as an argument. This will then call the rotate() function recursively. The frame rate cannot be set using requestAnimationFrame() ; it’s usually 60 frames per second, although it’s optimized for the device being used.

To start the animation, we need to call the requestAnimationFrame() method, giving the rotate() function as an argument. This will return a unique ID that can be employed to stop the animation using the window.cancelAnimationFrame() method:

cancelAnimationFrame(id);

Refresh the animation.html page and you should notice that the animation is much faster and smoother than before, as shown below.

Animation in the browser

Consider Using CSS Instead

The rotation animation example demonstrates how JavaScript can be used to perform animations in the browser. It could also be achieved using pure CSS animation with the following style rules in styles.css :

#square {    margin: 100px;    width: 100px;    height: 100px;    background: #cc0;    animation: spin 4s linear infinite;}@keyframes spin { from { transform:rotate(0deg); } to { transform:rotate(3600deg); } }

In general, it is typically better to use CSS for any animation effects, although there may be times when JavaScript might be the better solution.

Quiz Ninja Project

We’re now going add a timer to give our quiz a “beat the clock’ element. We’ll do this using the window object’s setInterval() method to add a time limit. First of all, we’ll add an element to the HTML for the timer. Update the <header> inside the index.html file to include an extra <div> element with an id of ‘timer’:

<header>    <h1>Quiz Ninja!</h1>    <div id='timer'>Time: <strong>20</strong></div>    <div id='score'>Score: <strong>0</strong></div></header>

We’ll use this ID to add a reference to this element as a property of the view object in the main.js file:

timer: document.querySelector('#timer strong')

Next, we need to add the following code to the game.start() method:

this.secondsRemaining = 20;this.timer = setInterval( this.countdown , 1000 );

This initializes a property of the game object called secondsRemaining to 20 . It is used to measure, in seconds, how the game will last. The next line sets up an interval that calls a method called countdown() every second (1,000 milliseconds). This method needs adding to the game object:

countdown() {    game.secondsRemaining--;    view.render(view.timer,game.secondsRemaining);    if(game.secondsRemaining < 0) {        game.gameOver();    }}

This function decreases the secondsRemaining variable that we initialized earlier by 1 using the -- operator, then calls the view.render() method so the number of seconds remaining is displayed in the header. Last of all, we check to see if the time has fallen below zero and, if it has, we call the gameOver() function as time has run out!

Finally, we have to add a line at the end of the game.gameOver() method that will remove the interval when the game has finished, otherwise it will continue to keep counting down past zero! To stop this from happening, we place the following line of code anywhere inside the gameOver() function:

gameOver(){    view.render(view.info,`Game Over, you scored ${this.score} point${this.score !== 1 ? 's' : ''}`);    view.teardown();    clearInterval(this.timer);}

Try playing the game now by opening index.html in a browser and see how you go with the added pressure of beating the clock.

You can see a live example onCodePen.

Chapter Summary

  • The window object is the global object in a browser.
  • Global variables are actually properties of the window object in a browser environment.
  •  alertconfirm() , and prompt() are all methods of the window object, and open dialogs that halt the execution of the program.
  • The window.navigator object gives information about the user’s browser and operating system, although it can be unreliable.
  • The window.location object provides information about the URL of the current page.
  • The window.history object keeps information about the pages that have been visited in the session.
  • You can open, close, resize, and move windows (although, this doesn’t mean you should!).
  • The window.screen object provides information about the user’s screen.
  •  document.write() is an archaic method of writing text to the document and should be avoided.
  • Cookies can be used to store small pieces of information between requests using the document.cookie property.
  • The window.setTimeout() method can be used to invoke a function after a set amount of time. It can be canceled using the clearTimeout() method.
  • The window.setInterval() method can be used to repeatedly invoke a function. It can be stopped using the clearInterval() method.
  • The window.requestAnimationFrame() method can be used to produce smooth and optimized animation by utilizing the browser’s built-in graphics capabilities. It can be canceled using the cancelAnimationFrame() method.

In the next chapter, we’ll be looking at how to handle errors and write tests in JavaScript.

Pages: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16