You are previewing JavaScript: The Definitive Guide, 6th Edition.

JavaScript: The Definitive Guide, 6th Edition

Cover of JavaScript: The Definitive Guide, 6th Edition by David Flanagan Published by O'Reilly Media, Inc.
  1. JavaScript: The Definitive Guide
  2. Dedication
  3. A Note Regarding Supplemental Files
  4. Preface
    1. Conventions Used in This Book
    2. Example Code
    3. Errata and How to Contact Us
    4. Acknowledgments
  5. 1. Introduction to JavaScript
    1. Core JavaScript
    2. Client-Side JavaScript
      1. Example: A JavaScript Loan Calculator
  6. I. Core JavaScript
    1. 2. Lexical Structure
      1. Character Set
      2. Comments
      3. Literals
      4. Identifiers and Reserved Words
      5. Optional Semicolons
    2. 3. Types, Values, and Variables
      1. Numbers
      2. Text
      3. Boolean Values
      4. null and undefined
      5. The Global Object
      6. Wrapper Objects
      7. Immutable Primitive Values and Mutable Object References
      8. Type Conversions
      9. Variable Declaration
      10. Variable Scope
    3. 4. Expressions and Operators
      1. Primary Expressions
      2. Object and Array Initializers
      3. Function Definition Expressions
      4. Property Access Expressions
      5. Invocation Expressions
      6. Object Creation Expressions
      7. Operator Overview
      8. Arithmetic Expressions
      9. Relational Expressions
      10. Logical Expressions
      11. Assignment Expressions
      12. Evaluation Expressions
      13. Miscellaneous Operators
    4. 5. Statements
      1. Expression Statements
      2. Compound and Empty Statements
      3. Declaration Statements
      4. Conditionals
      5. Loops
      6. Jumps
      7. Miscellaneous Statements
      8. Summary of JavaScript Statements
    5. 6. Objects
      1. Creating Objects
      2. Querying and Setting Properties
      3. Deleting Properties
      4. Testing Properties
      5. Enumerating Properties
      6. Property Getters and Setters
      7. Property Attributes
      8. Object Attributes
      9. Serializing Objects
      10. Object Methods
    6. 7. Arrays
      1. Creating Arrays
      2. Reading and Writing Array Elements
      3. Sparse Arrays
      4. Array Length
      5. Adding and Deleting Array Elements
      6. Iterating Arrays
      7. Multidimensional Arrays
      8. Array Methods
      9. ECMAScript 5 Array Methods
      10. Array Type
      11. Array-Like Objects
      12. Strings As Arrays
    7. 8. Functions
      1. Defining Functions
      2. Invoking Functions
      3. Function Arguments and Parameters
      4. Functions As Values
      5. Functions As Namespaces
      6. Closures
      7. Function Properties, Methods, and Constructor
      8. Functional Programming
    8. 9. Classes and Modules
      1. Classes and Prototypes
      2. Classes and Constructors
      3. Java-Style Classes in JavaScript
      4. Augmenting Classes
      5. Classes and Types
      6. Object-Oriented Techniques in JavaScript
      7. Subclasses
      8. Classes in ECMAScript 5
      9. Modules
    9. 10. Pattern Matching with Regular Expressions
      1. Defining Regular Expressions
      2. String Methods for Pattern Matching
      3. The RegExp Object
    10. 11. JavaScript Subsets and Extensions
      1. JavaScript Subsets
      2. Constants and Scoped Variables
      3. Destructuring Assignment
      4. Iteration
      5. Shorthand Functions
      6. Multiple Catch Clauses
      7. E4X: ECMAScript for XML
    11. 12. Server-Side JavaScript
      1. Scripting Java with Rhino
      2. Asynchronous I/O with Node
  7. II. Client-Side JavaScript
    1. 13. JavaScript in Web Browsers
      1. Client-Side JavaScript
      2. Embedding JavaScript in HTML
      3. Execution of JavaScript Programs
      4. Compatibility and Interoperability
      5. Accessibility
      6. Security
      7. Client-Side Frameworks
    2. 14. The Window Object
      1. Timers
      2. Browser Location and Navigation
      3. Browsing History
      4. Browser and Screen Information
      5. Dialog Boxes
      6. Error Handling
      7. Document Elements As Window Properties
      8. Multiple Windows and Frames
    3. 15. Scripting Documents
      1. Overview of the DOM
      2. Selecting Document Elements
      3. Document Structure and Traversal
      4. Attributes
      5. Element Content
      6. Creating, Inserting, and Deleting Nodes
      7. Example: Generating a Table of Contents
      8. Document and Element Geometry and Scrolling
      9. HTML Forms
      10. Other Document Features
    4. 16. Scripting CSS
      1. Overview of CSS
      2. Important CSS Properties
      3. Scripting Inline Styles
      4. Querying Computed Styles
      5. Scripting CSS Classes
      6. Scripting Stylesheets
    5. 17. Handling Events
      1. Types of Events
      2. Registering Event Handlers
      3. Event Handler Invocation
      4. Document Load Events
      5. Mouse Events
      6. Mousewheel Events
      7. Drag and Drop Events
      8. Text Events
      9. Keyboard Events
    6. 18. Scripted HTTP
      1. Using XMLHttpRequest
      2. HTTP by <script>: JSONP
      3. Comet with Server-Sent Events
    7. 19. The jQuery Library
      1. jQuery Basics
      2. jQuery Getters and Setters
      3. Altering Document Structure
      4. Handling Events with jQuery
      5. Animated Effects
      6. Ajax with jQuery
      7. Utility Functions
      8. jQuery Selectors and Selection Methods
      9. Extending jQuery with Plug-ins
      10. The jQuery UI Library
    8. 20. Client-Side Storage
      1. localStorage and sessionStorage
      2. Cookies
      3. IE userData Persistence
      4. Application Storage and Offline Webapps
    9. 21. Scripted Media and Graphics
      1. Scripting Images
      2. Scripting Audio and Video
      3. SVG: Scalable Vector Graphics
      4. Graphics in a <canvas>
    10. 22. HTML5 APIs
      1. Geolocation
      2. History Management
      3. Cross-Origin Messaging
      4. Web Workers
      5. Typed Arrays and ArrayBuffers
      6. Blobs
      7. The Filesystem API
      8. Client-Side Databases
      9. Web Sockets
  8. III. Core JavaScript Reference
    1. I. Core JavaScript Reference
      1. arguments[ ]
      2. Arguments
      3. Arguments.callee
      4. Arguments.length
      5. Array
      6. Array.concat()
      7. Array.every()
      8. Array.filter()
      9. Array.forEach()
      10. Array.indexOf()
      11. Array.join()
      12. Array.lastIndexOf()
      13. Array.length
      14. Array.map()
      15. Array.pop()
      16. Array.push()
      17. Array.reduce()
      18. Array.reduceRight()
      19. Array.reverse()
      20. Array.shift()
      21. Array.slice()
      22. Array.some()
      23. Array.sort()
      24. Array.splice()
      25. Array.toLocaleString()
      26. Array.toString()
      27. Array.unshift()
      28. Boolean
      29. Boolean.toString()
      30. Boolean.valueOf()
      31. Date
      32. Date.getDate()
      33. Date.getDay()
      34. Date.getFullYear()
      35. Date.getHours()
      36. Date.getMilliseconds()
      37. Date.getMinutes()
      38. Date.getMonth()
      39. Date.getSeconds()
      40. Date.getTime()
      41. Date.getTimezoneOffset()
      42. Date.getUTCDate()
      43. Date.getUTCDay()
      44. Date.getUTCFullYear()
      45. Date.getUTCHours()
      46. Date.getUTCMilliseconds()
      47. Date.getUTCMinutes()
      48. Date.getUTCMonth()
      49. Date.getUTCSeconds()
      50. Date.getYear()
      51. Date.now()
      52. Date.parse()
      53. Date.setDate()
      54. Date.setFullYear()
      55. Date.setHours()
      56. Date.setMilliseconds()
      57. Date.setMinutes()
      58. Date.setMonth()
      59. Date.setSeconds()
      60. Date.setTime()
      61. Date.setUTCDate()
      62. Date.setUTCFullYear()
      63. Date.setUTCHours()
      64. Date.setUTCMilliseconds()
      65. Date.setUTCMinutes()
      66. Date.setUTCMonth()
      67. Date.setUTCSeconds()
      68. Date.setYear()
      69. Date.toDateString()
      70. Date.toGMTString()
      71. Date.toISOString()
      72. Date.toJSON
      73. Date.toLocaleDateString()
      74. Date.toLocaleString()
      75. Date.toLocaleTimeString()
      76. Date.toString()
      77. Date.toTimeString()
      78. Date.toUTCString()
      79. Date.UTC()
      80. Date.valueOf()
      81. decodeURI()
      82. decodeURIComponent()
      83. encodeURI()
      84. encodeURIComponent()
      85. Error
      86. Error.message
      87. Error.name
      88. Error.toString()
      89. escape()
      90. eval()
      91. EvalError
      92. Function
      93. Function.apply()
      94. Function.arguments[]
      95. Function.bind()
      96. Function.call()
      97. Function.caller
      98. Function.length
      99. Function.prototype
      100. Function.toString()
      101. Global
      102. Infinity
      103. isFinite()
      104. isNaN()
      105. JSON
      106. JSON.parse()
      107. JSON.stringify()
      108. Math
      109. Math.abs()
      110. Math.acos()
      111. Math.asin()
      112. Math.atan()
      113. Math.atan2()
      114. Math.ceil()
      115. Math.cos()
      116. Math.E
      117. Math.exp()
      118. Math.floor()
      119. Math.LN10
      120. Math.LN2
      121. Math.log()
      122. Math.LOG10E
      123. Math.LOG2E
      124. Math.max()
      125. Math.min()
      126. Math.PI
      127. Math.pow()
      128. Math.random()
      129. Math.round()
      130. Math.sin()
      131. Math.sqrt()
      132. Math.SQRT1_2
      133. Math.SQRT2
      134. Math.tan()
      135. NaN
      136. Number
      137. Number.MAX_VALUE
      138. Number.MIN_VALUE
      139. Number.NaN
      140. Number.NEGATIVE_INFINITY
      141. Number.POSITIVE_INFINITY
      142. Number.toExponential()
      143. Number.toFixed()
      144. Number.toLocaleString()
      145. Number.toPrecision()
      146. Number.toString()
      147. Number.valueOf()
      148. Object
      149. Object.constructor
      150. Object.create()
      151. Object.defineProperties()
      152. Object.defineProperty()
      153. Object.freeze()
      154. Object.getOwnPropertyDescriptor()
      155. Object.getOwnPropertyNames()
      156. Object.getPrototypeOf()
      157. Object.hasOwnProperty()
      158. Object.isExtensible()
      159. Object.isFrozen()
      160. Object.isPrototypeOf()
      161. Object.isSealed()
      162. Object.keys()
      163. Object.preventExtensions()
      164. Object.propertyIsEnumerable()
      165. Object.seal()
      166. Object.toLocaleString()
      167. Object.toString()
      168. Object.valueOf()
      169. parseFloat()
      170. parseInt()
      171. RangeError
      172. ReferenceError
      173. RegExp
      174. RegExp.exec()
      175. RegExp.global
      176. RegExp.ignoreCase
      177. RegExp.lastIndex
      178. RegExp.source
      179. RegExp.test()
      180. RegExp.toString()
      181. String
      182. String.charAt()
      183. String.charCodeAt()
      184. String.concat()
      185. String.fromCharCode()
      186. String.indexOf()
      187. String.lastIndexOf()
      188. String.length
      189. String.localeCompare()
      190. String.match()
      191. String.replace()
      192. String.search()
      193. String.slice()
      194. String.split()
      195. String.substr()
      196. String.substring()
      197. String.toLocaleLowerCase()
      198. String.toLocaleUpperCase()
      199. String.toLowerCase()
      200. String.toString()
      201. String.toUpperCase()
      202. String.trim()
      203. String.valueOf()
      204. SyntaxError
      205. TypeError
      206. undefined
      207. unescape()
      208. URIError
  9. IV. Client-Side JavaScript Reference
    1. II. Client-Side JavaScript Reference
      1. ApplicationCache
      2. ArrayBuffer
      3. ArrayBufferView
      4. Attr
      5. Audio
      6. BeforeUnloadEvent
      7. Blob
      8. BlobBuilder
      9. Button
      10. Canvas
      11. CanvasGradient
      12. CanvasPattern
      13. CanvasRenderingContext2D
      14. ClientRect
      15. CloseEvent
      16. Comment
      17. Console
      18. ConsoleCommandLine
      19. CSS2Properties
      20. CSSRule
      21. CSSStyleDeclaration
      22. CSSStyleSheet
      23. DataTransfer
      24. DataView
      25. Document
      26. DocumentFragment
      27. DocumentType
      28. DOMException
      29. DOMImplementation
      30. DOMSettableTokenList
      31. DOMTokenList
      32. Element
      33. ErrorEvent
      34. Event
      35. EventSource
      36. EventTarget
      37. FieldSet
      38. File
      39. FileError
      40. FileReader
      41. FileReaderSync
      42. Form
      43. FormControl
      44. FormData
      45. FormValidity
      46. Geocoordinates
      47. Geolocation
      48. GeolocationError
      49. Geoposition
      50. HashChangeEvent
      51. History
      52. HTMLCollection
      53. HTMLDocument
      54. HTMLElement
      55. HTMLFormControlsCollection
      56. HTMLOptionsCollection
      57. IFrame
      58. Image
      59. ImageData
      60. Input
      61. jQuery
      62. KeyEvent
      63. Label
      64. Link
      65. Location
      66. MediaElement
      67. MediaError
      68. MessageChannel
      69. MessageEvent
      70. MessagePort
      71. Meter
      72. MouseEvent
      73. Navigator
      74. Node
      75. NodeList
      76. Option
      77. Output
      78. PageTransitionEvent
      79. PopStateEvent
      80. ProcessingInstruction
      81. Progress
      82. ProgressEvent
      83. Screen
      84. Script
      85. Select
      86. Storage
      87. StorageEvent
      88. Style
      89. Table
      90. TableCell
      91. TableRow
      92. TableSection
      93. Text
      94. TextArea
      95. TextMetrics
      96. TimeRanges
      97. TypedArray
      98. URL
      99. Video
      100. WebSocket
      101. Window
      102. Worker
      103. WorkerGlobalScope
      104. WorkerLocation
      105. WorkerNavigator
      106. XMLHttpRequest
      107. XMLHttpRequestUpload
  10. Index
  11. About the Author
  12. Colophon
  13. Copyright
O'Reilly logo

Client-Side JavaScript

Client-side JavaScript does not exhibit the nonlinear cross-reference problem nearly to the extent that the core language does, and it is possible to learn how to use JavaScript in web browsers in a fairly linear sequence. But you’re probably reading this book to learn client-side JavaScript, and Part II is a long way off, so this section is a quick sketch of basic client-side programming techniques, followed by an in-depth example.

Chapter 13, JavaScript in Web Browsers, is the first chapter of Part II and it explains in detail how to put JavaScript to work in web browsers. The most important thing you’ll learn in that chapter is that JavaScript code can be embedded within HTML files using the <script> tag:

<html>
<head>
<script src="library.js"></script> <!-- include a library of JavaScript code -->
</head>
<body>
<p>This is a paragraph of HTML</p>
<script>
// And this is some client-side JavaScript code
// literally embedded within the HTML file
</script>
<p>Here is more HTML.</p>
</body>
</html>

Chapter 14, The Window Object, explains techniques for scripting the web browser and covers some important global functions of client-side JavaScript. For example:

<script>
function moveon() {
    // Display a modal dialog to ask the user a question
    var answer = confirm("Ready to move on?");
    // If they clicked the "OK" button, make the browser load a new page
    if (answer) window.location = "http://google.com";
}
// Run the function defined above 1 minute (60,000 milliseconds) from now.
setTimeout(moveon, 60000);
</script>

Note that the client-side example code shown in this section comes in longer snippets than the core language examples earlier in the chapter. These examples are not designed to be typed into a Firebug (or similar) console window. Instead you can embed them in an HTML file and try them out by loading them in your web browser. The code above, for instance, works as a stand-alone HTML file.

Chapter 15, Scripting Documents, gets down to the real business of client-side JavaScript, scripting HTML document content. It shows you how to select particular HTML elements from within a document, how to set HTML attributes of those elements, how to alter the content of those elements, and how to add new elements to the document. This function demonstrates a number of these basic document searching and modification techniques:

// Display a message in a special debugging output section of the document.
// If the document does not contain such a section, create one.
function debug(msg) {
    // Find the debugging section of the document, looking at HTML id attributes
    var log = document.getElementById("debuglog");
    
    // If no element with the id "debuglog" exists, create one.
    if (!log) {
        log = document.createElement("div");  // Create a new <div> element
        log.id = "debuglog";                  // Set the HTML id attribute on it
        log.innerHTML = "<h1>Debug Log</h1>"; // Define initial content
        document.body.appendChild(log);       // Add it at end of document
    }

    // Now wrap the message in its own <pre> and append it to the log
    var pre = document.createElement("pre");  // Create a <pre> tag
    var text = document.createTextNode(msg);  // Wrap msg in a text node
    pre.appendChild(text);                    // Add text to the <pre>
    log.appendChild(pre);                     // Add <pre> to the log
}

Chapter 15 shows how JavaScript can script the HTML elements that define web content. Chapter 16, Scripting CSS, shows how you can use JavaScript with the CSS styles that define the presentation of that content. This is often done with the style or class attribute of HTML elements:

function hide(e, reflow) { // Hide the element e by scripting its style
    if (reflow) {                      // If 2nd argument is true
        e.style.display = "none"       // hide element and use its space
    }
    else {                             // Otherwise
        e.style.visibility = "hidden"; // make e invisible, but leave its space
    }
}

function highlight(e) {    // Highlight e by setting a CSS class
    // Simply define or append to the HTML class attribute.
    // This assumes that a CSS stylesheet already defines the "hilite" class
    if (!e.className) e.className = "hilite";
    else e.className += " hilite";
}

JavaScript allows us to script the HTML content and CSS presentation of documents in web browsers, but it also allows us to define behavior for those documents with event handlers. An event handler is a JavaScript function that we register with the browser and the browser invokes when some specified type of event occurs. The event of interest might be a mouse click or a key press (or on a smart phone, it might be a two-finger gesture of some sort). Or an event handler might be triggered when the browser finishes loading a document, when the user resizes the browser window, or when the user enters data into an HTML form element. Chapter 17, Handling Events, explains how you can define and register event handlers and how the browser invokes them when events occur.

The simplest way to define event handlers is with HTML attributes that begin with “on”. The “onclick” handler is a particularly useful one when you’re writing simple test programs. Suppose that you had typed in the debug() and hide() functions from above and saved them in files named debug.js and hide.js. You could write a simple HTML test file using <button> elements with onclick event handler attributes:

<script src="debug.js"></script>
<script src="hide.js"></script>
Hello
<button onclick="hide(this,true); debug('hide button 1');">Hide1</button>
<button onclick="hide(this); debug('hide button 2');">Hide2</button>
World

Here is some more client-side JavaScript code that uses events. It registers an event handler for the very important “load” event, and it also demonstrates a more sophisticated way of registering event handler functions for “click” events:

// The "load" event occurs when a document is fully loaded. Usually we
// need to wait for this event before we start running our JavaScript code.
window.onload = function() {  // Run this function when the document loads
    // Find all <img> tags in the document
    var images = document.getElementsByTagName("img");

    // Loop through them, adding an event handler for "click" events to each
    // so that clicking on the image hides it.
    for(var i = 0; i < images.length; i++) {
        var image = images[i];
        if (image.addEventListener) // Another way to register a handler
            image.addEventListener("click", hide, false);
        else                        // For compatibility with IE8 and before
            image.attachEvent("onclick", hide);
    }

    // This is the event handler function registered above
    function hide(event) { event.target.style.visibility = "hidden"; }
};

Chapters 15, 16, and 17 explain how you can use JavaScript to script the content (HTML), presentation (CSS), and behavior (event handling) of web pages. The APIs described in those chapters are somewhat complex and, until recently, riddled with browser incompatibilities. For these reasons, many or most client-side JavaScript programmers choose to use a client-side library or framework to simplify their basic programming tasks. The most popular such library is jQuery, the subject of Chapter 19, The jQuery Library . jQuery defines a clever and easy-to-use API for scripting document content, presentation, and behavior. It has been thoroughly tested and works in all major browsers, including old ones like IE6.

jQuery code is easy to identify because it makes frequent use of a function named $(). Here is what the debug() function used previously looks like when rewritten to use jQuery:

function debug(msg) {
    var log = $("#debuglog");          // Find the element to display msg in.
    if (log.length == 0) {             // If it doesn't exist yet, create it...
        log = $("<div id='debuglog'><h1>Debug Log</h1></div>");
        log.appendTo(document.body);   // and insert it at the end of the body.
    }
    log.append($("<pre/>").text(msg)); // Wrap msg in <pre> and append to log.
}

The four chapters of Part II described so far have all really been about web pages. Four more chapters shift gears to focus on web applications. These chapters are not about using web browsers to display documents with scriptable content, presentation, and behavior. Instead, they’re about using web browsers as application platforms, and they describe the APIs that modern browsers provide to support sophisticated client-side web apps. Chapter 18, Scripted HTTP, explains how to make scripted HTTP requests with JavaScript—a kind of networking API. Chapter 20, Client-Side Storage, describes mechanisms for storing data—and even entire applications—on the client side for use in future browsing sessions. Chapter 21, Scripted Media and Graphics, covers a client-side API for drawing arbitrary graphics in an HTML <canvas> tag. And, finally, Chapter 22, HTML5 APIs, covers an assortment of new web app APIs specified by or affiliated with HTML5. Networking, storage, graphics: these are OS-type services being provided by the web browser, defining a new cross-platform application environment. If you are targeting browsers that support these new APIs, it is an exciting time to be a client-side JavaScript programmer. There are no code samples from these final four chapters here, but the extended example below uses some of these new APIs.

Example: A JavaScript Loan Calculator

This chapter ends with an extended example that puts many of these techniques together and shows what real-world client-side JavaScript (plus HTML and CSS) programs look like. Example 1-1 lists the code for the simple loan payment calculator application pictured in Figure 1-2.

A loan calculator web application

Figure 1-2. A loan calculator web application

It is worth reading through Example 1-1 carefully. You shouldn’t expect to understand everything, but the code is heavily commented and you should be able to at least get the big-picture view of how it works. The example demonstrates a number of core JavaScript language features, and also demonstrates important client-side JavaScript techniques:

  • How to find elements in a document.

  • How to get user input from form input elements.

  • How to set the HTML content of document elements.

  • How to store data in the browser.

  • How to make scripted HTTP requests.

  • How to draw graphics with the <canvas> element.

Example 1-1. A loan calculator in JavaScript

<!DOCTYPE html>   
<html>
<head>
<title>JavaScript Loan Calculator</title>
<style> /* This is a CSS style sheet: it adds style to the program output */
.output { font-weight: bold; }           /* Calculated values in bold */
#payment { text-decoration: underline; } /* For element with id="payment" */
#graph { border: solid black 1px; }      /* Chart has a simple border */
th, td { vertical-align: top; }          /* Don't center table cells */
</style>
</head>
<body>
<!--
  This is an HTML table with <input> elements that allow the user to enter data
  and <span> elements in which the program can display its results.
  These elements have ids like "interest" and "years". These ids are used
  in the JavaScript code that follows the table. Note that some of the input
  elements define "onchange" or "onclick" event handlers. These specify strings
  of JavaScript code to be executed when the user enters data or clicks.
-->
<table>
  <tr><th>Enter Loan Data:</th>
      <td></td>
      <th>Loan Balance, Cumulative Equity, and Interest Payments</th></tr>
  <tr><td>Amount of the loan ($):</td>
      <td><input id="amount" onchange="calculate();"></td>
      <td rowspan=8>
         <canvas id="graph" width="400" height="250"></canvas></td></tr>
  <tr><td>Annual interest (%):</td>
      <td><input id="apr" onchange="calculate();"></td></tr>
  <tr><td>Repayment period (years):</td>
      <td><input id="years" onchange="calculate();"></td>
  <tr><td>Zipcode (to find lenders):</td>
      <td><input id="zipcode" onchange="calculate();"></td>
  <tr><th>Approximate Payments:</th>
      <td><button onclick="calculate();">Calculate</button></td></tr>
  <tr><td>Monthly payment:</td>
      <td>$<span class="output" id="payment"></span></td></tr>
  <tr><td>Total payment:</td>
      <td>$<span class="output" id="total"></span></td></tr>
  <tr><td>Total interest:</td>
      <td>$<span class="output" id="totalinterest"></span></td></tr>
  <tr><th>Sponsors:</th><td  colspan=2>
    Apply for your loan with one of these fine lenders:
    <div id="lenders"></div></td></tr>
</table>

<!-- The rest of this example is JavaScript code in the <script> tag below -->
<!-- Normally, this script would go in the document <head> above but it -->
<!-- is easier to understand here, after you've seen its HTML context. -->
<script>
"use strict"; // Use ECMAScript 5 strict mode in browsers that support it

/*
 * This script defines the calculate() function called by the event handlers
 * in HTML above. The function reads values from <input> elements, calculates
 * loan payment information, displays the results in <span> elements. It also
 * saves the user's data, displays links to lenders, and draws a chart.
 */
function calculate() {
    // Look up the input and output elements in the document
    var amount = document.getElementById("amount");
    var apr = document.getElementById("apr");
    var years = document.getElementById("years");
    var zipcode = document.getElementById("zipcode");
    var payment = document.getElementById("payment");
    var total = document.getElementById("total");
    var totalinterest = document.getElementById("totalinterest");
    
    // Get the user's input from the input elements. Assume it is all valid.
    // Convert interest from a percentage to a decimal, and convert from
    // an annual rate to a monthly rate. Convert payment period in years
    // to the number of monthly payments.
    var principal = parseFloat(amount.value);
    var interest = parseFloat(apr.value) / 100 / 12;
    var payments = parseFloat(years.value) * 12;

    // Now compute the monthly payment figure.
    var x = Math.pow(1 + interest, payments);   // Math.pow() computes powers
    var monthly = (principal*x*interest)/(x-1);

    // If the result is a finite number, the user's input was good and
    // we have meaningful results to display
    if (isFinite(monthly)) {
        // Fill in the output fields, rounding to 2 decimal places
        payment.innerHTML = monthly.toFixed(2);
        total.innerHTML = (monthly * payments).toFixed(2);
        totalinterest.innerHTML = ((monthly*payments)-principal).toFixed(2);

        // Save the user's input so we can restore it the next time they visit
        save(amount.value, apr.value, years.value, zipcode.value);
        
        // Advertise: find and display local lenders, but ignore network errors
        try {      // Catch any errors that occur within these curly braces
            getLenders(amount.value, apr.value, years.value, zipcode.value);
        }
        catch(e) { /* And ignore those errors */ }

        // Finally, chart loan balance, and interest and equity payments
        chart(principal, interest, monthly, payments);
    }
    else {  
        // Result was Not-a-Number or infinite, which means the input was
        // incomplete or invalid. Clear any previously displayed output.
        payment.innerHTML = "";        // Erase the content of these elements
        total.innerHTML = ""
        totalinterest.innerHTML = "";
        chart();                       // With no arguments, clears the chart
    }
}

// Save the user's input as properties of the localStorage object. Those
// properties will still be there when the user visits in the future
// This storage feature will not work in some browsers (Firefox, e.g.) if you 
// run the example from a local file:// URL.  It does work over HTTP, however.
function save(amount, apr, years, zipcode) {
    if (window.localStorage) {  // Only do this if the browser supports it
        localStorage.loan_amount = amount;
        localStorage.loan_apr = apr;
        localStorage.loan_years = years;
        localStorage.loan_zipcode = zipcode;
    }
}

// Automatically attempt to restore input fields when the document first loads.
window.onload = function() {
    // If the browser supports localStorage and we have some stored data
    if (window.localStorage && localStorage.loan_amount) {  
        document.getElementById("amount").value = localStorage.loan_amount;
        document.getElementById("apr").value = localStorage.loan_apr;
        document.getElementById("years").value = localStorage.loan_years;
        document.getElementById("zipcode").value = localStorage.loan_zipcode;
    }
};

// Pass the user's input to a server-side script which can (in theory) return
// a list of links to local lenders interested in making loans.  This example
// does not actually include a working implementation of such a lender-finding
// service. But if the service existed, this function would work with it.
function getLenders(amount, apr, years, zipcode) {
    // If the browser does not support the XMLHttpRequest object, do nothing
    if (!window.XMLHttpRequest) return;

    // Find the element to display the list of lenders in
    var ad = document.getElementById("lenders");
    if (!ad) return;                            // Quit if no spot for output 

    // Encode the user's input as query parameters in a URL
    var url = "getLenders.php" +                // Service url plus
        "?amt=" + encodeURIComponent(amount) +  // user data in query string
        "&apr=" + encodeURIComponent(apr) +
        "&yrs=" + encodeURIComponent(years) +
        "&zip=" + encodeURIComponent(zipcode);

    // Fetch the contents of that URL using the XMLHttpRequest object
    var req = new XMLHttpRequest();        // Begin a new request
    req.open("GET", url);                  // An HTTP GET request for the url
    req.send(null);                        // Send the request with no body

    // Before returning, register an event handler function that will be called
    // at some later time when the HTTP server's response arrives. This kind of 
    // asynchronous programming is very common in client-side JavaScript.
    req.onreadystatechange = function() {
        if (req.readyState == 4 && req.status == 200) {
            // If we get here, we got a complete valid HTTP response
            var response = req.responseText;     // HTTP response as a string
            var lenders = JSON.parse(response);  // Parse it to a JS array

            // Convert the array of lender objects to a string of HTML
            var list = "";
            for(var i = 0; i < lenders.length; i++) {
                list += "<li><a href='" + lenders[i].url + "'>" +
                    lenders[i].name + "</a>";
            }

            // Display the HTML in the element from above.
            ad.innerHTML = "<ul>" + list + "</ul>"; 
        }
    }
}

// Chart monthly loan balance, interest and equity in an HTML <canvas> element.
// If called with no arguments then just erase any previously drawn chart.
function chart(principal, interest, monthly, payments) {
    var graph = document.getElementById("graph"); // Get the <canvas> tag
    graph.width = graph.width;  // Magic to clear and reset the canvas element

    // If we're called with no arguments, or if this browser does not support
    // graphics in a <canvas> element, then just return now.
    if (arguments.length == 0 || !graph.getContext) return;

    // Get the "context" object for the <canvas> that defines the drawing API
    var g = graph.getContext("2d"); // All drawing is done with this object
    var width = graph.width, height = graph.height; // Get canvas size

    // These functions convert payment numbers and dollar amounts to pixels
    function paymentToX(n) { return n * width/payments; }
    function amountToY(a) { return height-(a * height/(monthly*payments*1.05));}

    // Payments are a straight line from (0,0) to (payments, monthly*payments)
    g.moveTo(paymentToX(0), amountToY(0));         // Start at lower left
    g.lineTo(paymentToX(payments),                 // Draw to upper right
             amountToY(monthly*payments));
    g.lineTo(paymentToX(payments), amountToY(0));  // Down to lower right
    g.closePath();                                 // And back to start
    g.fillStyle = "#f88";                          // Light red
    g.fill();                                      // Fill the triangle
    g.font = "bold 12px sans-serif";               // Define a font
    g.fillText("Total Interest Payments", 20,20);  // Draw text in legend

    // Cumulative equity is non-linear and trickier to chart
    var equity = 0;
    g.beginPath();                                 // Begin a new shape
    g.moveTo(paymentToX(0), amountToY(0));         // starting at lower-left
    for(var p = 1; p <= payments; p++) {
        // For each payment, figure out how much is interest
        var thisMonthsInterest = (principal-equity)*interest;
        equity += (monthly - thisMonthsInterest);  // The rest goes to equity
        g.lineTo(paymentToX(p),amountToY(equity)); // Line to this point
    }
    g.lineTo(paymentToX(payments), amountToY(0));  // Line back to X axis
    g.closePath();                                 // And back to start point
    g.fillStyle = "green";                         // Now use green paint
    g.fill();                                      // And fill area under curve
    g.fillText("Total Equity", 20,35);             // Label it in green

    // Loop again, as above, but chart loan balance as a thick black line
    var bal = principal;
    g.beginPath();
    g.moveTo(paymentToX(0),amountToY(bal));
    for(var p = 1; p <= payments; p++) {
        var thisMonthsInterest = bal*interest;
        bal -= (monthly - thisMonthsInterest);     // The rest goes to equity
        g.lineTo(paymentToX(p),amountToY(bal));    // Draw line to this point
    }
    g.lineWidth = 3;                               // Use a thick line
    g.stroke();                                    // Draw the balance curve
    g.fillStyle = "black";                         // Switch to black text
    g.fillText("Loan Balance", 20,50);             // Legend entry

    // Now make yearly tick marks and year numbers on X axis
    g.textAlign="center";                          // Center text over ticks
    var y = amountToY(0);                          // Y coordinate of X axis
    for(var year=1; year*12 <= payments; year++) { // For each year
        var x = paymentToX(year*12);               // Compute tick position
        g.fillRect(x-0.5,y-3,1,3);                 // Draw the tick
        if (year == 1) g.fillText("Year", x, y-5); // Label the axis
        if (year % 5 == 0 && year*12 !== payments) // Number every 5 years
            g.fillText(String(year), x, y-5);
    }

    // Mark payment amounts along the right edge
    g.textAlign = "right";                         // Right-justify text
    g.textBaseline = "middle";                     // Center it vertically
    var ticks = [monthly*payments, principal];     // The two points we'll mark
    var rightEdge = paymentToX(payments);          // X coordinate of Y axis
    for(var i = 0; i < ticks.length; i++) {        // For each of the 2 points
        var y = amountToY(ticks[i]);               // Compute Y position of tick
        g.fillRect(rightEdge-3, y-0.5, 3,1);       // Draw the tick mark
        g.fillText(String(ticks[i].toFixed(0)),    // And label it.
                   rightEdge-5, y);
    }
}
</script>
</body>
</html>

The best content for your career. Discover unlimited learning on demand for around $1/day.