JavaScript Cookbook, 2nd Edition

wxm8

贡献于2015-02-09

字数:0 关键词: JavaScript开发 Java JavaScript

JAVASCRIPT/WEB JavaScript Cookbook ISBN: 978-1-491-90188-5 US $49.99 CAN $57.99 “A comprehensive, example- driven tour of the language and its platforms.”—Dr. Axel Rauschmayer author of Speaking JavaScript Shelley Powers has been working with and writing about web tech- nologies—from the first release of JavaScript to the latest graphics and design tools—for more than 18 years. Her recent O’Reilly books have covered JavaScript, HTML5 media objects, Ajax, and web graphics. Twitter: @oreillymedia facebook.com/oreilly Problem solving with JavaScript is a lot trickier now that its use has expanded considerably in size, scope, and complexity. This cookbook has your back, with recipes for common tasks across the JavaScript world, whether you’re working in the browser, the server, or a mobile environment. Each recipe includes reusable code and practical advice for tackling JavaScript objects, Node, Ajax, JSON, data persistence, graphical and media applications, complex frameworks, modular JavaScript, APIs, and many related technologies. Aimed at people who have some experience with JavaScript, the first part covers traditional uses of JavaScript, along with new ideas and improved functionality. The second part dives into the server, mobile development, and a plethora of leading-edge tools. You’ll save time—and learn more about JavaScript in the process. Topics include: Classic JavaScript: ■■ Arrays, functions, and the JavaScript Object ■■ Accessing the user interface ■■ Testing and accessibility ■■ Creating and using JavaScript libraries ■■ Client-server communication with Ajax ■■ Rich, interactive web effects JavaScript, All Blown Up: ■■ New ECMAScript standard objects ■■ Using Node on the server ■■ Modularizing and managing JavaScript ■■ Complex JavaScript frameworks ■■ Advanced client-server communications ■■ Visualizations and client-server graphics ■■ Mobile application development JavaScript Cookbook SECOND EDITION Powers Shelley Powers JavaScript Cookbook PROGRAMMING THE WEB 2nd Edition JAVASCRIPT/WEB JavaScript Cookbook ISBN: 978-1-491-90188-5 US $49.99 CAN $57.99 “A comprehensive, example- driven tour of the language and its platforms.”—Dr. Axel Rauschmayer author of Speaking JavaScript Shelley Powers has been working with and writing about web tech- nologies—from the first release of JavaScript to the latest graphics and design tools—for more than 18 years. Her recent O’Reilly books have covered JavaScript, HTML5 media objects, Ajax, and web graphics. Twitter: @oreillymedia facebook.com/oreilly Problem solving with JavaScript is a lot trickier now that its use has expanded considerably in size, scope, and complexity. This cookbook has your back, with recipes for common tasks across the JavaScript world, whether you’re working in the browser, the server, or a mobile environment. Each recipe includes reusable code and practical advice for tackling JavaScript objects, Node, Ajax, JSON, data persistence, graphical and media applications, complex frameworks, modular JavaScript, APIs, and many related technologies. Aimed at people who have some experience with JavaScript, the first part covers traditional uses of JavaScript, along with new ideas and improved functionality. The second part dives into the server, mobile development, and a plethora of leading-edge tools. You’ll save time—and learn more about JavaScript in the process. Topics include: Classic JavaScript: ■■ Arrays, functions, and the JavaScript Object ■■ Accessing the user interface ■■ Testing and accessibility ■■ Creating and using JavaScript libraries ■■ Client-server communication with Ajax ■■ Rich, interactive web effects JavaScript, All Blown Up: ■■ New ECMAScript standard objects ■■ Using Node on the server ■■ Modularizing and managing JavaScript ■■ Complex JavaScript frameworks ■■ Advanced client-server communications ■■ Visualizations and client-server graphics ■■ Mobile application development JavaScript Cookbook SECOND EDITION Powers Shelley Powers JavaScript Cookbook PROGRAMMING THE WEB 2nd Edition Shelley Powers SECOND EDITION JavaScript Cookbook JavaScript Cookbook, Second Edition by Shelley Powers Copyright © 2015 Shelley Powers. All rights reserved. Printed in the United States of America. Published by O’Reilly Media, Inc., 1005 Gravenstein Highway North, Sebastopol, CA 95472. O’Reilly books may be purchased for educational, business, or sales promotional use. Online editions are also available for most titles (http://safaribooksonline.com). For more information, contact our corporate/ institutional sales department: 800-998-9938 or corporate@oreilly.com. Editors: Simon St. Laurent and Brian MacDonald Production Editor: Kara Ebrahim Copyeditor: Jasmine Kwityn Proofreader: Kara Ebrahim Indexer: Judy McConville Cover Designer: Ellie Volckhausen Interior Designer: David Futato Illustrator: Rebecca Demarest February 2015: Second Edition Revision History for the Second Edition: 2015-01-23: First release See http://oreilly.com/catalog/errata.csp?isbn=9781491901885 for release details. The O’Reilly logo is a registered trademark of O’Reilly Media, Inc. JavaScript Cookbook, Second Edition, the cover image of a little egret, and related trade dress are trademarks of O’Reilly Media, Inc. While the publisher and the author have used good faith efforts to ensure that the information and instruc‐ tions contained in this work are accurate, the publisher and the author disclaim all responsibility for errors or omissions, including without limitation responsibility for damages resulting from the use of or reliance on this work. Use of the information and instructions contained in this work is at your own risk. If any code samples or other technology this work contains or describes is subject to open source licenses or the intel‐ lectual property rights of others, it is your responsibility to ensure that your use thereof complies with such licenses and/or rights. ISBN: 978-1-491-90188-5 [LSI] Table of Contents The World of JavaScript. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ix Part I. Classic JavaScript 1. The JavaScript Not-So-Simple Building Blocks. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 1.1. Differentiating Between a JavaScript Object, Primitive, and Literal 3 1.2. Extracting a List from a String 7 1.3. Checking for an Existing, Nonempty String 10 1.4. Inserting Special Characters 14 1.5. Replacing Patterns with New Strings 16 1.6. Finding and Highlighting All Instances of a Pattern 18 1.7. Swapping Words in a String Using Capturing Parentheses 22 1.8. Replacing HTML Tags with Named Entities 25 1.9. Converting an ISO 8601 Formatted Date to a Date Object Acceptable Format 25 1.10. Using Function Closures with Timers 29 1.11. Tracking Elapsed Time 31 1.12. Converting a Decimal to a Hexadecimal Value 32 1.13. Summing All Numbers in a Table Column 33 1.14. Converting Between Degrees and Radians 36 1.15. Find the Radius and Center of a Circle to Fit Within a Page Element 37 1.16. Calculating the Length of a Circular Arc 39 1.17. Using ES6 String Extras Without Leaving Users in the Dirt 40 2. JavaScript Arrays. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43 2.1. Searching Through an Array 43 2.2. Flattening a Two-Dimensional Array with concat() and apply() 45 2.3. Removing or Replacing Array Elements 46 2.4. Extracting a Portion of an Array 48 iii 2.5. Applying a Function Against Each Array Element 48 2.6. Traversing the Results from querySelectorAll() with forEach() and call() 50 2.7. Applying a Function to Every Element in an Array and Returning a New Array 51 2.8. Creating a Filtered Array 52 2.9. Validating Array Contents 52 2.10. Using an Associative Array to Store Form Element Names and Values 54 2.11. Using a Destructuring Assignment to Simplify Code 58 3. Functions: The JavaScript Building Blocks. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61 3.1. Placing Your Function and Hoisting 61 3.2. Passing a Function As an Argument to Another Function 63 3.3. Implementing a Recursive Algorithm 65 3.4. Preventing Code Blocking with a Timer and a Callback 68 3.5. Creating a Function That Remembers Its State 72 3.6. Converting Function Arguments into an Array 75 3.7. Reducing Redundancy by Using a Partial Application 77 3.8. Improving Application Performance with Memoization (Caching Calculations) 80 3.9. Using an Anonymous Function to Wrap Global Variables 82 3.10. Providing a Default Parameter 83 4. The Malleable JavaScript Object. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85 4.1. Keeping Object Members Private 86 4.2. Using Prototype to Create Objects 87 4.3. Inheriting an Object’s Functionality 90 4.4. Extending an Object by Defining a New Property 92 4.5. Preventing Object Extensibility 94 4.6. Preventing Any Changes to an Object 95 4.7. Namespacing Your JavaScript Objects 97 4.8. Rediscovering this with Prototype.bind 100 4.9. Chaining Your Object’s Methods 103 5. JavaScript and Directly Accessing the User Interface. . . . . . . . . . . . . . . . . . . . . . . . . . . . 107 5.1. Accessing a Given Element and Finding Its Parent and Child Elements 107 5.2. Accessing All Images in the Web Page 110 5.3. Discovering All Images in Articles Using the Selectors API 116 5.4. Setting an Element’s Style Attribute 119 5.5. Applying a Striped Theme to an Unordered List 122 5.6. Finding All Elements That Share an Attribute 123 5.7. Inserting a New Paragraph 124 5.8. Adding Text to a New Paragraph 125 iv | Table of Contents 5.9. Deleting Rows from an HTML Table 127 5.10. Adding a Page Overlay 129 5.11. Creating Collapsible Form Sections 133 5.12. Hiding Page Sections 136 5.13. Creating Hover-Based Pop-Up Info Windows 137 5.14. Displaying a Flash of Color to Signal an Action 140 6. Preliminary Testing and Accessibility. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143 6.1. Cleaning Up Your Code with JSHint 143 6.2. Unit Testing Your Code with QUnit 145 6.3. Testing Your Application in Various Environments 148 6.4. Performance Testing Different Coding Techniques 152 6.5. Highlighting Errors Accessibly 156 6.6. Creating an Accessible Automatically Updated Region 163 7. Creating and Using JavaScript Libraries. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165 7.1. Finding the Perfect Library 165 7.2. Testing for Features with Modernizr.load 166 7.3. Going Beyond the Math Object’s Capability 168 7.4. Finding the Number of Days Between Two Dates 170 7.5. Using an External Library: Building on the jQuery Framework 171 7.6. Using a jQuery Plugin 174 7.7. Handling Keyboard Shortcuts with Mousetrap 177 7.8. Utilizing the Utility Library Underscore 180 7.9. Packaging Your Code 182 7.10. Adding Support for Private Data Members 185 7.11. Minify Your Library 187 7.12. Hosting Your Library 189 7.13. Serving Code from a CDN 192 7.14. Convert Your Library to a jQuery Plug-in 193 7.15. Safely Combining Several Libraries in Your Applications 195 8. Simplified Client-Server Communication and Data. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199 8.1. Handling an XML Document Returned via an Ajax Call 199 8.2. Extracting Pertinent Information from an XML Tree 201 8.3. Parsing and Automatically Modifying JSON 206 8.4. Converting an Object to a Filtered/Transformed String with JSON 208 8.5. Making an Ajax Request to Another Domain (Using JSONP) 209 8.6. Processing JSON from an Ajax Request 212 8.7. Populating a Selection List from the Server 214 8.8. Using a Timer to Automatically Update the Page with Fresh Data 218 Table of Contents | v 9. Creating Media Rich, Interactive Web Effects. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221 9.1. Creating a Dynamic Line Chart in Canvas 221 9.2. Adding JavaScript to SVG 227 9.3. Accessing SVG from Web Page Script 231 9.4. Integrating SVG and the Canvas Element in HTML 233 9.5. Running a Routine When an Audio File Begins Playing 235 9.6. Controlling Video from JavaScript with the video Element 237 9.7. Adding Filter Effects to Video via Canvas 241 Part II. JavaScript, All Blown Up 10. The New ECMAScript Standard Objects. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 249 10.1. Using let in Your Browser Application 250 10.2. Creating a Collection of Non-Duplicated Values 253 10.3. Creating Unique Key/Value Pairs with Uniquely Different Keys 255 10.4. Creating Absolutely Unique Object Property Keys 258 10.5. Iterating Through Tasks Made Easy 260 10.6. Creating Functions that Gracefully Yield 261 10.7. Implementing Just-in-Time Object Behavioral Modifications with Proxies 263 10.8. Creating a True Class and Extending It (with a Little Help from Traceur) 266 10.9. Using Promises for Efficient Asynchronous Processing 270 11. Node: JavaScript on the Server. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 273 11.1. Responding to a Simple Browser Request 273 11.2. Serving Up Formatted Data 276 11.3. Reading and Writing File Data 278 11.4. Using let and Other ES 6 Additions in Node 283 11.5. Interactively Trying Out Node Code Snippets with REPL 284 11.6. Getting Input from the Terminal 287 11.7. Working with Node Timers and Understanding the Node Event Loop 289 11.8. Managing Callback Hell 293 11.9. Accessing Command-Line Functionality Within a Node Application 297 11.10. Running Node and Apache on the Same Port 300 11.11. Keeping a Node Instance Up and Running 302 11.12. Monitoring Application Changes and Restarting 304 11.13. Screen Scraping with Request 305 11.14. Creating a Command-Line Utility with Help From Commander 307 12. Modularizing and Managing JavaScript. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 311 12.1. Loading Scripts with a Script Loader 312 vi | Table of Contents 12.2. Loading Scripts Asynchronously the HTML5 Way 315 12.3. Converting Your JavaScript to AMD and RequireJS 316 12.4. Using RequireJS with jQuery or Another Library 319 12.5. Loading and Using Dojo Modules 323 12.6. Installing and Maintaining Node Modules with npm 325 12.7. Searching for a Specific Node Module via npm 326 12.8. Converting Your Library into a Node Module 327 12.9. Taking Your Code Across All Module Environments 329 12.10. Creating an Installable Node Module 333 12.11. Packaging and Managing Your Client-Side Dependencies with Bower 338 12.12. Compiling Node.js Modules for Use in the Browser with Browserify 340 12.13. Unit Testing Your Node Modules 342 12.14. Running Tasks with Grunt 345 13. Fun with APIs. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 351 13.1. Accessing JSON-Formatted Data via a RESTful API 351 13.2. Creating a RESTFul API with Restify 355 13.3. Enabling a Mobile-Like Notification in the Desktop Browser 360 13.4. Loading a File Locally in the Browser 362 13.5. Creating a Mini E-Pub Reader Using Web Workers and the File API 365 13.6. Exploring Google Maps and Other APIS 371 13.7. Accessing Twitter’s API from a Node Application 379 14. JavaScript Frameworks. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 387 14.1. Using the Express-Generator to Generate an Express Site 388 14.2. Converting a Generated Express Site into a Basic MVC App 396 14.3. Choosing a SPA Framework: Deconstructing the TodoMVC 410 14.4. Working with the OAuth Framework 422 14.5. Extending the Possible with Web Components 436 15. Advanced Client-Server Communications and Streams. . . . . . . . . . . . . . . . . . . . . . . . . . 447 15.1. Allowing Cross-Domain Requests 447 15.2. Implementing a PUT Request in Ajax 450 15.3. Sending Binary Data Through Ajax and Loading into an Image 453 15.4. Sharing HTTP Cookies Across Domains 455 15.5. Establishing Two-Way Communication Between Client and Server 458 15.6. Unloading and Zipping Files Using Transform Streams 464 15.7. Testing the Performance and Capability of Your WebSockets Application 466 16. Data Visualizations and Client/Server Graphics. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 469 16.1. Creating an SVG Bar Chart with D3 470 16.2. Mapping Data Point Variations with a Radar Chart 476 Table of Contents | vii 16.3. Feeding a Scrolling Timeline via WebSocket 478 16.4. Generating Screenshots of Generated Web Page Content (PhantomJS) 483 16.5. Converting Graphics to Text (Ocrad.js) 489 16.6. Cropping (or Otherwise Modifying) Uploaded Images 491 17. Data and Persistence. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 495 17.1. Validating Form Data 495 17.2. Persisting Information Using HTML5 502 17.3. Using sessionStorage for Client-Side Storage 505 17.4. Creating a localStorage Client-Side Data Storage Item 513 17.5. Using Squel.js to Query a MySQL Database 517 17.6. Persisting Larger Chunks of Data on the Client Using IndexedDB 520 17.7. Accessing Data in the Cloud Using Dropbox Datastores 523 18. JavaScript Hits the (Mobile) Road. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 535 18.1. Creating an Installable, Hosted Web App 535 18.2. Packaging Web Apps for the Amazon Appstore 541 18.3. Building a Basic Android App Using Cordova (PhoneGap) 543 18.4. Porting Where Am I? to Android 551 18.5. Creating a Geolocation Firefox OS App 559 18.6. Porting the Geolocation App to a Google Chrome App 569 18.7. Publishing Your Geolocation App in the Kindle Fire OS Environment 577 18.8. Debugging Your Android or Amazon Fire OS App 580 18.9. Getting Information About the Device 583 A. Up and Running in jsBin and jsFiddle. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 589 Index. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 595 viii | Table of Contents The World of JavaScript I wrote my first book on JavaScript in 1996. At the time, we had to really dig to fill the book. This was before DHTML, before ECMAScript, before mobile development, and definitely before Node.js. Form validation and popping up alerts were the big things. With this second edition of the JavaScript Cookbook, I had the opposite problem: the world of JavaScript is just too immense to stuff into one book. But I gave it my all. The world of JavaScript is the key to this book. The use of JavaScript has expanded from the browser to the server, to the mobile environment, to the cloud. We’ve gone beyond simple libraries to complex modular systems; from basic animations to rich data visu‐ alizations, with a little audio and video tossed in for fun and giggles. Entire applications are served in one HTML page thanks to sophisticated frameworks, and MEAN is no longer an adjective to apply to nasty folk. Ajax is still around and still relevant, but now it’s joined by direct and immediate bi‐ directional communication—no more having to fake server-client communication, be‐ cause we have it, for real. We can connect to Twitter and Dropbox, create apps for Android devices, and open ePub files directly in the browser for reading. The libraries and modules available in both the client and server take care of so much of the complex, tedious bits, that we can focus on creating what’s unique to our applications. Ten years ago, we’d be surprised at finding a library that met our needs. Now, we’re surprised when we don’t. We have all of this, but we still have JavaScript, the language. We still have String and Number, Array and Function, and the most basic of statements: var someVar = 'Hello, World?'; However, today’s JavaScript is not the same as the language I first wrote about in 1996. It’s growing and expanding, with ECMAScript 5, and now ECMAScript 6, and even the newest additions for ECMAScript 7. It seem as if there’s a new addition to the language every month. What am I saying…there is a new edition every month. ix Just to make it even more interesting and rich is the increasing number of APIs provided by both standards organizations and sevice providers. There’s never been a more exciting time to be a JavaScript developer. But it can also be a little overwhelming, and that’s the focus of this book: getting a handle on this big wonderful world of JavaScript. Book Audience In order to encompass the many subjects and topics reflective of JavaScript in use today, we had to start with one premise: this is not a book for a JavaScript newbie. There are so many good books and tutorials for those new to JavaScript that we felt comfortable setting the bar a little higher than the first edition of the JavaScript Cookbook. If you’ve been playing around with JavaScript for several months, maybe tried your hand with a little Node or Ajax development, you should be comfortable with the book ma‐ terial. Some of the subjects might be challenging, but in a good way. Book Architecture or Why Is This Book Organized in This Way? I originally had this idea of a large graphic diagramming the world of JavaScript, which I would split into fragments, which I would then use to introduce each chapter. It didn’t take long for me to realize that no component of JavaScript exists in isolation from the others. If anything, JavaScript is one big Venn diagram, with dozens of intersections— more of a spirograph than distinct, connected bubbles. So much for grand visualizations. Instead, I split the book into 18 loosely defined chapters, with overlap handled by cross references. The book is split into two parts labeled Classic JavaScript and JavaScript, All Blown Up. The classic parts of JavaScript are the solid foundations of the language we’ve had for the last decade, and aren’t going away. But they aren’t standing still, either. We have our friends String, Number, Boolean, Array, Function, and Object, but thanks to ECMA‐ Script 5 and 6, there’s a lot more we can do with these objects. In addition, before we can get into the more leading-edge, complex uses of JavaScript, we still need to under‐ stand how to use Ajax, work with JSON, create and use libraries, as well as incorporate one of the more popular (jQuery) into our applications. We also need to understand how to work within the browser, which is still the working environment for most Java‐ Script development, as well as test our creations and make sure they’re accessible. Now that video and audio, as well as the Canvas element and SVG, are supported in all modern browsers, a basic understanding of these rich media elements is fundamental. x | The World of JavaScript The All Blown Up part of JavaScript is basically everything else. This includes the new objects introduced in ECMAScript 6, JavaScript in the server (Node), complex frame‐ works (in the server and client), and modular JavaScript. It also includes JavaScript in mobile devices, data visualizatons, graphical tools available in the server, bidirectional client-server communication, and the rich world of available APIs, libraries, and modules. It seems a bewildering mess at times, but the more examples you try in the different environments, the more you realize that JavaScript is the key that makes it all come together. A break down of the chapters follows in the next sections. Part I, Classic JavaScript Part I focuses on traditional uses of JavaScript as they’ve been practiced the last several years, but updated to incorporate new ideas, modifications, and improved functionality: Chapter 1, The JavaScript Not-So-Simple Building Blocks Covering use of some familiar old friends: String, Number, Boolean, RegExp, Math, and Date. The coverage goes beyond the basic, and also touches on some of the new extensions that come to us via ECMAScript 5 and 6. Chapter 2, JavaScript Arrays Probably no component of JavaScript, the language, has changed more than the simple, essential Array. This chapter goes beyond basic Array use, and covers some of the newer functionality. Chapter 3, Functions: The JavaScript Building Blocks The ubiquitous Function—what would we do without it? In JavaScript, very little. This chapter covers some of the more advanced function uses, and introduces more modern functional uses. We’ll look at the three basic function construction types, as well as the extremely useful IIFE (Immediately Invoked Function Expression). Chapter 4, The Malleable JavaScript Object Following closely on the heels of the Array in undergoing change, both in percep‐ tion and use, the JavaScript Object is nothing if not malleable, hence the chapter title. Most of the chapter focuses on this malleability, both the good uses and the not as good. I also briefly touch on the increasing popularity of functional pro‐ gramming versus object-oriented. Chapter 5, JavaScript and Directly Accessing the User Interface You can’t escape the DOM, the DOM knows all (nless it’s Shadow DOM, covered in Chapter 14). It’s a whole lot more fun to work with the DOM nowadays, thanks to new querying capabilitiy. And though most folks use jQuery, it’s still important The World of JavaScript | xi to understand what’s happening beneath the surface of this and other popular libraries. Chapter 6, Preliminary Testing and Accessibility No matter how new JavaScript is, there are still JavaScript best practices to follow, such as keeping our code clean, testing, and ensuring accessibility. We now have new tools to make these necessary tasks a little easier, and a little more entertaining. Chapter 7, Creating and Using JavaScript Libraries Here we’ll look at the basics of library creation, including minification, hosting your library in GitHub or CDN, using external libraries (jQuery and Underscore), and converting your library to a jQuery plug-in. We’ll take jQuery for a spin, but not all libraries do all things—we’ll also take a look at libraries that focus on one single type of task. Once we have the basics of library building under our belt, we can continue with modularizing our code, in Chapter 12. Chapter 8, Simplified Client-Server Communication and Data You can’t play with the new communication techniques (e.g. WebSockets) without a good understanding of Ajax, as well as how to work with JSON and XML. Yes, XML still does exist. Understanding the technology covered in this chapter is nec‐ essary before working with the newer client-server communication covered in Chapter 14. Chapter 9, Creating Media Rich, Interactive Web Effects This chapter provides basic usage techniques for the Canvas element and 2D graphics, SVG, and the audio and video elements. It also touches on combining the media types (integrating SVG and Canvas) and altering videos as they run. Data visualizations, more escoteric graphical tools, and server-side graphics are covered in Chapter 16. Part II, JavaScript, All Blown Up Part II is labeled All Blown Up because a few years ago, JavaScript developers never could have imagined all the things we can do today. Not all of the technology covered is brand new, but most is leading-edge. This part of the book also gets into the more complex uses of JavaScript, such as advanced client-server communication, data visualization, OAuth, and mobile development: Chapter 10, The New ECMAScript Standard Objects There are several new objects introduced with ECMAScript 6. I touch on all of them, or at least, all of them known at the time the book was written. (Yes, JavaScript is changing…a bit too fast to keep up with, at times.) What’s my favorite new addi‐ tions? Function generators and iterators. xii | The World of JavaScript Chapter 11, Node: JavaScript on the Server This chapter provides a faster paced introduction to working with Node.js. You don’t need past experience with Node to work with the examples in this chapter, but the chapter does move along quickly. It also answers those most commonly asked Node questions: How do you keep a Node server running; how do you run Node on the same server as Apache; how do you automatically restart Node when the application changes, or it goes down for some reason. It also explores how to use Node modules, and how to create a stand alone application based on Node. Chapter 12, Modularizing and Managing JavaScript This chapter is all about the new world of JavaScript modularization. It takes the older but still necessary components of code reuse, covered in Chapter 7, and in‐ corporates the many new tools and approaches for creating, distributing, and using modular code. The chapter introduces the different approaches to modularization (AMD and Common JS), creating Node modules, working with RequireJS, Bower, Browserify, and that task master, Grunt. We in JavaScript are nothing if not creative with our application names. Chapter 13, Fun with APIs An API is a programmer contract. It’s an interface into the inner working of the browser, but it’s also a way of accessing services or data from a media server or remote resource. APIs are also used in graphical, data, and mobile applications, covered in Chapters 16 through 18. This chapter introduces three different kinds of APIs: remote APIs built on the principle of REST, new W3C APIs that introduce us to new capabilities in the browser, and remote APIs that act locally. Chapter 14, JavaScript Frameworks Some of the more complex JavaScript components are the frameworks, whether they’re located on the server (ExpressJS), or in the client (AngularJS, Backbone, or Ember). Frameworks also encompass complex strategies for certain problems or business use, like OAuth for authorization, and the very new Web Components. A few asides about this chapter before moving on: I wanted to include a demon‐ stration of at least one of the popular client-side MV* framework tools in the book, but couldn’t decide which one. They do differ enough that covering one results in recipes that aren’t helpful for folks not using that specific framework. That’s when I decided on deconstructing the ToDoMVC web application, and diving into how it’s implemented using three of the more popular framework tools: Angular, Em‐ ber.js, and Backbone. Hopefully, the process I used can be used with the other frameworks. I did go into much more depth with the OAuth framework, because of its increasing use authorizing access for data and services at the many APIs we’re interested in using. OAuth is also used with the Twitter API, covered in Chapter 13, as well as the Dropbox Datastores, covered in Chapter 17. The World of JavaScript | xiii I also covered Web Components, but without reliance on a polyfill (e.g. Google’s Polymer), as I’m wary of replying on roprietary technology when learning about something new. Chapter 15, Advanced Client-Server Communications and Streams Client-server communications is so much better now that WebSockets and CORS (Cross-Origin Resource Sharing) are standard in all modern browsers. Real-time, bidirectional communication can greatly simplify our lives. And because this type of communication is a stream, this chapter also takes a look at Node’s new transform streams. Chapter 16, Data Visualizations and Client/Server Graphics Some of the graphical applications we had fun with when the Canvas element and SVG first received broader support are giving way to more serious data visualiza‐ tions, most of which use data provided directly from the server. This chapter takes a closer look at data visualization tools, including one that partners with a Web‐ Socket server application. Speaking of the server, we now have access to the same rich visualization and graphics tools in the server that we’ve had in the client, and we’ll explore some of the more interesting possibilities. Chapter 17, Data and Persistence Data. The world runs on data. This chapter first checks in with form validation, because a good data system depends on good data. Next, we look at the new data storage mechanisms now available in our browsers, including the more complex IndexedDB. And because JavaScript is now on the server, we take a peek at accessing SQL databases (accessing MongoDB is covered in Chapter 15). Lastly, we’ll explore data in the cloud, by working with Dropbox’s Datastores. Chapter 18, JavaScript Hits the (Mobile) Road The last chapter is about all things mobile. Well, Android and web apps mobile (sorry, no iOS coverage). Thanks to Cordova/PhoneGap, and new mobile APIs, we can now take our mad HTML5, CSS, and JavaScript skills to Android and Kindle Fire tablets and smart phones. How fun is that? Appendix A, Up and Running in jsBin and jsFiddle This appendix introduces you to jsBin and jsFiddle, which are useful for trying out the different examples in the book. Conventions Used in This Book The following typographical conventions are used in this book: Italic Indicates new terms, URLs, email addresses, filenames, and file extensions. xiv | The World of JavaScript Constant width Indicates computer code in a broad sense, including commands, arrays, elements, statements, options, switches, variables, attributes, keys, functions, types, classes, namespaces, methods, modules, properties, parameters, values, objects, events, event handlers, XML tags, HTML tags, macros, the contents of files, and the output from commands. Constant width bold Shows commands or other text that should be typed literally by the user. Constant width italic Shows text that should be replaced with user-supplied values or by values deter‐ mined by context. This icon signifies a tip, suggestion, or general note. This icon indicates a warning or caution. Websites and pages are mentioned in this book to help you locate online information that might be useful. Normally both the address (URL) and the name (title, heading) of a page are mentioned. Some addresses are relatively complicated, but you can probably locate the pages easier using your favorite search engine to find a page by its name, typically by writing it inside quotation marks. This may also help if the page cannot be found by its address; it may have moved elsewhere, but the name may still work. Using Code Examples Supplemental material (code examples, exercises, etc.) is available for download at https://github.com/shelleyp/javascriptcookbook. This book is here to help you get your job done. In general, you may use the code in this book in your programs and documentation. You do not need to contact us for permission unless you’re reproducing a significant portion of the code. For example, writing a program that uses several chunks of code from this book does not require permission. Selling or distributing a CD-ROM of examples from O’Reilly books does require permission. Answering a question by citing this book and quoting example code The World of JavaScript | xv does not require permission. Incorporating a significant amount of example code from this book into your product’s documentation does require permission. We appreciate, but do not require, attribution. An attribution usually includes the title, author, publisher, and ISBN. For example: JavaScript Cookbook, Second Edition, by Shelley Powers. Copyright 2015 Shelley Powers, 978-1-491-90188-5. If you feel your use of code examples falls outside fair use or the permission given here, feel free to contact us at permissions@oreilly.com. Safari® Books Online Safari Books Online is an on-demand digital library that delivers expert content in both book and video form from the world’s leading authors in technology and business. Technology professionals, software developers, web designers, and business and crea‐ tive professionals use Safari Books Online as their primary resource for research, prob‐ lem solving, learning, and certification training. Safari Books Online offers a range of plans and pricing for enterprise, government, education, and individuals. Members have access to thousands of books, training videos, and prepublication manu‐ scripts in one fully searchable database from publishers like O’Reilly Media, Prentice Hall Professional, Addison-Wesley Professional, Microsoft Press, Sams, Que, Peachpit Press, Focal Press, Cisco Press, John Wiley & Sons, Syngress, Morgan Kaufmann, IBM Redbooks, Packt, Adobe Press, FT Press, Apress, Manning, New Riders, McGraw-Hill, Jones & Bartlett, Course Technology, and hundreds more. For more information about Safari Books Online, please visit us online. How to Contact Us Please address comments and questions concerning this book to the publisher: O’Reilly Media, Inc. 1005 Gravenstein Highway North Sebastopol, CA 95472 800-998-9938 (in the United States or Canada) 707-829-0515 (international or local) 707-829-0104 (fax) We have a web page for this book, where we list errata, examples, and any additional information. You can access this page at http://bit.ly/js-cookbook-2e. xvi | The World of JavaScript To comment or ask technical questions about this book, send email to bookques tions@oreilly.com. For more information about our books, courses, conferences, and news, see our website at http://www.oreilly.com. Find us on Facebook: http://facebook.com/oreilly Follow us on Twitter: http://twitter.com/oreillymedia Watch us on YouTube: http://www.youtube.com/oreillymedia Acknowledgments My appreciation to my editors, Simon St. Laurent and Brian McDonald, as well as all of the rest of the O’Reilly production staff. I also want to extend a thank you to my tech reviewers, Dr. Axel Rauschmayer and Semmy Purewal, with the caveat that any errors or gotchas still in the finished work are my responsibility. I also want to thank the many people in the JavaScript community who generously give their time in extending the language, creating the JavaScript implementations, and the other technologies so many of us have come to depend on and appreciate. I also want to include those who write about the technologies online so the rest of us don’t have to stumble around in the dark. The World of JavaScript | xvii PART I Classic JavaScript Classic JavaScript is the JavaScript that’s stable, well used, well known, and still a fun‐ damental component of any and all JavaScript applications. From working with the built-in objects, such as String and Number, to communicating with the server via Ajax and creating media effects with SVG, Canvas, Video, and Audio, the JavaScript in this section has broad support and well defined understanding. CHAPTER 1 The JavaScript Not-So-Simple Building Blocks Most JavaScript functionality comes to us via a very basic set of objects and data types. The functionality associated with strings, numbers, and booleans is provided via the String, Number, and Boolean data types. Other fundamental functionality, including regular expressions, dates, and necessary mathematical operations, are provided via the built-in RegExp, Date, and Math objects, respectively. The fundamental building blocks of JavaScript have changed over time, but their core functionality remains the same. In this chapter, I’m focusing less on the syntax associated with the objects, and more on understanding their place in JavaScript. A good, introductory overview of the JavaScript standard built-in objects can be found in the Mozilla Developer Network JavaScript Reference. 1.1. Differentiating Between a JavaScript Object, Primitive, and Literal Problem People toss around terms like object, primitive, and literal. What is the difference be‐ tween the three, and how can you tell which is which? 3 Solution A JavaScript literal represents a value of a specific type, such as a quoted string (String), floating-point number (Number), or boolean (Boolean): "this is a string" 1.45 true A JavaScript primitive is an instance of a particular data type, and there are five such in the language: String, Number, Boolean, null, and undefined. The following are exam‐ ples of JavaScript primitives: "this is a string" null Of the primitive data types, three have complementary constructor objects: String, Number, and Boolean. These objects provide access to the built-in properties and methods that allow us to do more than simple assignment and subsequent access: var str1 = "this is a string"; console.log(str1.length); // using String object's length property Many of the examples in this book use the console.log() function to display JavaScript results. “The Console Is Your Friend” on page 589 provides a quick how-to on accessing the JavaScript console in modern browers, and Appendix A also provides directions for set‐ ting up your environment and running the code snippets found in the solutions. Discussion It may seem as if we’re working with simple strings or numbers when we declare a variable: var str1 = "this is a simple string"; However, we’re actually creating doorways into an extensive set of functionality. Without reliance on JavaScript objects, we can assign a string, number, or boolean value to a variable and then access it at a later time. However, if we want to do more with the variable, we’ll need to use the data type’s complementary JavaScript object and its properties. As an example, if we want to see the length of a string, we’ll access the String object’s length property: var str1 = "this is a simple string"; console.log(str1.length); // prints out 23 to browser console 4 | Chapter 1: The JavaScript Not-So-Simple Building Blocks Behind the scenes, when the code accesses a String object’s property on the literal, a new String object is created and its value is set to the value of the string contained in the variable. The length property is accessed and printed out, and the newly created String object is discarded. JavaScript engines don’t have to actually create an object to wrap the primitive when you access object properties; they only have to emu‐ late this type behavior. There are exactly five primitive data types in JavaScript: string, number, boolean, null, and undefined. Only the string, number, and boolean data types have complementary constructor objects. The actual representation of strings, floating-point numbers, inte‐ gers, and booleans are literals: var str1 = "this is a simple string"; // the quoted string is the literal var num1 = 1.45; // the value of 1.45 is the literal var answer = true; // the values of true and false are boolean literals We can create primitive boolean, string, and number variables either by using a literal representation or using the object without using the new operator: var str1 = String("this is a simple string"); // primitive string var num1 = Number(1.45); // primitive number var bool1 = Boolean(true); // primitive boolean To deliberately instantiate an object, use the new operator: var str2 = new String("this is a simple string"); // String object instance var num2 = new Number(1.45); // Number object instance var bool2 = new Boolean(true); // primitive boolean You can quickly tell the difference between a primitive and an object instance when you compare an object instance to a literal value using strict equality. For example, running the following code in a browser: var str1 = String("string"); var num1 = Number(1.45); var bool1 = Boolean(true); if (str1 === "string") { console.log('equal'); } 1.1. Differentiating Between a JavaScript Object, Primitive, and Literal | 5 if (num1 === 1.45) { console.log('equal'); } if (bool1 === true) { console.log('equal'); } var str2 = new String("string"); var num2 = new Number(1.45); var bool2 = new Boolean(true); if (str2 === "string") { console.log('equal'); } else { console.log('not equal'); } if (num2 === 1.45) { console.log('equal'); } else { console.log('not equal'); } if (bool2 === true) { console.log('equal'); } else { console.log('not equal'); } Results in the following print outs to the console: equal equal equal not equal not equal not equal The primitive variables (those not created with new) are strictly equal to the literals, while the object instances are not. Why are the primitive variables strictly equal to the literals? Because primitives are compared by value, and values are literals. For the most part, JavaScript developers don’t directly create object instances for the three primitive data types. Developers just want a number, boolean, or string variable to act like a number, boolean, or string, rather than an object; we don’t need the enhanced functionality of the object. More importantly, when developers use strict equality or type checking in the code, they want a variable to match their expectations of data type, rather than be defined as “object”: 6 | Chapter 1: The JavaScript Not-So-Simple Building Blocks var num1 = 1.45; var num2 = new Number(1.45); console.log(typeof num1); // prints out number console.log(typeof num2); // prints out object Code validators, such as JSHint, output a warning if you instantiate a primitive data type object directly for just this reason. See Also Recipe 1.3 has a more detailed look at the strict equality operators, as compared to the standard equality operators. 1.2. Extracting a List from a String Problem You have a string with several sentences, one of which includes a list of items. The list begins with a colon (:) and ends with a period (.), and each item is separated by a comma. You want to extract just the list. Before: This is a list of items: cherries, limes, oranges, apples. After: ['cherries','limes','oranges','apples'] Solution The solution requires two actions: extract out the string containing the list of items, and then convert this string into a list. Use String’s indexOf() to locate the colon, and then use it again to find the first period following the colon. With these two locations, extract the string using String’s sub string(): var sentence = 'This is one sentence. This is a sentence with a list of items:' + 'cherries, oranges, apples, bananas. That was the list of items.'; var start = sentence.indexOf(':'); var end = sentence.indexOf('.', start+1); var listStr = sentence.substring(start+1, end); Once you have the string consisting of the list items, use the String split() to break the string into an array: 1.2. Extracting a List from a String | 7 var fruits = listStr.split(','); console.log(fruits); // ['cherries', ' oranges', ' apples', ' bananas'] Discussion The indexOf() method takes a search value, as first parameter, and an optional begin‐ ning index position, as second. The list is delimited by a beginning colon character and an ending period. The index Of() method is used without the second parameter in the first search, to find the colon. The method is used again but the colon’s position (plus 1) is used to modify the begin‐ ning location of the search for the period: var end = sentence.indexOf('.',start+1); If we didn’t modify the search for the ending period, we’d end up with the location of the first sentence’s period rather than the period for the sentence containing the list. Once we have the beginning and ending location for the list, we’ll use the sub string() method, passing in the two index values representing the beginning and end‐ ing positions of the string: var listStr = sentence.substring(start+1, end); The extracted string is: cherries, oranges, apples, bananas We’ll finish by using split() to split the list into its individual values: var fruits = listStr.split(',') ; // ['cherries', ' oranges', ' apples', ' bananas'] There is another string extraction method, substr(), that begins extraction at an index position marking the start of the substring and passing in the length of the substring as the second parameter. We can easily find the length just by subtracting the beginning position of the string from the end position: var listStr = sentence.substr(start+1, end-start); var fruits = listStr.split(','); See Also Another way to extract the string is to use regular expressions and the RegExp object, covered beginning in Recipe 1.5. 8 | Chapter 1: The JavaScript Not-So-Simple Building Blocks Advanced The result of splitting the extracted string is an array of list items. However, the items come with artifacts (leading spaces) from sentence white space. In most applications, we’ll want to clean up the resulting array elements. We’ll discuss the Array object in more detail in Chapter 2, but for now, we’ll use the Array forEach() method in addition to the String object’s trim() method to clean up the array: fruits = listStr.split(','); console.log(fruits); // [' cherries', ' oranges', ' apples', ' bananas'] fruits.forEach(function(elmnt,indx,arry) { arry[indx] = elmnt.trim(); }); console.log(fruits); // ['cherries', 'oranges', 'apples", "bananas"] The forEach() method applies the function passed as parameter (the callback) to each array element. The callback function supports three arguments: the array element value, and optionally, the array element index and the array itself. Another simpler approach is to pass a regular expression to the split() that trims the result before it’s returned: var fruits = listStr.split(/\s*,\s*/); Now the matching returned value is just the string without the surrounding white space. The forEach() method is also covered in Recipe 2.5. The code in this section mutates the array in place, which means it actually modifies the array as it’s traversed. Another nondestructive approach is to use the newer map() Array method, covered in Recipe 2.7. Extra: Simplifying the Code Using Chaining The example code in this recipe is correct, but a little verbose. We can compress the code by using JavaScript’s method chaining, allowing us to attach one method call to the end of a previous method call if the object and methods allow it. In this case, we can chain the split() method directly to the substring() method: var start = sentence.indexOf(":"); var end = sentence.indexOf(".", start+1); var fruits = sentence.substring(start+1, end).split(","); 1.2. Extracting a List from a String | 9 The code isn’t more accurate, but it uses less space and can be easier to read. I’ll cover method chaining in more detail in Recipe 4.9. 1.3. Checking for an Existing, Nonempty String Problem You want to verify that a variable is defined, is a string, and is not empty. Solution The simplest solution when testing for a nonempty string is the following: if (typeof unknownVariable === 'string' && unknownVariable.length > 0) If the variable isn’t a string, the test will fail, and if the string’s length isn’t longer than zero (0), it will fail. However, if you’re interested in testing for a string, regardless of whether it’s a String object or a string literal, you’ll need a different typeof test, as well as test to ensure the variable isn’t null: if (((typeof unknownVariable != 'undefined' && unknownVariable) && unknownVariable.length() > 0) && typeof unknownVariable.valueOf() == 'string') ... Discussion You can use length to find out how long the string is and test whether the string variable is an empty string (zero length): if (strFromFormElement.length == 0) // testing for empty string However, when you’re working with strings and aren’t sure whether they’re set or not, you can’t just check their length, as you’ll get an undefined JavaScript error if the variable has never been set (or even declared). You have to combine the length test with another test for existence and this brings us to the typeof operator. The JavaScript typeof operator returns the type of a variable. The list of possible re‐ turned values are: • number if the variable is a number • string if the variable is a string • boolean if the variable is a Boolean • function if the variable is a function • object if the variable is null, an array, or another JavaScript object 10 | Chapter 1: The JavaScript Not-So-Simple Building Blocks • undefined if the variable is undefined Combining the test for a string and a test on the string length ensures our app knows if the variable is a non-zero length string or not: if (typeof unknownVariable == 'string' && unknownVariable.length > 0) ... However, if you’re looking for a nonempty string regardless of whether the string is a literal or an object, than things get a little more interesting. A string that’s created using the String constructor: var str = new String('test'); has a typeof value equal to object not string. We need a more sophisticated test. First, we need a way to test whether a variable has been defined and isn’t null. The typeof can be used to ensure the variable isn’t undefined: if (typeof unknownVariable != 'undefined')... But it’s not sufficient, because null variables have a typeof value equal to object. So the defined and not null test is changed to check to see if the variable is defined and isn’t null: if (typeof unknownVariable != 'undefined' && unknownVariable) ... Just listing the variable is sufficient to test whether it’s null or not. We still don’t know, though, if the variable is a nonempty string. We’ll return the length test, which should allow us to test whether the variable is a string, and is not empty: if ((typeof unknownVariable != 'undefined' && unknownVariable) && unknownVariable.length > 0) ... If the variable is a number, the test fails because a number doesn’t have a length. The String object and string literal variables succeed, because both support length. However, an array also succeeds, because the Array object also supports length. To finish the test, turn to a little used method, valueOf(). The valueOf() method is available on all JavaScript objects, and returns the primitive (unwrapped) value of the object. For Number, String, and Boolean, valueOf() returns the primitive value. So if the variable is a String object, valueOf() returns a string literal. If the variable is already a string literal, applying the valueOf() method temporarily wraps it in a String object, which means the valueOf() method will still return a string literal. Our finished test then becomes: if(((typeof unknownVariable != "undefined" && unknownVariable) && (typeof unknownVariable.valueOf() == "string")) && 1.3. Checking for an Existing, Nonempty String | 11 Now, the test functions without throwing an error regardless of the value and type of the unknown variable, and only succeeds with a nonempty string, regardless of whether the string is a string object or literal. Our use of valueOf() is limited. The method is primarily used by the JavaScript engine, itself, to handle conversions in instances when a primitive is expected and the engine is given an object. The process is complex, and normally your application usually won’t have to be this extensive when testing a value. You’ll typically only need to test whether a variable has been set (typeof returns the correct data type), or find the length of a string in order to ensure it’s not an empty string. Extra: Loose and Strict Equality Operators I used loose equality (== and !=) in this section, but I use strict equality (=== and !==) elsewhere in the book. My use of both types of operators is not a typo. Some folks (Douglas Crockford being the most outspoken) consider the loose equality operators (== and !=) to be evil, and discourage their use. The main reason many de‐ velopers eschew loose equality operators is that they test primitive values rather than the variable object, in totality, and the results of the test can be unexpected. For instance, the following code succeeds: var str1 = new String('test'); if (str1 == 'test') { ...} whereas this code fails: var str1 = new String('test'); if (str1 === 'test') { ...} The first code snippet succeeds because the string literal (test) and the primitive value the str1 variable contains are identical. The second code snippet fails the conditional test because the objects being compared are not equivalent, though they both share the same primitive value (test): the str1 variable is a String object, while the compared value is a string literal. While results of this comparison are relatively intuitive, others are less so. In the fol‐ lowing code snippet, a string is compared to a number, and the results may be unexpected: var num = 0; var str = '0'; 12 | Chapter 1: The JavaScript Not-So-Simple Building Blocks console.log(num == str); // true console.log(num === str); // false In the Abstract Equality Comparison Algorithm, when a string is compared to a number, the loose equality comparison is treated as if the following occurs: console.log(num === toNumber(str)); And therein lies the risk of loose equality: not understanding the implicit type conversion. Sometimes, though, the very nature of the loose equality operator can be useful. For instance, the following code snippet demonstrates how the loose equality operator saves us time and code. The test to see if the variable is “bad” succeeds with standard equality regardless of whether the variable is undefined or null, where it wouldn’t succeed if strict equality had been used: var str1; if (str1 == null) { console.log('bad variable'); } Rather than using the first typeof in the solution, I could shorten the test to the following and get the same result: if ((unknownVariable != null && unknownVariable.length > 0) && typeof unknownVariable.valueOf() == 'string') ... Should you always use strict equality except in these rare instances? Just to ensure you don’t get unexpected results? I’m going to buck the industry trend and say “No.” As long as you’re cognizant of how the equality operators work, and your code is such that you’re either only interested in primitive values or you want the object type coercion I just demonstrated, you can use loose equality operators in addition to strict equality. Consider the following scenario: in a function, you’re testing to see if a counter is a certain value (100, for example). You’re expecting the counter, passed into the function, to be a number, but the developer who sent the value to your function passed it as a string. When you perform the test, you are only interested in the value of the variable, not whether it’s a string or number. In this case, strict equality would fail, but not because the value isn’t what you’re testing for, but because the tested value and the function argument are different types. And the failure may be such that the developer using your function thinks that the application generating the value is in error, not that a type conversion hasn’t been made. 1.3. Checking for an Existing, Nonempty String | 13 You don’t care in your function that the variable is a string and not a number. In this case, what you’re implicitly doing is converting the variable to what you expect and then doing the comparison. The following are equivalent: if (counter == 100) ... if (parseInt(counter) === 100) ... If the type is critically important, then a first test should be on the type and a relevant error generated. But this is what I mean by be‐ ing cognizant of your code. In a more realistic scenario, you may be working with a string, and you don’t care if the person who passed the string value to your function used a String constructor to create the string, or used a string literal—all you care about is the primitive value: var str = 'test'; var str2 = new String('test'); doSomething(str); doSomething(str2); ... function doSomething (passedString) { if (passedString == 'test') ... } See Also For more on the equality operators and their differences, as well as a view from the other side on the issue, I recommend JS101: Equality. The Mozilla Developer Network has a lovely, in-depth overview of the comparison operators and how they work in their doc‐ umentation on comparison operators. And do check out the Abstract Equality Com‐ parison Algorithm, directly. 1.4. Inserting Special Characters Problem You want to insert a special character, such as a line feed, into a string. 14 | Chapter 1: The JavaScript Not-So-Simple Building Blocks Solution Use one of the escape sequences in the string. For instance, to include the copyright symbol in a block of text to be added to the page (shown in Figure 1-1), use the escape sequence \u00A9: var resultString = "

This page \u00A9 Shelley Powers

"; // print out to page var blk = document.getElementById("result"); blk.innerHTML = resultString; Figure 1-1. Using an escape sequence to create the copyright symbol Discussion The escape sequences in JavaScript all begin with the backslash character, (\). This char‐ acter signals the application processing the string that what follows is a sequence of characters that need special handling. Table 1-1 lists the other escape sequences. Table 1-1. Escape sequences Sequence Character \' Single quote \" Double quote \\ Backslash \b Backspace \f Form feed \n Newline \r Carriage return \t Horizontal tab \ddd Octal sequence (3 digits: ddd) \xdd Hexadecimal sequence (2 digits: dd) \udddd Unicode sequence (4 hex digits: dddd) 1.4. Inserting Special Characters | 15 The last three escape sequences in Table 1-1 are patterns, where providing different numeric values will result in differing escape sequences. The copyright symbol in the solution is an example of the Unicode sequence pattern. The escape sequences listed in Table 1-1 can also be represented as a Unicode se‐ quence. Unicode is a computing standard for consistent encoding, and a Unicode se‐ quence is a specific pattern for a given character. For instance, the horizontal tab (\t), can also be represented as the Unicode escape sequence, \u0009. Of course, if the user agent disregards the special character, as browsers do with the horizontal tab, the use is moot. One of the most common uses of escape sequences is to include double or single quotes within strings delimited by the same character: var newString = 'You can\'t use single quotes ' + 'in a string surrounded by single quotes.' + 'Oh, wait a sec...yes you can.'; 1.5. Replacing Patterns with New Strings Problem You want to replace all matched substrings with a new substring. Solution Use the String’s replace() method, with a regular expression: var searchString = "Now is the time, this is the tame"; var re = /t\w{2}e/g; var replacement = searchString.replace(re, "place"); console.log(replacement); // Now is the place, this is the place Discussion The solution also makes use of a global search. Using the global flag (g) with the regular expression in combination with the String replace() method will replace all instances of the matched text with the replacement string. If we didn’t use the global flag, only the first match would trigger a replacement. The literal regular expression begins and ends with a slash (/). As an alternative, I could have used the built-in RegExp object: var re = new RegExp('t\\w{2}e',"g"); var replacement = searchString.replace(re,"place"); console.log(p); 16 | Chapter 1: The JavaScript Not-So-Simple Building Blocks The difference is the surrounding slashes aren’t necessary when using RegExp, but the use of the backslash in the pattern has to be escaped. In addition, the global indicator is a second, optional argument to the RegExp constructor. You can use a regular expression literal or a RegExp object instance interchangeably. The primary difference is that the RegExp constructor allows you to create the regular expression dynamically. Extra: Regular Expression Quick Primer Regular expressions are made up of characters that are used alone or in combination with special characters. For instance, the following is a regular expression for a pattern that matches against a string that contains the word technology and the word book, in that order, and separated by one or more whitespace characters: var re = /technology\s+book/; The backslash character (\) serves two purposes: either it’s used with a regular character, to designate that it’s a special character, or it’s used with a special character, such as the plus sign (+), to designate that the character should be treated literally. In this case, the backslash is used with s, which transforms the letter s to a special character designating a whitespace character (space, tab, line feed, or form feed). The \s special character is followed by the plus sign, \s+, which is a signal to match the preceding character (in this example, a whitespace character) one or more times. This regular expression would work with the following: technology book It would also work with the following: technology book It would not work with the following, because there is no white space between the words: technologybook It doesn’t matter how much whitespace is between technology and book, because of the use of \s+. However, using the plus sign does require at least one whitespace character. Table 1-2 shows the most commonly used special characters in JavaScript applications. Table 1-2. Regular expression special characters Character Matches Example ^ Matches beginning of input /^This/ matches This is… $ Matches end of input /end$/ matches This is the end * Matches zero or more times /se*/ matches seeee as well as se ? Matches zero or one time /ap?/ matches apple and and + Matches one or more times /ap+/ matches apple but not and 1.5. Replacing Patterns with New Strings | 17 Character Matches Example {n} Matches exactly n times /ap{2}/ matches apple but not apie {n,} Matches n or more times /ap{2,}/ matches all p’s in apple and appple but not apie {n,m} Matches at least n, at most m times /ap{2,4}/ matches four p’s in apppppple . Any character except newline /a.e/ matches ape and axe […] Any character within brackets /a[px]e/ matches ape and axe but not ale [^…] Any character but those within brackets /a[^px]/ matches ale but not axe or ape \b Matches on word boundary /\bno/ matches the first no in nono \B Matches on nonword boundary /\Bno/ matches the second no in nono \d Digits from 0 to 9 /\d{3}/ matches 123 in Now in 123 \D Any nondigit character /\D{2,4}/ matches Now ' in ‘Now in 123; \w Matches word character (letters, digits, underscores) /\w/ matches j in javascript \W Matches any nonword character (not letters, digits, or underscores) \/W/ matches % in 100% \n Matches a line feed \s A single whitespace character \S A single character that is not whitespace \t A tab (x) Capturing parentheses Remembers the matched characters Regular expressions are powerful but can be tricky. I’m only cover‐ ing them lightly in this book. If you want more in-depth coverage of regular expressions, I recommend the excellent Regular Expressions Cookbook by Jan Goyvaerts and Steven Levithan (O’Reilly). See Also Recipe 1.7 shows variations of using regular expressions with the String replace meth‐ od, including the use of capturing parenthesis. 1.6. Finding and Highlighting All Instances of a Pattern Problem You want to find all instances of a pattern within a string. 18 | Chapter 1: The JavaScript Not-So-Simple Building Blocks Solution Use the RegExp exec method and the global flag (g) in a loop to locate all instances of a pattern, such as any word that begins with t and ends with e, with any number of characters in between: var searchString = "Now is the time and this is the time and that is the time"; var pattern = /t\w*e/g; var matchArray; var str = ""; // check for pattern with regexp exec, if not null, process while((matchArray = pattern.exec(searchString)) != null) { str+="at " + matchArray.index + " we found " + matchArray[0] + "\n"; } console.log(str); The results are: at 7 we found the at 11 we found time at 28 we found the at 32 we found time at 49 we found the at 53 we found time Discussion The RegExp exec() method executes the regular expression, returning null if a match is not found, or an object with information about the match, if found. Included in the returned array is the actual matched value, the index in the string where the match is found, any parenthetical substring matches, and the original string: • index: The index of the located match • input: The original input string • [0]: The matched value • [1],…,[n]+: Parenthesized substring matches, if any The parentheses capture the matched values. Given a regular expression like that in the following code snippet: var re = /a(p+).*(pie)/ig; var result = re.exec("The apples in the apple pie are tart"); console.log(result); console.log(result.index); console.log(result.input); the resulting output is: 1.6. Finding and Highlighting All Instances of a Pattern | 19 ["apples in the apple pie", "pp", "pie"] 4 "The apples in the apple pie are tart" The array results contain the complete matched value at index zero (0), and the rest of the array entries are the parenthetical matches. The index is the index of the match, and the input is just a repeat of the string being matched. In the solution, the index where the match was found is printed out in addition to the matched value. The solution also uses the global flag (g). This triggers the RegExp object to preserve the location of each match, and to begin the search after the previously discovered match. When used in a loop, we can find all instances where the pattern matches the string. In the solution, the following are printed out: at 7 we found the at 11 we found time at 28 we found the at 32 we found time at 49 we found the at 53 we found time Both time and the match the pattern. Let’s look at the nature of global searching in action. In Example 1-1, a web page is created with a textarea and an input text box for accessing both a search string and a pattern. The pattern is used to create a RegExp object, which is then applied against the string. A result string is built, consisting of both the unmatched text and the matched text, except the matched text is surrounded by a span element (with a CSS class used to highlight the text). The resulting string is then inserted into the page, using the innerHTML for a div element. Example 1-1. Using exec and global flag to search and highlight all matches in a text string Searching for strings

20 | Chapter 1: The JavaScript Not-So-Simple Building Blocks Search pattern:

Figure 1-2 shows the application in action on William Wordsworth’s poem, “The Kitten and the Falling Leaves” after a search for the following pattern: lea(f|ves) 1.6. Finding and Highlighting All Instances of a Pattern | 21 The bar (|) is a conditional test, and will match a word based on the value on either side of the bar. So leaf matches, as well as leaves, but not leap. Figure 1-2. Application finding and highlighting all matched strings You can access the last index found through the RegExp’s lastIndex property. The lastIndex property is handy if you want to track both the first and last matches. See Also Recipe 1.5 describes another way to do a standard find-and-replace behavior, and Recipe 1.7 provides a simpler approach to finding and highlighting text in a string. 1.7. Swapping Words in a String Using Capturing Parentheses Problem You want to accept an input string with first and last name, and swap the names so the last name is first. 22 | Chapter 1: The JavaScript Not-So-Simple Building Blocks Solution Use capturing parentheses and a regular expression to find and remember the two names in the string, and reverse them: var name = "Abe Lincoln"; var re = /^(\w+)\s(\w+)$/; var newname = name.replace(re,"$2, $1"); Discussion Capturing parentheses allow us to not only match specific patterns in a string, but to reference the matched substrings at a later time. The matched substrings are referenced numerically, from left to right, as represented by $1 and $2 in the replace() method. In the solution, the regular expression matches two words separated by a space. Cap‐ turing parentheses were used with both words, so the first name is accessible using $1, the last name with $2. The capturing parentheses aren’t the only special characters available with replace(). Table 1-3 shows the other special characters that can be used with regular expressions and String replace(). Table 1-3. String.replace special patterns Pattern Purpose $$ Allows a literal dollar sign ($) in replacement $& Inserts matched substring $` Inserts portion of string before match $' Inserts portion of string after match $n Inserts n th captured parenthetical value when using RegExp The second pattern, which reinserts the matched substring, can be used to provide a simplified version of the Example 1-1 application shown in Recipe 1.6. That example found and provided markup and CSS to highlight the matched substring. It used a loop to find and replace all entries, but in Example 1-2 we’ll use replace() with the matched substring special pattern ($&). Example 1-2. Using String.replace and special pattern to find and highlight text in a string Searching for strings

Search pattern:

This is a simpler alternative, but the result isn’t quite the same: the line feeds aren’t preserved with Example 1-2, but they are with Example 1-1. The captured text can also be accessed via the RegExp object using the exec() method. Let’s return to the Recipe 1.7 solution code, this time using exec(): var name = "Abe Lincoln"; var re = /^(\w+)\s(\w+)$/; var result = re.exec(name); var newname = result[2] + ", " + result[1]; This approach is handy if you want to access the capturing parentheses values, but without having to use them within a string replacement. 24 | Chapter 1: The JavaScript Not-So-Simple Building Blocks 1.8. Replacing HTML Tags with Named Entities Problem You want to paste example markup into a web page, and escape the markup (i.e., have the angle brackets print out rather than have the contents parsed). Solution Use regular expressions to convert angle brackets (<>) into the named entities < and >: var pieceOfHtml = "

This is a paragraph

"; pieceOfHtml = pieceOfHtml.replace(//g,">"); console.log(pieceOfHtml); Discussion It’s not unusual to want to paste samples of markup into another web page. The only way to have the text printed out, as is, without having the browser parse it, is to convert all angle brackets into their equivalent named entities. The process is simple with the use of regular expressions and the String replace method, as demonstrated in the solution. The key is to remember to use the global flag with the regular expression, to match all instances of the angle brackets. Of course, if the regular expression finds the use of > or < in a mathematical or conditional expression, it will replace these, too. 1.9. Converting an ISO 8601 Formatted Date to a Date Object Acceptable Format Problem You need to convert an ISO 8601 formatted date string into values that can be used to create a new Date object instance. Solution Parse the ISO 8601 string into the individual date values, and use it to create a new JavaScript Date object instance: 1.8. Replacing HTML Tags with Named Entities | 25 var dtstr= "2014-3-04T19:35:32Z"; dtstr = dtstr.replace(/\D/g," "); var dtcomps = dtstr.split(" "); // modify month between 1 based ISO 8601 and zero based Date dtcomps[1]--; var convdt = new Date(Date.UTC.apply(null,dtcomps)); console.log(convdt.toString()); // Tue, 04 Mar 2014 19:35:32 GMT Discussion The ISO 8601 is an international standard that defines a representation for both dates and times. It’s not unusual for applications that provide APIs to require ISO 8601 for‐ matting. It’s also not unusual for most dates to and from APIs to be in UTC, rather than local time. The solution shows one variation of ISO 8601 formatting. The following demonstrate some others: • 2009 • 2009-10 • 2009-10-15 • 2009-10-15T19:20 • 2009-10-15T19:20:20 • 2009-10-15T19:20:20.50 The values are year, month, date, then T to represent time, and hours, minutes, seconds, and fractions of sections. The time zone also needs to be indicated. If the date is in UTC, the time zone is represented by the letter Z, as shown in the solution: 2014-3-04T19:35:32Z Otherwise, the time zone is represented as +hh:mm to represent a time zone ahead of UTC, and -hh:mm to represent a time zone behind UTC. If you attempt to create a JavaScript Date with an ISO 8601 formatted string, you’ll get an invalid date error. Instead, you have to convert the string into values that can be used with the JavaScript Date. The simplest way to parse an ISO 8601 formatted string is to use the String split() method. To facilitate using split(), all non-numeric characters are converted to one specific character. In the solution, the non-numeric characters are converted to a space: dtstr = dtstr.replace(/\D/g, " "); 26 | Chapter 1: The JavaScript Not-So-Simple Building Blocks The ISO-formatted string would be converted to: 2014 03 04 19 35 32 ISO months are one-based values of 1 through 12. To use the month value in JavaScript Dates, the month needs to be adjusted by subtracting 1: dtcomps[1]--; Finally, the new Date is created. To maintain the UTC setting, the Date’s UTC() method is used to create the date in universal time, which is then passed to the Date constructor. Rather than listing out each and every single date value, the apply() method is used, with null as the first value, and all of the arguments as an array as the second: var convdt = new Date(Date.UTC.apply(null,dtcomps)); The task gets more challenging when you have to account for the different ISO 8601 formats. Example 1-3 shows a JavaScript application that contains a more complex JavaScript function that converts from ISO 8601 to allowable Date values. The first test in the function ensures that the ISO 8601 format can be converted to a JavaScript Date. This means that, at a minimum, the formatted string must have a month, day, and year. Example 1-3. Converting ISO 8601 formatted dates to JavaScript Dates Converting ISO 8601 date

Datestring in ISO 8601 format:

Another test incorporated into Example 1-3 is whether a time is given. If there aren’t enough array elements to cover a time, then the hours, minutes, and seconds are set to zero when the UTC date is created. There are other issues related to dates not covered in the application. For instance, if the ISO 8601 formatted string isn’t in UTC time, converting it to UTC can require additional code, both to parse the time zone and to adjust the date to incorporate the time zone. Eventually, you won’t need this special processing, because ECMA‐ Script 5 includes support for ISO 8601 dates in methods such as Date parse(). However, implementation is still inconsistent across all ma‐ jor browsers—nonexistent in older browsers—so you’ll need these workarounds, or a shim, for now. See Recipe 1.17 for more on using a shim. 28 | Chapter 1: The JavaScript Not-So-Simple Building Blocks 1.10. Using Function Closures with Timers Problem You want to provide a function with a timer, but you want to add the function directly into the timer method call. Solution Use an anonymous function as first parameter to the setInterval() or setTimeout() method call: intervalId=setInterval( function() { x+=5; var left = x + "px"; document.getElementById("redbox").style.left=left; },100); Discussion Unlike the other material covered in this chapter, JavaScript timers don’t belong to any of the basic built-in objects. Instead, they’re part of the basic Web API (previously known as the Browser Object Model, or BOM). In the browser, they’re properties of the Window object, the browser’s global object, though we don’t need to specify window when ac‐ cessing them. In Node.js, the two timer functions are part of the global object. When you’re creating timers using setTimeout() and setInterval(), you can pass in a function variable as the first parameter: function bing() { alert('Bing!'); } setTimeout(bing, 3000); However, you can also use an anonymous function, as demonstrated in the solution. This approach is more useful, because rather than have to clutter up the global space with a function just to use with the timer, you can embed the function directly. In addition, you can use a variable local to the scope of the enclosing function when you use an anonymous function. Example 1-4 demonstrates an anonymous function within a setInterval() method call. The approach also demonstrates how the use of this function closure allows access to the parent function’s local variables within the timer method. In the example, clicking the red box starts the timer, and the box moves. Clicking the box again clears the timer, 1.10. Using Function Closures with Timers | 29 and the box stops. The position of the box is tracked in the x variable, which is within scope for the timer function, as it operates within the scope of the parent function. Example 1-4. Using an anonymous function within a setInterval timer parameter interval and anonymous function
There’s no guarantee that the timer event fires when it is supposed to fire. Timers run on the same execution thread as all other user interface (UI) events, such as mouse- clicks. All events are queued and blocked, including the timer event, until its turn. So, if you have several events in the queue ahead of the timer, the actual time could differ —probably not enough to be noticeable to your application users, but a delay can happen. 30 | Chapter 1: The JavaScript Not-So-Simple Building Blocks See Also John Resig offers an excellent discussion on how timers work, and especially the issues associated with event queues and single threads of execution. Function closures are covered in more detail in Recipe 3.5. See function closures in timers in action in Recipe 1.11. 1.11. Tracking Elapsed Time Problem You want to track the elapsed time between events. Solution Create a Date object when the first event occurs, a new Date object when the second event occurs, and subtract the first from the second. The difference is in milliseconds; to convert to seconds, divide by 1,000: var firstDate = new Date(); setTimeout(function() { doEvent(firstDate); }, 25000); function doEvent() { var secondDate = new Date(); var diff = secondDate - firstDate; console.log(diff); // approx. 25000 } Discussion Some arithmetic operators can be used with Date, but with interesting results. In the example, one Date instance can be subtracted from another, and the difference between the two is returned as milliseconds. However, if you add two dates together, the result is a string with the second Date instance concatenated to the first: Thu Oct 08 2009 20:20:34 GMT-0500 (CST)Thu Oct 08 2009 20:20:31 GMT-0500 (CST) If you divide the Date instances, the dates are converted to their millisecond value, and the result of dividing one by the other is returned. Multiplying two dates will return a very large millisecond result. 1.11. Tracking Elapsed Time | 31 Only the Date instance subtraction operator really makes sense, but it’s interesting to see what happens with arithmetic operators and the Date object. 1.12. Converting a Decimal to a Hexadecimal Value Problem You have a decimal value, and need to find its hexadecimal equivalent. Solution Use the Number toString() method: var num = 255; // displays ff, which is hexadecimal equivalent for 255 console.log(num.toString(16)); Discussion By default, numbers in JavaScript are base 10, or decimal. However, they can also be converted to a different radix, including hexadecimal (16) and octal (8). Hexadecimal numbers begin with 0x (a zero followed by lowercase x), and octal numbers always begin with zero: var octoNumber = 0255; // equivalent to 173 decimal var hexaNumber = 0xad; // equivalent to 173 decimal A decimal number can be converted to another radix, in a range from 2 to 36: var decNum = 55; var octNum = decNum.toString(8); // value of 67 octal var hexNum = decNum.toString(16); // value of 37 hexadecimal var binNum = decNum.toString(2); // value of 110111 binary To complete the octal and hexadecimal presentation, you’ll need to concatenate the zero to the octal, and the 0x to the hexadecimal value. Although decimals can be converted to any base number (between a range of 2 to 36), only the octal, hexadecimal, and decimal numbers can be manipulated, directly as numbers. In addition, when using JavaScript strict mode, only decimal and hexadecimal literals are supported, as octal integers are no longer supported in JavaScript. 32 | Chapter 1: The JavaScript Not-So-Simple Building Blocks Extra: Speaking of Strict Mode Strict mode is an ECMAScript 5 addition that signals the use of a more restricted version of the JavaScript language. Strict mode can be implemented for an entire script or only for a function. Triggering is simple: 'use strict'; or: "use strict"; This code should be the first line in your script block or function. When strict mode is engaged, a mistake that would normally be ignored now generates an error. What kind of mistake? • Typos in variable names in assignment throw an error. • Assignments that would normally fail quietly now throw an error. • Attempting to delete an undeletable property fails. • Using nonunique property names. • Using nonunique function parameter names. Strict mode also triggers other requirements: • Octals aren’t supported in strict mode. • The eval() statement is limited, and with is not supported. • When constructing a new object, new is required for this to function correctly. Bottom line: strict mode helps eliminate unexpected and unexplainable results. 1.13. Summing All Numbers in a Table Column Problem You want to sum all numbers in a table column. Solution Traverse the table column containing numeric string values, convert to numbers, and sum the numbers: var sum = 0; // use querySelector to find all second table cells var cells = document.querySelectorAll("td:nth-of-type(2)"); 1.13. Summing All Numbers in a Table Column | 33 for (var i = 0; i < cells.length; i++) { sum+=parseFloat(cells[i].firstChild.data); } Discussion The global functions parseInt() and parseFloat() convert strings to numbers, but parseFloat() is more adaptable when it comes to handling numbers in an HTML table. Unless you’re absolutely certain all of the numbers will be integers, parseFloat() can work with both integers and floating-point numbers. As you traverse the HTML table and convert the table entries to numbers, sum the results. Once you have the sum, you can use it in a database update, print it to the page, or pop up a message box, as the solution demonstrates. You can also add a sum row to the HTML table. Example 1-5 demonstrates how to convert and sum up numeric values in an HTML table, and then how to insert a table row with this sum, at the end. The code uses document.querySelectorAll(), which uses a different variation on the CSS selector, td + td, to access the data this time. This selector finds all table cells that are preceded by another table cell. Example 1-5. Converting table values to numbers and summing the results Accessing numbers in table
Washington145
Oregon233
Missouri833
Being able to provide a sum or other operation on table data is helpful if you’re working with dynamic updates via an Ajax operation, such as accessing rows of data from a database. The Ajax operation may not be able to provide summary data, or you may not want to provide summary data until a web page reader chooses to do so. The users may want to manipulate the table results, and then push a button to perform the summing operation. Adding rows to a table is simple, as long as you remember the steps: 1. Create a new table row using document.createElement("tr"). 2. Create each table row cell using document.createElement("td"). 3. Create each table row cell’s data using document.createTextNode(), passing in the text of the node (including numbers, which are automatically converted to a string). 4. Append the text node to the table cell. 5. Append the table cell to the table row. 6. Append the table row to the table. Rinse, repeat. If you perform this operation frequently, you can create functions for these operations, and package them into JavaScript libraries that you can reuse. Also, many of the available JavaScript libraries can do much of this work for you. 1.13. Summing All Numbers in a Table Column | 35 See Also Wonder why I’m not using forEach() with the results of the query? That’s because the querySelectorAll() returns a NodeList, not an array, and forEach() is an Array method. But there is a workaround, covered in Recipe 2.6. Extra: Modularization of Globals The parseFloat() and parseInt() methods are global methods. As part of a growing effort to modularize JavaScript, both methods are now attached to the Number object, as new static methods, in ECMAScript 6: var num = Number.parseInt('123'); The motive is good, but at the time this book was written, only Firefox supported the Number methods. 1.14. Converting Between Degrees and Radians Problem You have an angle in degrees. To use the value in the Math object’s trigonometric func‐ tions, you need to convert the degrees to radians. Solution To convert degrees to radians, multiply the value by (Math.PI / 180): var radians = degrees * (Math.PI / 180); To convert radians to degrees, multiply the value by (180 / Math.PI): var degrees = radians * (180 / Math.PI); Discussion All Math trigonometric methods (sin(), cos(), tin(), asin(), acos(), atan(), and atan2()), take values in radians, and return radians as a result. Yet it’s not unusual for people to provide values in degrees rather than radians, as degrees are the more familiar unit of measure. The functionality covered in the solution provides the conversion between the two units. 36 | Chapter 1: The JavaScript Not-So-Simple Building Blocks 1.15. Find the Radius and Center of a Circle to Fit Within a Page Element Problem Given the width and height of a page element, you need to find the center and radius of the largest circle that fits within that page element. Solution Find the smaller of the width and height; divide this by 2 to find the radius: var circleRadius = Math.min(elementWidth, elementHeight) / 2; Given the page element’s width and height, find the center by dividing both by 2: var x = elementWidth / 2; var y = elementHeight / 2; Discussion Working with graphics requires us to do things such as finding the center of an element, or finding the radius of the largest circle that will fit into a rectangle (or largest rectangle that can fit in a circle). Example 1-6 demonstrates both of the solution calculations, modifying an SVG circle contained within an HTML document so that the circle fits within the div element that surrounds it. Example 1-6. Fitting a SVG circle into a div element Using Math method to fit a circle
Figure 1-3 shows the page once it’s loaded. There are techniques in SVG that can ac‐ complish the same procedure using the SVG element’s viewPort setting, but even with these, at some point in time you’ll need to dust off your basic geometry skills if you want to work with graphics. However, as the example demonstrates, most of the math you’ll need is basic. 38 | Chapter 1: The JavaScript Not-So-Simple Building Blocks Figure 1-3. Page with SVG circle fit into rectangular div element 1.16. Calculating the Length of a Circular Arc Problem Given the radius of a circle, and the angle of an arc in degrees, find the length of the arc. Solution Use Math.PI to convert degrees to radians, and use the result in a formula to find the length of the arc: // angle of arc is 120 degrees, radius of circle is 2 var radians = degrees * (Math.PI / 180); var arclength = radians * radius; // value is 4.18879020478... Discussion The length of a circular arc is found by multiplying the circle’s radius times the angle of the arc, in radians. If the angle is given in degrees, you’ll need to convert the degree to radians first, before multiplying the angle by the radius. 1.16. Calculating the Length of a Circular Arc | 39 See Also Recipe 1.14 covers how to convert between degrees and radians. 1.17. Using ES6 String Extras Without Leaving Users in the Dirt Problem You want to use new ECMAScript 6 features, such as the string extras like starts With() and endsWith(), but you don’t want your applications to break for people using browsers that don’t support this newer functionality. Solution Use an ECMAScript 6 (or ES 6) shim to provide support for the functionality in browsers not currently implementing it. Example 1-7 demonstrates how a shim enables support for the new ES 6 String functionality. Example 1-7. Using a shim to enable ES 6 functionality ES 6 String Discussion JavaScript (or ECMAScript, the more proper name) is advancing much more rapidly now than in the past, but uneven implementation is still an issue. We do live in better times, as the major browser companies are more ready to embrace new features more quickly, and automated browser upgrades help eliminate some of the bogging down we had with a browser such as IE 6. In addition, until we see complete cross-browser sup‐ port for a new feature, we can still make use of enhancements in Node.js applications on the server, and via the use of shims in the client. I’ll cover Node.js in a later chapter, but for now, let’s look at shims, JavaScript compatibility, and what they mean for some‐ thing like the new String object enhancements. The shim used in the example is the ES6-shim created by Paul Mill‐ er. There are other shims and libraries known as polyfills, which you’ll see used elsewhere in this book. The latest formal release of ECMAScript (ES) is ECMAScript 5, and I make use of several ES 5 features throughout the book. Work is underway, though, on the next generation of ES, appropriately named ES.Next (ECMA-262 Edition 6), but commonly referred to as ES 6. As consensus is reached on new ES features, they’re added to the existing draft specifi‐ cation. They’re also listed in ES compatibility tables, such as the ones Mozilla incorpo‐ rates in much of its documentation, and the exceedingly helpful ECMAScript 6 Com‐ patibility Table. Among the ES 6 additions are the following new String.prototype methods: • startsWith: Returns true if string begins with characters from another string • endsWith: Returns true if string ends with characters from another string • contains: Returns true if string contains another string • repeat: Repeats the string a given number of times and returns the result • codePointAt: Returns the Unicode code point (unicode number) that starts at the given index 1.17. Using ES6 String Extras Without Leaving Users in the Dirt | 41 Both startsWith() and endsWith() require a string to examine as first parameter, and an optional integer as second parameter. For startsWith(), the integer marks the po‐ sition in the string to begin the search; for endsWith(), the integer represents the po‐ sition in the string where the search should terminate. The contains() method also takes two parameters—search string and optional starting position for search—but it returns true or false depending on whether it found the search string anywhere in the string: console.log(str.contains("gardens")); // true The repeat() method takes a given string and repeats it however many times is given in the only parameter, returning the result: var str2 = 'abc'; console.log(str2.repeat(2)); // abcabc The codePointAt() method returns the UTF-16 encoded code point value for the character found at the position in the string. In addition, there’s also a new static method, fromCodePoint, which returns a string created by a sequence of code points: var cp = str.codePointAt(50); // 102 for 'f' var cp2 = str.codePointAt(51); // 111 for 'o' var cp3 = str.codePointAt(52); // 114 for 'r' var str2 = String.fromCodePoint(cp,cp2,cp3); console.log(str2); // for At the time of this writing, if I were to access the web page in Example 1-7 without the use of the ES 6 shim, the JavaScript would fail for all but a developer release of Firefox. With the use of the shim, the JavaScript works for all modern browsers. See Also Another alternative to a shim is a transpiler that compiles tomorrow’s code into today’s environment. Google’s version, Traceur, is introduced in Recipe 2.11, and demonstrated more fully in Recipe 10.8. 42 | Chapter 1: The JavaScript Not-So-Simple Building Blocks CHAPTER 2 JavaScript Arrays There is no array data type in JavaScript. Instead, support for arrays is managed through the JavaScript Array object. Array object support has changed considerably over the years—going from simple array access and assignment to sophisticated functionality allowing us to search and sort arrays, as well as manipulate the array elements using more efficient techniques. This chapter focuses on how to best utilize these more modern Array additions. Most modern browsers support the solutions presented in this chap‐ ter. To support folks not using modern browsers, there are several shims you can use to ensure applications work for most. Recipe 1.17 described some of the shims, and demonstrated how you can use them. 2.1. Searching Through an Array Problem You want to search an array for a specific value and get the array element index if found. Solution Use the Array methods indexOf() and lastIndexOf(): var animals = new Array("dog","cat","seal","elephant","walrus","lion"); console.log(animals.indexOf("elephant")); // prints 3 43 Discussion Though support for both indexOf() and lastIndexOf() has existed in browsers for some time, their use wasn’t standardized until the release of ECMAScript 5. Both meth‐ ods take a search value that is then compared to every element in the array. If the value is found, both return an index representing the array element. If the value is not found, –1 is returned. The indexOf() method returns the first one found, the lastIndex Of() returns the last one found: var animals = new Array("dog","cat","seal","walrus","lion", "cat"); console.log(animals.indexOf("cat")); // prints 1 console.log(animals.lastIndexOf("cat")); // prints 5 Both methods can take a starting index, setting where the search is going to start: var animals = ["dog","cat","seal","walrus","lion", "cat"]; console.log(animals.indexOf("cat",2)); // prints 5 console.log(animals.lastIndexOf("cat",4)); // prints 1 If your interest goes beyond just finding an exact match, you can use the ECMAScript 6 (ES 6) Array method findIndex(), providing a function that tests each array value, returning the index of the array element when the test is successful. An example use of findIndex() is the following, using the new method to find an array element whose value equals or exceeds 100: var nums = [2, 4, 19, 15, 183, 6, 7, 1, 1]; var over = nums.findIndex(function(element) { return (element >= 100); }); console.log(nums[over]); A comparable ES 6 Array method is find(), which does the same process but returns the value of the element that successfully passes the given test. Both methods take a callback function, and an optional second argument to act as this in the function. The callback function has three arguments, the array element, index, and array, itself, but only the first is required. Neither method mutates the original array. See Also See Recipe 3.5 for more on callback functions. 44 | Chapter 2: JavaScript Arrays Extra: Array Literal or Array Object Instance? The solutions demonstrated in this recipe show two different ways of creating an array: creating an instance of an Array object, or assigning an array literal. So, which is better? As with most of JavaScript, the answer is, it depends. An array literal is simpler to create and takes less space in the file. Some JavaScript developers would say it looks more elegant, too. When you use the Array object con‐ structor, the JavaScript engine has to spend more time understanding exactly what it is that you want to do: you’re creating an object, OK, what kind of object, and so on. However, if you use an array literal in a function, JavaScript instantiates the array each time the function is called. And, if you need to create a specifically sized array, you should use the Array contructor. Tools such as JSLint will complain if you use the Array constructor, as will as most JavaScript developers. Based on this, I use array literals, but its use isn’t inherently wrong. jsPerf is a site that allows you to quickly create JavaScript test cases and run the tests. You can then share your test case and results with the world. I cover it in more detail in Recipe 6.4. 2.2. Flattening a Two-Dimensional Array with concat() and apply() Problem You want to flatten a two-dimensional array. Solution Use the Array object concat() method to merge the multidimensional array into a single-dimensional array: var fruitarray = []; fruitarray[0] = ['strawberry','orange']; fruitarray[1] = ['lime','peach','banana']; fruitarray[2] = ['tangerine','apricot']; fruitarray[3] = ['raspberry','kiwi']; // flatten array var newArray = fruitarray.concat.apply([],fruitarray); console.log(newArray[5]); // tangerine 2.2. Flattening a Two-Dimensional Array with concat() and apply() | 45 Discussion The Array object concat() method takes one or more arrays and appends the array elements on to the end of the contents of the parent array on which the method was called. The merged array is then returned as a new array. One use for this type of func‐ tionality is to return a single-dimensional array made up of elements from a multidi‐ mensional array, as shown in the solution. I could have flattened the array using the following: var newArray = fruitarray[0].concat(fruitarray[1],fruitarray[2],fruitarray[3]); But if the array has several members, this approach is tedious and error prone. I could also have used a loop or recursion, but these approaches can be equally tedious. Instead, I used the apply() method. This method allows us to apply the function being called (concat) given an array of arguments. In this case, the array of arguments is the original multidimensional array. In order for this to work, an empty array is passed as the first parameter to apply(), because concat() works by concatenating the arrays onto an existing array. I can’t use the first element of the array, because its values will be repeated in the final result. 2.3. Removing or Replacing Array Elements Problem You want to find occurrences of a given value in an array, and either remove the element or replace with another value. Solution Use the Array indexOf() and splice() to find and remove/replace array elements: var animals = new Array("dog","cat","seal","walrus","lion", "cat"); // remove the element from array animals.splice(animals.indexOf("walrus"),1); // dog,cat,seal,lion,cat // splice in new element animals.splice(animals.lastIndexOf("cat"),1,"monkey"); // dog,cat,seal,lion,monkey console.log(animals.toString()); Discussion The splice() method takes three parameters: the first parameter is required, as it’s the index where the splicing is to take place; the second, optional parameter is the number 46 | Chapter 2: JavaScript Arrays of elements to remove; the third parameter, also optional, is a set of the replacement elements (if any). If the index is negative, the elements will be spliced from the end rather than the beginning of the array: var animals = ["cat","walrus","lion", "cat"]; // splice in new element animals.splice(-1,1,"monkey"); // cat,walrus,lion,monkey If the number of elements to splice is not provided, all elements from the index to the end will be removed: var animals = ["cat","walrus","lion", "cat"]; // remove all elements after second animals.splice(2); // cat,walrus The last parameter, the replaced value, can be a set of replacement elements, separated by commas: var animals = ["cat","walrus","lion", "cat"]; // replace second element with two animals.splice(2,1,"zebra","elephant"); // cat,walrus,zebra,elephant,cat Removing or replacing one element is handy, but being able to remove or replace all instances of a particular element is even handier. In Example 2-1, an array is created with several elements, including multiple instances of a specific value. The splice() method is used in a loop to replace all of the elements with a given value. The splice() method is used again, in a separate loop, to remove the newly spliced elements. Example 2-1. Using looping and splice to replace and remove elements var charSets = ["ab","bb","cd","ab","cc","ab","dd","ab"]; // replace element while (charSets.indexOf("ab") != -1) { charSets.splice(charSets.indexOf("ab"),1,"**"); } // ["**", "bb", "cd", "**", "cc", "**", "dd", "**"] console.log(charSets); // delete new element while(charSets.indexOf("**") != -1) { charSets.splice(charSets.indexOf("**"),1); } console.log(charSets); // ["bb", "cd", "cc", "dd"] 2.3. Removing or Replacing Array Elements | 47 2.4. Extracting a Portion of an Array Problem You want to extract out a portion of an array but keep the original array intact. Solution The Array slice() method extracts a shallow copy of a portion of an existing array: var animals = ['elephant','tiger','lion','zebra','cat','dog','rabbit','goose']; var domestic = animals.slice(4,7); console.log(domestic); // ['cat','dog','rabbit']; Discussion The slice() makes a copy of a portion of an existing array, returning a new array. It makes a shallow copy, which means that if the array elements are objects, both arrays point to the same object—modifications to the object in the new array is reflected in the same object in the old array. In the following, slice() is used on an array of array elements to extract out of the arrays. The contents are modified and both arrays are printed out. The changes to the new array are reflected in the old: var mArray = []; mArray[0] = ['apple','pear']; mArray[1] = ['strawberry','lemon']; mArray[2] = ['lime','peach','berry']; var nArray = mArray.slice(1,2); console.log(mArray[1]); // ['strawberry','lemon'] nArray[0][0] = 'raspberry'; console.log(nArray[0]); // ['raspberry','lemon'] console.log(mArray[1]); // ['raspberry','lemon'] The values are copied by reference. If the array element is a primitive data type, such as a string or number, the elements are copied by value—changes to the new array won’t be reflected in the old. 2.5. Applying a Function Against Each Array Element Problem You want to use a function to check an array value, and replace it if it matches a given criterion. 48 | Chapter 2: JavaScript Arrays Solution Use the Array method forEach() to apply a callback function to each array element: var charSets = ["ab","bb","cd","ab","cc","ab","dd","ab"]; function replaceElement(element,index,array) { if (element == "ab") array[index] = "**"; } // apply function to each array element charSets.forEach(replaceElement); console.log(charSets); // ["**", "bb", "cd", "**", "cc", "**", "dd", "**"] Discussion In Example 2-1, we used a while loop to traverse an array and replace a value. Most of the tedious bits are eliminated by using the forEach() method. The forEach() method takes one argument, the callback function. The function itself has three parameters: the array element, an index of the element, and the array. All three were used in the function, replaceElement, though only the first argument is required. In the function, the value is tested to see if it matches a given string, ab. If matched, the array element’s index is used to modify the array element’s value with the replacement string, **. Don’t return a value from the function passed to the forEach() method, as the value will be discarded. See Also The solution mutates the array in place. If you wish to make a copy of the modified array rather than replacing the original, use map(), which is covered in Recipe 2.7. Extra: About That Conditional Statement The solution used a single line for the conditional statement, sans braces: if (element == "ab") array[index] = "**"; Using this single line syntax without braces can be frowned upon because it doesn’t account for the possibility of future additions to the conditional block: if (element == "ab") { array[index] = "**"; 2.5. Applying a Function Against Each Array Element | 49 //some other line of code } However, we should be aware of what we’re doing with our code, enough so that we’ll know if there’s a strong possibility of additional code within the conditional block. I don’t see any harm in using a single line when warranted, such as when the conditional stands alone and isn’t part of an if-then-else construct. Of course, we can be even more cryptic and use the following: (element == "ab") && (array[index] = "**"); If the element has a value of ab, the assignment in the second set of parenthesis is performed. This syntax not only works, it can be faster than the original approach. However, those not familiar with this cryptic conditional could have problems under‐ standing what’s happening, as the approach is less readable than using the traditional if statement. 2.6. Traversing the Results from querySelectorAll() with forEach() and call() Problem You want to use forEach() on the nodeList returned from a call to querySelector All(). Solution You can coerce forEach() into working with a NodeList (the collection returned by querySelectorAll()) using the following: // use querySelector to find all second table cells var cells = document.querySelectorAll("td + td"); [].forEach.call(cells,function(cell) { sum+=parseFloat(cell.firstChild.data); }); Discussion The forEach() is an Array method, and the results of querySelectorAll() is a NodeList, which is a different type of object than an Array. In the solution, to get forEach() to work with the NodeList, we’re calling the method on an empty array, and then using call() on the object to emulate the effect of an Array method on the NodeList, as if it were an actual array. 50 | Chapter 2: JavaScript Arrays Simple, but there are drawbacks. Unless you convert the NodeList into an Array, the coercion is a one-shot that has to be repeated if you need to use the same functionality again. In addition, later in the code you may automatically try another Array method on the NodeList, only to have it fail because you didn’t use coercion. See Also This approach is also used to convert both NodeLists and function arguments into an array, as detailed in Recipe 3.6. 2.7. Applying a Function to Every Element in an Array and Returning a New Array Problem You want to convert an array of decimal numbers into a new array with their hexadec‐ imal equivalents. Solution Use the Array map() method to create a new array consisting of elements from the old array that have been modified via a callback function passed to the method: var decArray = [23, 255, 122, 5, 16, 99]; var hexArray = decArray.map(function(element) { return element.toString(16); }); console.log(hexArray); // ["17", "ff", "7a", "5", "10", "63"] Discussion Like the forEach() method in Recipe 2.5, the map() method applies a callback function to each array element. Unlike forEach(), though, the map() method results in a new array rather than modifying the original array. You don’t return a value when using forEach(), but you must return a value when using map(). The function that’s passed to the map() method has three parameters: the current array element, and, optionally, the array index and array. Only the first is required. 2.7. Applying a Function to Every Element in an Array and Returning a New Array | 51 2.8. Creating a Filtered Array Problem You want to filter element values in an array and assign the results to a new array. Solution Use the Array filter() method: var charSet = ["**","bb","cd","**","cc","**","dd","**"]; var newArray = charSet.filter(function(element) { return (element !== "**"); }); console.log(newArray); // ["bb", "cd", "cc", "dd"] Discussion The filter() method is another ECMAScript 5 addition, like forEach() and map() (covered in Recipe 2.5 and Recipe 2.7, respectively). Like them, the method is a way of applying a callback function to every array element. The function passed as a parameter to the filter() method returns either true or false based on some test against the array elements. This returned value determines if the array element is added to a new array: it’s added if the function returns true; other‐ wise, it’s not added. In the solution, the character string (**) is filtered from the original array when the new array is created. The function has three parameters: the array element, and, optionally, the index for the element and the original array. 2.9. Validating Array Contents Problem You want to ensure that array contents meet certain criteria. Solution Use the Array every() method to check that every element passes a given criterion. For instance, the following code checks to ensure that every element in the array consists of alphabetical characters: // testing function function testValue (element,index,array) { 52 | Chapter 2: JavaScript Arrays var textExp = /^[a-zA-Z]+$/; return textExp.test(element); } var elemSet = ["**",123,"aaa","abc","-",46,"AAA"]; // run test var result = elemSet.every(testValue); console.log(result); // false var elemSet2 = ["elephant","lion","cat","dog"]; result = elemSet2.every(testValue); console.log(result); // true Or use the Array some() method to ensure that one or more of the elements pass the criteria. As an example, the following code checks to ensure that at least some of the array elements are alphabetical strings: var elemSet = new Array("**",123,"aaa","abc","-",46,"AAA"); // testing function function testValue (element) { var textExp = /^[a-zA-Z]+$/; return textExp.test(element); } // run test var result = elemSet.some(testValue); console.log(result); // true Discussion Unlike the Array methods I covered earlier in the chapter, every() and some() functions do not work against all array elements: they only process as many array elements as necessary to fulfill their functionality. The solution demonstrates that the same callback function can be used for both the every() and some() methods. The difference is that when using every(), as soon as the function returns a false value, the processing is finished, and the method returns false. The some() method continues to test against every array element until the call‐ back function returns true. At that time, no other elements are validated, and the method returns true. However, if the callback function tests against all elements, and doesn’t return true at any point, some() returns false. 2.9. Validating Array Contents | 53 Which method to use depends on your needs. If all array elements must meet certain criteria, then use every(); otherwise, use some(). The callback function takes three parameters: the element, and an optional element index and array. Extra: Using Anonymous Functions in Array Methods In Recipe 2.8, I used an anonymous function, but in this solution, I use a named function. When to use which depends on the context and your own preferences. The advantage to a named function is it shows up in a stack trace when you’re debugging your code, while an anonymous function doesn’t. This isn’t an issue when you’re dealing with simple, targeted functionality, as demonstrated in all of these solutions. However, it can very much be an issue in functionality that’s deeply nested, or is asynchronous. Another advantage to the named function is you can use it in multiple places. Again, though, it doesn’t make sense to reuse a function targeted to a specific Array method callback for any reason other than that specific callback. And the disadvantage is that you are cluttering up the global space when you use a named function. A final possible advantage is that named functions perform better in all browsers than anonymous functions in the context of an Array method callback. Now, this might be the best reason for using a named function. 2.10. Using an Associative Array to Store Form Element Names and Values Problem You want to store form element names and values for later validation purposes. Solution Use an associative array to store the elements, using the element identifiers as array index: var elemArray = new Object(); // notice Object, not Array var elem = document.forms[0].elements[0]; elemArray[elem.id] = elem.value; Iterate over the array using a combination of keys() and forEach(): Object.keys(elemArray).forEach(function (key) { var value = elemArray[key]; console.log(value); }); 54 | Chapter 2: JavaScript Arrays Discussion Typically, JavaScript arrays use a numeric index: arr[0] = value; However, you can create an associative array in JavaScript, where the array index can be a string representing a keyword, mapping that string to a given value. In the solution, the array index is the identifier given the array element, and the actual array value is the form element value. You can create an associative array, but you’re not using the Array object to do so. Using the Array object is risky and actively discouraged—especially if you’re using one of the built-in libraries that use the prototype attribute for extending objects, as people dis‐ covered when the popular Prototype.js library was first released several years ago. When we use an Array to create an associative array, what we’re really doing is adding new properties to the Array, rather than adding new array elements. You could actually create an associative array with a RegExp or String, as well as an Array. All are JavaScript objects, which are associative arrays. When you’re adding a new array, element: obj[propName] = "somevalue"; what you’re really doing is adding a new object property: obj.propName = "somevalue"; Additionally, when you use an Array to create an associative array, you can no longer access the array elements by numeric index, and the length property returns zero. Instead of using an Array to create the associative array, use the JavaScript Object di‐ rectly. You get the exact same functionality, but avoid the clashes with libraries that extend the base Array object using prototype. Example 2-2 shows a web page with a form. When the form is submitted, all of the form elements of type text are accessed and stored in an associative array. The element IDs are used as the array keyword, and the values assigned to the array elements. Once collected, the associative array is passed to another function that could be used to val‐ idate the values, but in this case just creates a string of keyword/value pairs that are displayed. Example 2-2. Demonstrating associative array with form elements Associative Array

2.10. Using an Associative Array to Store Form Element Names and Values | 55


In the example, notice that the array index is formed by the form element’s id. The Object keys() returns an array of the object’s enumerable properties, and forEach() traverses the array. Figure 2-1 shows the example after values are typed into the form fields and the form is submitted. 56 | Chapter 2: JavaScript Arrays Figure 2-1. Demonstration of associative array and traversing form elements See Also For additional information on the risks associated with associative arrays in JavaScript, read JavaScript “Associative Arrays” Considered Harmful. Extra: The dict Pattern Dr. Axel Rauschmayer discusses a pattern called the dict pattern. Rather than creating a standard object, create one that has a null prototype to avoid inheriting existing prop‐ erties that can muck up the applications. The difference between using a standard object and using one with a null prototype is demonstrated in the following code snippet: var newMap = {}; var key = 'toString'; console.log(key in newMap); console.log(newMap[key]); var secondMap = Object.create(null); console.log(key in secondMap); secondMap[key] = 'something diff'; console.log(key in secondMap); console.log(secondMap[key]); 2.10. Using an Associative Array to Store Form Element Names and Values | 57 Here’s the result of the code (run in jsBin): true function toString() { [native code] } false true "something diff" The toString() method is, of course, a standard Object method. So when we test to see if it exists in a regularly created object, it does. However, if we create the null pro‐ totype object, toString() doesn’t exist. Not until we add it as a key. 2.11. Using a Destructuring Assignment to Simplify Code Problem You want to assign array element values to several variables, but you really don’t want to have assign each, individually. Solution Use ECMAScript 6’s destructuring assignment to simplify array assignment: var stateValues = [459, 144, 96, 34, 0, 14]; var [Arizona, Missouri, Idaho, Nebraska, Texas, Minnesota] = stateValues; console.log(Missouri); // 144 Discussion In the solution, variables are declared and instantiated with values in an Array, starting with the Array index at position zero. If the number of variables is less than the array elements, they’re assigned the element values up until all variables have been assigned. If there are more variables than array elements, the unmatched variables are created, but they’re set to undefined. The destructuring assignment is less a new feature in ECMAScript 6, and more an en‐ hancement of existing functionality. It’s not necessary functionality, as it’s not providing access to functionality we’ve not had and can’t emulate. It’s an improvement in the language that will, according to the specification, “Allow for destructuring of arrays and objects using syntax that mirrors array and object initialisers.” Unfortunately, implementation of the functionality is extremely limited, and there is no shim that I know of that can emulate it. However, things change quickly in the world of JavaScript, and it’s good to be aware of what’s coming down the road. 58 | Chapter 2: JavaScript Arrays Extra: Harmony Flags and Using Traceur V8 JavaScript Engine developers have signaled future implementation of destructuring assignment, which means the capability will also be available in Node.js, as well as browsers like Chrome and Opera (in addition to existing support in Firefox). Enabling the newest, latest ECMAScript features is typically off by default in most browsers and environments. However, you can signal that you want to enable support for newer features in some of the environments. In Chrome and Opera, you can enable ES 6 feature support by accessing the URL chrome://flags. This opens up the flags page, where you can search for a flag labeled Experimental JavaScript and enable it. Note that this can add to the instability of your JavaScript applications. In addition to enabling ES 6 support in browsers and Node, Google created Traceur, an application that takes ECMAScript.next code and transpiles (translates and compiles) it into ECMAScript.current code, which should be executable in all modern environ‐ ments. An example of using Traceur is the following, the results of using the application on the solution code: $traceurRuntime.ModuleStore.getAnonymousModule(function() { "use strict"; var stateValues = [459, 144, 96, 34, 0, 14]; var $__3 = stateValues, Arizona = $__3[0], Missouri = $__3[1], Idaho = $__3[2], Nebraska = $__3[3], Texas = $__3[4], Minnesota = $__3[5]; console.log(Missouri); return {}; }); The Traceur results were derived using Google’s Traceur REPL (Read- Eval-Print) application. Just copy the code into the given space on the left side of the application, and it produces the translated and com‐ piled results on the right. Traceur can be accessed via the Traceur GitHub page. It’s also an option in jsBin (see Appendix A for more on working with jsBin). There are also polyfills and shims to enable ES 6 support. Addy Osmani’s page Tracking ECMAScript 6 Support tracks ES 6 support tools and technology. 2.11. Using a Destructuring Assignment to Simplify Code | 59 See Also Recipe 10.8 contains a detailed description on how to use Traceur with your browser- based applications. A more detailed discussion on support for ECMAScript 6 (Harmony) features in different browsers can be found in Recipe 10.1. 60 | Chapter 2: JavaScript Arrays CHAPTER 3 Functions: The JavaScript Building Blocks JavaScript functions provide a way to encapsulate a block of code in order to reuse the code several times. They are first-class objects in JavaScript, which means they can be treated as an object, as well as an expression or statement. There are three basic ways to create a function: Declarative function A declarative function is a statement triggered by the function keyword; declarative functions are parsed when the JavaScript application is first loaded. Anonymous function or function constructor An anonymous function is constructed using the new operator and referencing the Function object. It’s anonymous because it isn’t given a name, and access to the function occurs through a variable or another object property. Unlike the declara‐ tive function, an anonymous function is parsed each time it’s accessed. Function literal or function expression A literal function is a function expression, including parameter and body, which is used in place—such as a callback function in an argument to another function. Similar to the declarative function, function literals are also parsed only once, when the JavaScript application is loaded. The function literal can also be anonymous. 3.1. Placing Your Function and Hoisting Problem You’re not sure where to place your function to ensure it’s accessible when needed. 61 Solution If you’re using a declarative function, you can place the function anywhere in the code. However, if you’re using a function expression, you must do so before the function is used. The reasons are given in the discussion. Discussion An important concept to remember when considering the different ways you can create a function is hoisting. In JavaScript, all variable declarations are moved, or hoisted to the top of their current scope. That’s declaration, though, not instantiation. The differ‐ ence is critical. If you access a variable that’s not declared, you’ll get a reference error. However, if you access a variable before it’s declared, but it is declared within the scope at a later time, you’ll get an undefined: console.log(a); // undefined var a; If you don’t assign a value to the variable when you declare it, you’ll still get an undefined when you access the variable: console.log(a); // undefined var a = 1; This is because the variable declaration is hoisted, not the assignment, which happens in place. What does this have to do with functions? Quite a bit. If you create a declarative function, hoisting will ensure the function definition is moved to the top of the current scope before it’s accessed: console.log(test()); // 'hello' function test() { return 'hello'; } However, if you use a functional expression, you’re basically toast: console.log(test()); var test = function() { return 'hello'; }; A reference error results because the variable test may be declared, but it’s not instan‐ tiated, and your code is trying to treat this noninstantiated variable as a function. 62 | Chapter 3: Functions: The JavaScript Building Blocks 3.2. Passing a Function As an Argument to Another Function Problem You want to pass a function as an argument to another function. Solution For the following function: function otherFunction(x,y,z) { x(y,z); } Use a function expression (literal function) as argument: var param = function(arg1, arg2) { alert(arg1 + " " + arg2); }; otherFunction(param, "Hello", "World"); or: otherFunction(function(arg1,arg2) { alert(arg1 + ' ' + arg2); }, "Hello","World"); Discussion The function keyword is an operator as well as a statement, and can be used to create a function as an expression. Functions created this way are called function expressions, function literals, and anonymous functions. A function name can be provided with literal functions, but it’s only accessible within the function: var param = function inner() { return typeof inner; } console.log(param()); // "function" The main benefit with naming the function expression is when you’re debugging your JavaScript, the named function appears by its name in a stack trace, rather than just an anonymous function. You can pass a function as an argument to another function as a named variable, or even directly within the argument list, as shown in the solution. Function literals are parsed when the page is loaded, rather than each time they’re accessed. 3.2. Passing a Function As an Argument to Another Function | 63 Extra: Functional Programming and JavaScript A function that accepts another function as a parameter, or returns a function, or both, is known as a higher order function. The concept is from a programming paradigm known as functional programming. Functional programming is a way of abstracting out the complexity of an application, replacing complicated loops and conditional state‐ ments with nice, clean function calls. Our more traditional JavaScript development approach using condi‐ tionals, loops, and associated procedures is called imperative programming. As an example of functional programming, consider something simple: sum all the numbers in an array. One way of doing so is to create a variable to hold the sum, iterate through the array using a for loop, and adding the value of each array element to the sum variable: var nums = [1, 45, 2, 16, 9, 12]; var sum = 0; for (var i = 0; i < nums.length; i++) { sum+=nums[i]; } console.log(sum); // 85 Now examine how the function Array.reduce(), one of the ECMAScript 5 additions to JavaScript, performs the same functionality: var nums = [1, 45, 2, 16, 9, 12]; var sum = nums.reduce(function(n1,n2) { return n1 + n2; }); console.log(sum); // 85 The results are the same, but the difference is less verbose code, and a clearer under‐ standing of what’s happening to drive the result: the array elements are traversed, some operation is performed until the end result is reduced to a single value—in this case, a sum of all array element values. The real benefit to functional programming, however, is more reliable code. When you break your application down into functional bits, and each function performs a single task, and the only data available to that function comes in via function arguments, the only output from the operation is that returned by the function, you reduce unintended side effects in the code. 64 | Chapter 3: Functions: The JavaScript Building Blocks A function that always delivers the same result given the same argu‐ ment values, and that never modifies any external data or triggers any side effects is known as a pure function. In other words, no matter what happens elsewhere in the application—in any library, with any global variable—the effects of applying a function such as Array.reduce() will always be consistent. See Also See Recipe 3.3 for a demonstration of using a named function literal in recursion. See Recipe 3.4 for a demonstration of using a callback function to prevent blocking. 3.3. Implementing a Recursive Algorithm Problem You want to implement a function that will recursively traverse an array and return a string of the array element values, in reverse order. Solution Use a function literal recursively until the end goal is met: var reverseArray = function(x,indx,str) { return indx == 0 ? str : reverseArray(x,--indx,(str+= " " + x[indx])); } var arr = ['apple','orange','peach','lime']; var str = reverseArray(arr,arr.length,""); console.log(str); var arr2 = ['car','boat','sun','computer']; str = reverseArray(arr2,arr2.length,""); console.log(str); Discussion Before looking at the solution, I want to cover the concept of recursion first, and then look at functional recursion. 3.3. Implementing a Recursive Algorithm | 65 Recursion is a well-known concept in the field of mathematics, as well as computer science. An example of recursion in mathematics is the Fibonacci Sequence: f(n)= f(n-1) + f(n-2), for n= 2,3,4,...,n and f(0) = 0 and f(1) = 1 A Fibonacci number is the sum of the two previous Fibonacci numbers. Another example of mathematical recursion is a factorial, usually denoted with an ex‐ clamation point (4!). A factorial is the product of all integers from 1 to a given number n. If n is 4, then the factorial (4!) would be: 4! = 4 x 3 x 2 x 1 = 24 These recursions can be coded in JavaScript using a series of loops and conditions, but they can also be coded using functional recursion. A common example of JavaScript recursion is the solution for a Fibonacci: var fibonacci = function (n) { return n < 2 ? n : fibonacci(n - 1) + fibonacci(n - 2); } or a factorial: function factorial(n) { return n == 1 ? 1 : n * Factorial(n -1); } In the Fibonacci example, n is tested to see if it is less than 2. If it is, it’s returned; otherwise the Fibonacci function is called again with (n – 1) and with (n – 2), and the sum of both is returned. Neither function works with negative values. Negative numbers are not extensible to factorial, and the function given for the Fibonacci is not adjusted for negative values. A little convoluted? The second example with the factorial might be clearer. In this example, when the function is first called, the value passed as argument is compared to the number 1. If n is less than or equal to 1, the function terminates, returning 1. However, if n is greater than 1, what’s returned is the value of n times a call to the factorial function again, this time passing in a value of n – 1. The value of n, then, decreases with each iteration of the function, until the terminating condition (or base) is reached. 66 | Chapter 3: Functions: The JavaScript Building Blocks What happens is that the interim values of the function call are pushed onto a stack in memory and kept until the termination condition is met. Then the values are popped from memory and returned, in a state similar to the following: return 1; return 1; return 1 * 2; return 1 * 2 * 3; return 1 * 2 * 3 * 4; In the solution, we reverse the array elements by using a recursive function literal. In‐ stead of beginning at index zero, we begin the array from the end length, and decrement this value with each iteration. When the value is zero, we return the string. If we want the reverse—to concatenate the array elements, in order, to a string—modify the function: var orderArray = function(x,indx,str) { return indx == x.length-1 ? str : orderArray(x,++indx,(str+=x[indx] + " ")); } var arr = ['apple','orange','peach','lime']; var str = orderArray(arr,-1,""); // apple orange peach lime console.log(str); Rather than the length of the array, we start with an index value of –1, and continue the loop until one less than the length of the array. We increment the index value rather than decrement it with each loop. Most recursive functions can be replaced with code that performs the same function linearly, via some kind of loop. The advantage of recursion is that recursive functions can be fast and efficient. In addition, it adheres to the functional programming para‐ digm, which means the code is going to be more reliable and consistent. The downside, though, is that recursive functions can be very memory-intensive. How‐ ever, the next section explains why this is likely to change in future implementations of JavaScript. Advanced: Tail Call Optimization Promised in ECMAScript 6 is a new JavaScript feature called tail call optimization, or more properly, proper tail calls. In the following recursive factorial function (less cryptically displayed than the one I provided previously): 3.3. Implementing a Recursive Algorithm | 67 function factorial(num) { if (num == 0) { return 1; } // Otherwise, call this recursive procedure again. else { return (num * factorial(num - 1)); } } The call to the function at the end of the function is the tail call. Currently, each time the recursive function is called, another frame is added to the call stack. Basically what’s happening is the JavaScript engine is keeping track of the function call and the data passed to it. Enough calls, and the memory is exhausted and you get a RangeError. What the proper tail call (optimization) does is reuse the same frame rather than add a new one. Once this feature is incorporated, the possibility of a RangeError error hap‐ pening is eliminated. At the time this was written, no browser or other JavaScript engine has implemented proper tail call functionality. There is a way of working around the lack of this optimization by using what is known as a trampoline. However, the approach is not for the faint of heart. For more on using a trampoline, I recommend reading Reginald Braithwaite’s Functional JavaScript—Tail Call Optimization and Trampolines. See Also Some of the negative consequences of recursive functions can be mitigated via memo‐ ization, covered in Recipe 3.8. Accessing the outer variable internally with the recursive function is covered in Recipe 3.5, which goes into function scope. 3.4. Preventing Code Blocking with a Timer and a Callback Problem You have a piece of code that can be time consuming, and you don’t want to block the rest of the code from processing while waiting for it to finish. But you do need to perform some functionality when the time-consuming function is finished. Solution Use a callback function in conjunction with setTimeout() with timer set to zero (0). 68 | Chapter 3: Functions: The JavaScript Building Blocks In Example 3-1, factorial() is called twice: once with a value of 3 and once with a value of 4. In factorial(), the value of the parameter is printed out to the console in each iteration. In noBlock(), a setTimeout() is used to call factorial(), passing to it its first parameter. In addition, an optional second parameter is called if the second parameter is a function. noBlock() is called twice with other JavaScript statements printing nonessential text to the console inserted preceding, between, and after the two calls. It’s also called a third time, in the callback for the very first call to noBlock(). Example 3-1. Using a timer and callback function to prevent code blocking function factorial(n) { console.log(n); return n == 1 ? 1 : n * factorial(n -1); } function noBlock(n, callback) { setTimeout(function() { var val = factorial(n); if (callback && typeof callback == 'function') { callback(val); } },0); } console.log("Top of the morning to you"); noBlock(3, function(n) { console.log('first call ends with ' + n); noBlock(n, function(m) { console.log("final result is " + m); }); }); var tst = 0; for (i = 0; i < 10; i++) { tst+=i; } console.log("value of tst is " + tst); noBlock(4, function(n) { console.log("end result is " + n); }); console.log("not doing too much"); The result of this application run in jsBin is the following output: "Top of the morning to you" "value of tst is 45" "not doing too much" 3.4. Preventing Code Blocking with a Timer and a Callback | 69 3 2 1 "first call ends with 6" 4 3 2 1 "end result is 24" 6 5 4 3 2 1 "final result is 720" Even though the calls to noBlock() occur before a couple of the extraneous con sole.log() calls, the function’s process doesn’t block the other JavaScript from pro‐ cessing. In addition, the calls to callBack() are processed in the proper order: the two outer calls complete, before the second one invoked in the callback for the first call to callBack() is processed. Discussion Regardless of the underlying system or application, JavaScript is not multithreaded: all processes are run on a single thread of execution. Normally this isn’t an issue except for those times when you’re running an extremely lengthy bit of code and you don’t want to block the rest of the application from finishing its work. In addition, you may want to hold off on running another piece of code until after the lengthy code is finished. One solution for both programming challenges is to use a JavaScript timer in conjunc‐ tion with a callback function—a function passed as parameter to another function, and called within that function in certain circumstances and/or at the end of a process. When a JavaScript timer event occurs, like any other asynchronous event in JavaScript, it’s added to the end of the event queue rather than getting pushed into the queue im‐ mediately. Exactly how and where it enters the queue varies by browser and application environment, but generally, any functionality associated with the timer event is pro‐ cessed after any other functionality within the same queue. This can be a bit of an annoyance if you want a process to run exactly after so many seconds, but the functionality can also be a handy way of not blocking an application while waiting for a time-intensive event. By setting the setTimeout() timer to zero (0), all we’ve done in the solution is to create an event that’s pushed to the end of the execution queue. By putting the time-intensive event into the timer’s process, we’re now no longer blocking, while waiting for the process to complete. 70 | Chapter 3: Functions: The JavaScript Building Blocks And because we usually want to perform a final operation when a time-consuming process finishes, we pass a callback function to the timer process that’s called only when the process is ended. In the program output, the three outer console.log() calls are processed immediately, as is the outer loop within the program execution queue: "Top of the morning to you" "value of tst is 45" "not doing too much" The next event in the queue is the first noBlock() function call, where the code called factorial() logged its activity as it ran, followed by a call to the callback function logging the function’s result: 3 2 1 "first call ends with 6" The second call to callBack() operated the same way and again factorial() logged its activity, and the callback logged the result: 4 3 2 1 "end result is 24" Only then is the third call to callBack(), invoked in the callback function for the first callBack(), and using the end result of the first function call: 6 5 4 3 2 1 "final result is 720" The concept of a callback and not blocking while waiting on long processes or events is essential for event handling for mouse clicks and Ajax calls, as well as underlying the processing architecture for Node.js, which we’ll explore in more detail later in the book. 3.4. Preventing Code Blocking with a Timer and a Callback | 71 3.5. Creating a Function That Remembers Its State Problem You want to create a function that can remember data, but without having to use global variables and without resending the same data with each function call. Solution Create an outer function that takes one or more parameters, and then an inner function that also takes one or more parameters but uses both its and its parent function’s pa‐ rameters. Return the inner function from the outer function, and assign it to a variable. From that point, use the variable as a function: function greetingMaker(greeting) { function addName(name) { return greeting + " " + name; } return addName; } // Now, create new partial functions var daytimeGreeting = greetingMaker("Good Day to you"); var nightGreeting = greetingMaker("Good Evening"); ... // if daytime console.log(daytimeGreeting(name)); // if night console.log(nightGreeting(name)); Discussion We want to avoid cluttering up the global space with variables, as much as possible. However, there are times when you need to store data to be used across several function calls, and you don’t want to have to repeatedly send this information to the function each time. A way to persist this data from one function to another is to create one of the functions within the other, so both have access to the data, and then return the inner function from the outer. Returning one function from another, when the returned function is using the outer function’s scope, is known as a function closure. Before I get into the specifics of function closure, I want to spend a few minutes on functions and scope. In the solution, the inner function addName() is defined in the outer function greeting Maker(). Both of the functions have one argument. The inner function has access to 72 | Chapter 3: Functions: The JavaScript Building Blocks both its argument and the outer function’s argument, but the outer function cannot access the argument passed to the inner function. The inner function can operate on the outer function’s parameters because it is operating within the same context, or scope, of the outer function. In JavaScript, there is one scope that is created for the outermost application environ‐ ment. All global variables, functions, and objects are contained within this outer scope. When you create a function, you create a new scope that exists as long as the function exists. The function has access to all variables in its scope, as well as all of the variables from the outer scope, but the outer scope does not have access to the variables in the function. Because of these scoping rules, we can access window and document objects in all of our browser applications, and the inner function in the solution can also access the data passed to, or originating in, the outer function that wraps it. This also explains how the recursive functions in Recipe 3.3 can in‐ ternally access the variables they’re assigned to in the outer applica‐ tion scope. However, the outer function cannot access the inner function’s arguments or local data because they exist in a different scope. When a function returns a function that refers to the outer function’s local scope: function outer (x) { return function(y) { return x * y; }; } var multiThree = outer(3); alert(multiThree(2)); // 6 is printed alert(multiThree(3)); // 9 is printed The returned function forms a closure. A JavaScript closure is both a function and an environment that existed at the time it was created. In addition, the example also dem‐ onstrates partial application, where a function’s arguments are partially filled (our bound) before it’s executed. When the inner function is returned from the outer function, its application scope at the time, including all references to the outer function’s variables, persist with the func‐ tion. So even though the outer function’s application scope no longer exists, the inner function’s scope exists at the time the function was returned including a snapshot of the outer function’s data. It will continue to exist until the application is finished. 3.5. Creating a Function That Remembers Its State | 73 Another way a closure can be made is if an inner function is as‐ signed to a global variable. So what happens to these variables when an application scope is released? JavaScript supports automatic garbage collection, which means that you and I don’t have to man‐ ually allocate or deallocate memory for our variables. Instead, the memory for variables is created automatically when we create variables and objects, and deallocated auto‐ matically when the variable scope is released. In the solution, the outer function greetingMaker() takes one argument, which is a specific greeting. It also returns an inner function, addName(), which itself takes the person’s name. In the code, greetingMaker is called twice, once with a daytime greeting, assigned to a variable called daytimeGreeting, and once with a nighttime greeting, assigned to a variable called nightGreeting. Now, whenever we want to greet someone in daytime, we can use the daytime greeting function, daytimeGreeting, passing in the name of the person. The same applies to the nighttime greeting function, nightGreeting. No matter how many times each is used, the greeting string doesn’t need to be re-specified: we just pass in a different name. The specialized variations of the greeting remain in scope until the application terminates. Closures are interesting and useful, especially when working with JavaScript objects, as we’ll see later in the book. But there is a downside to closures that turn up when we create accidental closures. An accidental closure occurs when we code JavaScript that creates closures, but aren’t aware that we’ve done so. Each closure takes up memory, and the more closures we create, the more memory is used. The problem is compounded if the memory isn’t released when the application scope is released. When this happens, the result is a per‐ sistent memory leak. Here’s an example of an accidental closure: function outerFunction() { var doc = document.getElementById("doc"); var newObj = { 'doc' : doc}; doc.newObj = newObj; } The newObj contains one property, doc, which contains a reference to the page element identified by doc. But then this element is given a new property, newObj, which contains a reference to the new object you just created, which in turn contains a reference to the page element. This is a circular reference from object to page element, and page element to object. 74 | Chapter 3: Functions: The JavaScript Building Blocks The problem with this circular reference is exacerbated in earlier versions of IE, because these older IE versions did not release memory associated with DOM objects (such as the doc element) if the application scope was released. Even leaving the page does not reclaim the memory: you have to close the browser. Other browsers and newer versions of IE detect this type of situation and perform a cleanup when the user leaves the application (the web page where the JavaScript resided). However, function closures should be deliberate, rather than accidental. See Also Mozilla provides a nice, clean description of closures at http://mzl.la/1z2yXUY. John Resig’s Partial Application in JavaScript has a good write up and demonstration on the concept of partial application. Another example can be found in Recipe 3.7. 3.6. Converting Function Arguments into an Array Problem You want to use Array functionality on a function’s arguments, but the arguments object isn’t an array. Solution Use Array.prototype.slice() and then the function call() method to convert the arguments collection into an array: function someFunc() { var args = Array.prototype.slice.call(arguments); ... } Or, here’s a simpler approach: function someFunc() { var args = [].slice.call(arguments); } Discussion The arguments object is available within a function (and only available within a func‐ tion) and is an array-like object consisting of all arguments passed to the function. I say “array like” because the only Array property available to the object is length. There could be any number of times when our function may get an unknown number of arguments and we’re going to want to do something with them, such as iterate over 3.6. Converting Function Arguments into an Array | 75 the batch and perform some process. It would be nice to be able to use a handy Array method like reduce() with the arguments: function sumRounds() { var args = [].slice.call(arguments); return args.reduce(function(val1,val2) { return parseInt(val1,10) + parseInt(val2,10); }); } var sum = sumRounds("2.3", 4, 5, "16", 18.1); console.log(sum); // 45 The slice() method returns a shallow copy of a portion of an array, or all of an array if a begin or ending value is not given. The slice() method is also a function, which means functional methods like call() can be used with it. In the code snippet, call() is passed the function arguments, which performs the necessary conversion on the argument list, passing in the resulting array to slice(). The call() method’s first argument is a this value—typically the calling object itself, followed by any number of arguments. In the solution, this is the slice() method, and the outer function’s arguments are passed as arguments for slice(). What this technique has effectively done is coerce the outer function’s arguments into an accept‐ able format to serve as argument for slice(). See Also The Advanced section in Recipe 3.7 has a twist on the argument conversion process. Extra The approach described for converting arguments into an array can also be used to convert a NodeList into an array. Given HTML with the following:
test
test2
test3
A query for all div elements results in a NodeList. You can process each node using forEach() if you first convert the NodeList to an array: var nlElems = document.querySelectorAll('div'); var aElems = [].slice.call(nlElems); aElems.forEach(function(elem) { console.log(elem.textContent); }); 76 | Chapter 3: Functions: The JavaScript Building Blocks This code prints out: test test2 test3 You can also use forEach() on the NodeList directly, as covered in Recipe 2.6. 3.7. Reducing Redundancy by Using a Partial Application Problem You have a function with three arguments (has an arity of three (3)) but the first two arguments are typically repeated based on specific use. You want to eliminate the rep‐ etition of arguments whenever possible. Solution Create one function that manipulates three values and returns a result: function makeString(ldelim, str, rdelim) { return ldelim + str + rdelim; } Now create another function that accepts two arguments, and returns the previously created function, but this time, encoding two of the arguments: function quoteString(str) { return makeString("'",str,"'"); } function barString(str) { return makeString("-", str, "-"); } function namedEntity(str) { return makeString("&#", str, ";"); } Only one argument is needed for the new functions: console.log(quoteString("apple")); // "'apple'" console.log(barString("apple")); // "-apple-" console.log(namedEntity(169)); // "© - copyright symbol Discussion Reducing the arity of a function is a classic example of partial application as demon‐ strated earlier in Recipe 3.5 and in this solution. 3.7. Reducing Redundancy by Using a Partial Application | 77 One function performs a process on a given number of arguments and returns a result, while a second function acts as a function factory: churning out functions that return the first function, but with arguments already encoded. As the solution demonstrates, the encoded arguments can be the same, or different. Advanced: A Partial Function Factory We can reduce the redundancy of our function factory even further by creating a generic function, named partial(), capable of reducing any number of arguments for any number of functions: function partial( fn /*, args...*/) { var args = [].slice.call( arguments, 1 ); return function() { return fn.apply( this, args.concat( [].slice.call( arguments ) ) ); }; } We’ll need a copy of the arguments passed to partial() but we don’t want the first, which is the actual function. Typically, to convert a function’s arguments into an array, we’d use syntax like the following: var args = [].slice.call(arguments); In partial(), we specify the beginning value for slice(), in this case 1, skipping over the first argument. Next, an anonymous function is returned that consists of returning the results of the apply() method on the function passed as an argument to parti al(), passing in the anonymous function as this, and concatenating the arguments passed to partial() to whatever arguments are also passed to the newly generated function. The apply() method is similar to call() in that it calls the function (repre‐ sented by this), but accepts an array-like list of arguments, rather than an actual array of arguments. Now we can create functions to generate strings, or add a constant to numbers, or any other type of functionality: function add(a,b) { return a + b; } var add100 = partial(add, 100); console.log(add100(14)); // 114 However, we have to be aware, of the order of arguments. In the case of the delimited string function, we need to remember that partial() concatenates whatever is passed to the generated function to the end of the argument list passed to partial(): 78 | Chapter 3: Functions: The JavaScript Building Blocks function makeString(ldelim, rdelim, str) { return ldelim + str + rdelim; } var namedEntity = partial(makeString, "&#", ";"); console.log(namedEntity(169)); I had to modify makeString() to expect the inserted string to be at the end of the argument list, rather than in the middle, as was demonstrated in the solution. Extra: Using bind() to Partially Provide Arguments ECMAScript 5 simplifies the creation of partial applications via the Function.proto type.bind() method. The bind() method returns a new function, setting this to whatever is provided as first argument. All the other arguments are prepended to the argument list for the new function. Rather than having to use partial() to create the named entity function, we can now use bind() to provide the same functionality, passing in undefined as the first argument: function makeString(ldelim, rdelim, str) { return ldelim + str + rdelim; } var named = makeString.bind(undefined, "&#", ";"); console.log(named(169)); // "©" Now you have two good ways to simplify your functions. See Also Many of us conflated partial application with currying when we first started exploring this capability in JavaScript (guilty as charged). But there is a difference. Partial appli‐ cation is a way of fixing however many arguments to a returned function, which is then invoked with whatever new arguments are necessary to finish the task. Currying, on the other hand, keeps returning functions for however many arguments are passed. For an excellent look at partial applications compared to currying, I recommend Ben Alman’s blog post “Partial Application in JavaScript”. 3.7. Reducing Redundancy by Using a Partial Application | 79 3.8. Improving Application Performance with Memoization (Caching Calculations) Problem You want to optimize your JavaScript applications and libraries by reducing the need to repeat complex and CPU-intensive computations. Solution Use function memoization in order to cache the results of a complex calculation. Here, I’m borrowing an example from Douglas Crockford’s book, JavaScript: The Good Parts (O’Reilly), as applied to the code to generate a Fibonacci number: var fibonacci = function () { var memo = [0,1]; var fib = function (n) { var result = memo[n]; if (typeof result != "number") { result = fib(n -1) + fib(n - 2); memo[n] = result; } return result; }; return fib; }(); Discussion Memoization is the process where interim values are cached rather than recreated, cut‐ ting down on the number of iterations and computation time. It works especially well with something like the Fibonacci numbers or factorials, both of which operate against previously calculated values. For instance, we can look at a factorial, 4!, as follows: return 1; return 1; return 1 * 2; return 1 * 2 * 3; return 1 * 2 * 3 * 4; But we can also view it as: 3! * 4 // 4! In other words, if we cache the value for 2! when creating 3!, we don’t need to recalculate 1 * 2 and if we cache 3! when calculating 4!, we don’t need 1 * 2 * 3, and so on. Memoization is built into some languages, such as Java, Perl, Lisp, and others, but not into JavaScript. If we want to memoize a function, we have to build the functionality 80 | Chapter 3: Functions: The JavaScript Building Blocks ourselves. The key to the effective use of memoization is being aware that the technique doesn’t result in performance improvements until the number of operations is signifi‐ cant enough to compensate for the extra effort. Example 3-2 shows the memoized and nonmemoized versions of the Fibonacci function that Crockford provided in his book. Note that the calculations are intense and can take a considerable time. Save any work you have in other tabs. You may have to override a message given by the browser, too, about killing a script that’s running a long time. Example 3-2. A demonstration of memoization // Memoized Function var fibonacci = function () { var memo = [0,1]; var fib = function (n) { var result = memo[n]; if (typeof result != "number") { result = fib(n -1) + fib(n - 2); memo[n] = result; } return result; }; return fib; }(); // nonmemoized function var fib = function (n) { return n < 2 ? n : fib(n - 1) + fib(n - 2); }; // run nonmemo function, with timer console.time("non-memo"); for (var i = 0; i <= 10; i++) { console.log(i + " " + fib(i)); } console.timeEnd("non-memo"); // now, memo function with timer console.time("memo"); for (var i = 0; i <= 10; i++) { console.log(i + " " + fibonacci(i)); } console.timeEnd("memo"); First, the code is run in 10 times in a loop, in jsFiddle via Firefox: non-memo: 14ms memo: 8ms The result generates one big “meh.” In the second run, though, the code is edited to run the code in a for loop of 30. The result is as follows: 3.8. Improving Application Performance with Memoization (Caching Calculations) | 81 non-memo: 4724ms memo: 19ms A major change. When I tried to run the example in a loop of 50 iterations, my browser crashed. See Also There’s little information on JavaScript memoization online. Crockford provides a generic “memoize” function in his book, as does Addy Osmani in “Faster JavaScript Memoization for Improved Application Performance”. In addition, the Underscore.js library also provides a memoize() function. 3.9. Using an Anonymous Function to Wrap Global Variables Problem You need to create a variable that maintains state between function calls, but you want to avoid global variables. Solution Use an Immediately-Invoked Function Expression (IIFE) to wrap variables and functions both: faux Global Discussion An anonymous function surrounds the global values, is immediately evaluated, and then never evaluated again. Ben Allam gave the pattern the name of Immediately- Invoked Function Expression (IIFE or “iffy”), though functionality demonstrated in the solution has existed for some time. IIFEs are used in many major libraries and frame‐ works, including the popular jQuery, as a way of wrapping plug-in functions so that the code can use the jQuery dollar sign function ($) when the jQuery plug-in is used with another framework library. The approach consists of surrounding the code block with parentheses, beginning with the anonymous function syntax, and following up with the code block and then the final function closure. It could be the following, if there’s no parameter passed into the code block: })(); or the following, if you are passing a parameter into the function: })(jQuery); Now you can create as many variables as you need without polluting the global space or colliding with global variables used in other libraries. See Also Ben Alman coined the IIFE phrase in Immediately-Invoked Function Expression. In addition to the writing on IIFE, take time to read the articles Ben links in the “Further Reading” section. 3.10. Providing a Default Parameter Problem You want to specify a default value for a parameter if no argument value is given when a function is called. 3.10. Providing a Default Parameter | 83 Solution Use the new ECMAScript 6 (ES 6) default parameter functionality: function makeString(str, ldelim = "'", rdelim = "'") { return ldelim + str + rdelim; } console.log(makeString(169)); // "'169'" Discussion One of the biggest gaps in JavaScript is the lack of a default parameter. Yes, we can emulate the same functionality, but nothing is simpler and more elegant than having support for a default parameter built in. The use is simple: if one or more arguments can be optional, you can provide a default parameter using syntax like the following: ldelim = "'" Just assign the default value (in whatever data type format) to the parameter. The default parameter functionality can be used with any parameter. To maintain the proper argument position, you can pass a value of undefined in the argument: console.log(makeString(169,undefined,"-")); // "'str-" At the time I wrote this, only Firefox had implemented default parameter functionality. To ensure future compatibility, test the parameter for the undefined value and adjust accordingly: function makeString(str, ldelim="'", rdelim="'") { ldelim = typeof ldelim !== 'undefined' ? ldelim : "'"; rdelim = typeof rdelim !== 'undefined' ? rdelim : "'"; return ldelim + str + rdelim; } 84 | Chapter 3: Functions: The JavaScript Building Blocks CHAPTER 4 The Malleable JavaScript Object With the increased interest in functional programming, you might think there’s less interest in JavaScript’s object-based capability. However, JavaScript is a flexible, adapt‐ able language, and is just as happy to embrace both functional programming and object- oriented development. There is a caveat related to JavaScript’s object-oriented capabilities: unlike languages such as Java or C++, which are based on classes and class instances, JavaScript is based on prototypical inheritance. What prototypical inheritance means is that reuse occurs through creating new instances of existing objects, rather than instances of a class. In‐ stead of extensibility occurring through class inheritance, prototypical extensibility happens by enhancing an existing object with new properties and methods. Prototype-based languages have an advantage in that you don’t have to worry about creating the classes first, and then the applications. You can focus on creating applica‐ tions, and then deriving the object framework via the effort. It sounds like a mishmash concept, but hopefully as you walk through the recipes you’ll get a better feel for JavaScript’s prototype-based, object-oriented capabilities. A Brief Note About Functional Programming and Object-Oriented Development In the last few years, especially after the explosive growth of jQuery and Node.js, the trend is to use functional programming (discussed in Chapter 2) as a development ap‐ proach over the more traditional prototype object-oriented techniques. Functional programming does have benefits. There is less chance of unwanted side effects, the coding can be simpler to read and maintain, and at times, it seems we’re spending less time on the fussy “get everything together” parts. 85 But object-oriented development is a long-established and understood development paradigm, with well-understood design principles. In addition, there is strong support for object-oriented development in JavaScript, and much of the history of the language has been focused on this approach. The thing is, JavaScript supports both, and is completely neutral as to which approach is best. That is its benefit, and its bane: As far as JavaScript development is concerned, what are the pros and cons of each paradigm? The biggest drawback is that the language supports both paradigms but leaves it up to you. It supports both but doesn’t mandate (or make particularly suitable) any. Frameworks exist to simplify object-oriented JavaScript as well as function- oriented JavaScript. — Dino Esposito Read more of Dino Esposito’s comparison of functional programming and object- oriented development in “Functional vs. Object-Oriented JavaScript Development”. 4.1. Keeping Object Members Private Problem You want to keep one or more object properties private, so they can’t be accessed outside the object instance. Solution When creating the private data members, do not use the this keyword with the member: function Tune(song,artist) { var title = song; this.concat = function() { return title + " " + artist; } } var happySongs = []; happySongs[0] = new Tune("Putting on the Ritz", "Ella Fitzgerald"); console.log(happySongs[0].title); // undefined // prints out correct title and artist console.log(happySongs[0].concat()); Discussion Variables in the object constructor (the function body), are not accessible outside the object unless they’re attached to that object using this. If they’re redefined using the 86 | Chapter 4: The Malleable JavaScript Object var keyword or passed in as parameters only, the Tune’s inner function, the concat() method, can access these now-private data members. This type of method—one that can access the private data members, but is, itself, ex‐ posed to public access via this—has been termed a privileged method by Douglas Crockford, the father of JSON (JavaScript Object Notation). As he himself explains: This pattern of public, private, and privileged members is possible because JavaScript has closures. What this means is that an inner function always has access to the vars and parameters of its outer function, even after the outer function has returned. This is an extremely powerful property of the language [. . . .] Private and privileged members can only be made when an object is constructed. Public members can be added at any time. Be aware, though, that the privacy of the variable is somewhat illusory. One can easily assign a value to that property outside the constructor function, and overwrite the pri‐ vate data: happySongs[0].title = 'testing'; console.log(happySongs[0].title); // testing However, the “privacy” of the data isn’t meant to ensure security of the object. It’s a contract with the developer, a way of saying, “This data isn’t meant to be accessed directly, and doing so will probably mess up your application.” As such, developers also typically use a naming convention where private data members begin with an underscore, to highlight that they aren’t meant to be accessed or set directly: function Tune(song,artist) { var _title = song; this.concat = function() { return _title + " " + artist; } } See Also See Recipe 3.5 for more on function closures. See Recipe 4.2 for more on adding public members after the object has been defined. 4.2. Using Prototype to Create Objects You want to create a new object, but you don’t want to add all the properties and methods into the constructor function. Solution Use the object’s prototype to add the new properties: 4.2. Using Prototype to Create Objects | 87 Tune.prototype.addCategory = function(categoryName) { this.category = categoryName; } Discussion Object is the ancestor for every object in JavaScript; objects inherit methods and prop‐ erties from the Object via the Object prototype. It’s through the prototype that we can add new methods to existing objects: var str = 'one'; String.prototype.exclaim = function() { if (this.length == 0) return this; return this + '!'; } var str2 = 'two'; console.log(str.exclaim()); // one! console.log(str2.exclaim()); // two! Before ECMAScript 5 added trim() to the String object, applications used to extend the String object by adding a trim method through the prototype object: String.prototype.trim = function() { return (this.replace(/^[\s\xA0]+/, "").replace(/[\s\xA0]+$/, "")); } Needless to say, you’d want to use extreme caution when using this functionality. Ap‐ plications that have extended the String object with a homegrown trim method may end up behaving differently than applications using the new standard trim method. To avoid this, libraries test to see if the method already exists before adding their own. We can also use prototype to add properties to our own objects. In Example 4-1, the new object, Tune, is defined using function syntax. It has two private data members, a title and an artist. A publicly accessible method, concatTitleArtist(), takes these two private data members, concatenates them, and returns the result. After a new instance of the object is created, and the object is extended with a new method (addCategory()) and data member (category) the new method is used to update the existing object instance. Example 4-1. Instantiating a new object, adding values, and extending the object function Tune(title,artist) { this.concatTitleArtist = function() { return title + " " + artist; } } 88 | Chapter 4: The Malleable JavaScript Object // create instance, print out values var happySong = new Tune("Putting on the Ritz", "Ella Fitzgerald"); // extend the object Tune.prototype.addCategory = function(categoryName) { this.category = categoryName; } // add category happySong.addCategory("Swing"); // print song out to new paragraph var song = "Title and artist: " + happySong.concatTitleArtist() + " Category: " + happySong.category; console.log(song); The result of running the code is the following line printed out to the console: "Title and artist: Putting on the Ritz Ella Fitzgerald Category: Swing" One major advantage to extending an object using prototype is increased efficiency. When you add a method directly to a function constructor, such as the concat TitleArtist() method in Tune, every single instance of the object then has a copy of this function. Unlike the data members, the function isn’t unique to each object instance. When you extend the object using prototype, as the code did with addCategory(), the method is created on the object itself, and then shared equally between all instances of the objects. Of course, using prototype also has disadvantages. Consider again the concat TitleArtist() method. It’s dependent on access to data members that are not accessible outside the object. If the concatTitleArtist() method was defined using prototype and then tried to access these data members, an error occurs. If you define the method using prototype directly in the constructor function, it is created in the scope of the function and does have access to the private data, but the data is overridden each time a new object instance is created: function Tune(title,artist) { var title = title; var artist = artist; Tune.prototype.concatTitleArtist = function() { return title + " " + artist; } } var sad = new Tune('Sad Song', 'Sad Singer') var happy = new Tune('Happy', 'Happy Singer'); 4.2. Using Prototype to Create Objects | 89 console.log(sad.concatTitleArtist()); // Happy Happy Singer The only data unique to the prototype function is what’s available via this. There are twisty ways around this, but they not only add to the complexity of the application, they tend to undermine whatever efficiency we get using prototype. Generally, if your function must deal with private data, it should be defined within the function constructor, and without using prototype. Otherwise, the data should be available via this, or static and never changing once the object is created. 4.3. Inheriting an Object’s Functionality Problem When creating a new object type, you want to inherit the functionality of an existing JavaScript object. Solution Use Object.create() to implement the inheritance: function origObject() { this.val1 = 'a'; this.val2 = 'b'; } origObject.prototype.returnVal1 = function() { return this.val1; }; origObject.prototype.returnVal2 = function() { return this.val2; }; function newObject() { this.val3 = 'c'; origObject.call(this); } newObject.prototype = Object.create(origObject.prototype); newObject.prototype.constructor=newObject; newObject.prototype.getValues = function() { return this.val1 + " " + this.val2 + " "+ this.val3; }; var obj = new newObject(); 90 | Chapter 4: The Malleable JavaScript Object console.log(obj instanceof newObject); // true console.log(obj instanceof origObject); // true console.log(obj.getValues()); "a b c" Discussion The Object.create() method introduced with ECMAScript 5 provides classical in‐ heritance in JavaScript. The first parameter is the object that serves as prototype for the newly created object, and the second optional parameter is a set of properties defined for the object, and equivalent to the second argument in Object.defineProperties(). In the solution for this recipe, the prototype for the original object is passed in the Object.create() call, assigned to the new object’s own prototype. The new object’s constructor property is set to the new object’s constructor function. The new object’s prototype is then extended with a new method, getValues(), which returns a string consisting of concatenated properties from both objects. Note the use of instanceof demonstrating how both the old and new object prototypes are in the new object’s prototype chain. In the constructor function for the new object, you need to use call() to chain the constructors for both objects. If you want to pass the argument list between the two objects, use apply() instead, as demonstrated in Example 4-2. Example 4-2. Demonstrating classical inheritance in JavaScript with Object.create function Book (title, author) { this.getTitle=function() { return "Title: " + title; }; this.getAuthor=function() { return "Author: " + author; }; } function TechBook (title, author, category) { this.getCategory = function() { return "Technical Category: " + category; }; this.getBook=function() { return this.getTitle() + " " + author + " " + this.getCategory(); }; Book.apply(this, arguments); } TechBook.prototype = Object.create(Book.prototype); 4.3. Inheriting an Object’s Functionality | 91 TechBook.prototype.constructor = TechBook; // get all values var newBook = new TechBook("The JavaScript Cookbook", "Shelley Powers", "Programming"); console.log(newBook.getBook()); // now, individually console.log(newBook.getTitle()); console.log(newBook.getAuthor()); console.log(newBook.getCategory()); In jsBin, the output for the application is: "Title: The JavaScript Cookbook Shelley Powers Technical Category: Programming" "Title: The JavaScript Cookbook" "Author: Shelley Powers" "Technical Category: Programming" 4.4. Extending an Object by Defining a New Property Problem You can easily slap a new property onto an object, but you want to do so in such a way that you have more control of how it’s used. Solution Use the defineProperty() method to add the property. Given the following object: var data = {} If you want to add the following two properties with the given characteristics: • type: Initial value set and can’t be changed, can’t be deleted or modified, but can be enumerated • id: Initial value set, but can be changed, can’t be deleted or modified, and can’t be enumerated Use the following JavaScript: var data = {}; Object.defineProperty(data, 'type', { value: 'primary', enumerable: true }); 92 | Chapter 4: The Malleable JavaScript Object console.log(data.type); // primary data.type = 'secondary'; console.log(data.type); // nope, still primary Object.defineProperty(data, 'id', { value: 1, writable: true }); console.log(data.id); // 1 data.id=300; console.log(data.id); // 300 for (prop in data) { console.log(prop); // only type displays } Discussion The defineProperty() is a way of adding a property to an object other than direct assignment that gives us some control over its behavior and state. There are two varia‐ tions of property you can create with defineProperty(): a data descriptor, as demon‐ strated in the solution, and an accessor descriptor, defined with a getter-setter function pair. The defineProperty() Object method for accessor descriptors re‐ places the now deprecated __defineGetter and __defineSetter. An example of an accessor descriptor is the following: var data = {}; var group = 'history'; Object.defineProperty(data, "category", { get: function () { return group; }, set: function (value) { group = value; }, enumerable: true, configurable: true }); console.log(data.category); // history group = 'math'; console.log(data.category); // math 4.4. Extending an Object by Defining a New Property | 93 data.category = 'spanish'; console.log(data.category); // spanish console.log(group); // spanish Changes to the value for data.category and group are now interconnected. The Object.defineProperty() supports three parameters: the object, the property, and a descriptor object. The latter consists of the following options: • configurable: false by default; controls whether the property descriptor can be changed • enumerable: false by default; controls whether the property can be enumerated • writable: false by default; controls whether the property value can be changed through assignment • value: The initial value for the property • get: undefined by default; property getter • set: undefined by default; property setter The defineProperty() method has wide support in all modern browsers, but with caveats. Safari does not allow its use on a DOM object, while IE8 only supports it on a DOM object (IE9 and later support it on all objects). See Also Recipe 4.5 details how to prevent the addition of new properties to an object, and Recipe 4.6 covers freezing an object against any further change. 4.5. Preventing Object Extensibility Problem You want to prevent others from extending an object. Solution Use the ECMAScript 5 Object.preventExtensions() method to lock an object against future property additions: 'use strict'; var Test = { value1 : "one", value2 : function() { return this.value1; } 94 | Chapter 4: The Malleable JavaScript Object }; try { Object.preventExtensions(Test); // the following fails, and throws a TypeError in Strict mode Test.value3 = "test"; } catch(e) { console.log(e); } Discussion The Object.preventExtensions() method prevents developers from extending the object with new properties, though property values themselves are still writable. It sets an internal property, Extensible, to false. You can check to see if an object is extensible using Object.isExtensible: if (Object.isExtensible(obj)) { // extend the object } If you attempt to add a property to an object that can’t be extended, the effort will either fail silently, or, if strict mode is in effect, will throw a TypeError exception: TypeError: Can't add property value3, object is not extensible Though you can’t extend the object, you can edit existing property values, as well as modify the object’s property descriptor. See Also Recipe 4.4 covers property descriptors. strict mode was covered in “Extra: Speaking of Strict Mode” on page 33. 4.6. Preventing Any Changes to an Object Problem You’ve defined your object, and now you want to make sure that its properties aren’t redefined or edited by other applications using the object. Solution Use Object.freeze() to freeze the object against any and all changes: 'use strict'; 4.6. Preventing Any Changes to an Object | 95 var test = { value1 : 'one', value2 : function() { return this.value1; } } try { // freeze the object Object.freeze(test); // the following throws an error in Strict Mode test.value2 = 'two'; // so does the following test.newProperty = 'value'; var val = 'test'; // and the following Object.defineProperty(test, 'category', { get: function () { return test; }, set: function (value) { test = value; }, enumerable: true, configurable: true }); } catch(e) { console.log(e); } Discussion ECMAScript 5 brought us several Object methods for better object management. The least restrictive is Object.preventExtensions(obj), covered in Recipe 4.5, which dis‐ allows adding new properties to an object, but you can still change the object’s property descriptor or modify an existing property value. The next, more restrictive method is Object.seal(), which prevents any modifications or new properties from being added to the property descriptor, but you can modify an existing property value. The most restrictive method is Object.freeze(). This method disallows extensions to the object and restricts changes to the property descriptor. In addition, Object.freeze() also prevents any and all edits to existing object properties. Literally, once the object is frozen, that’s it—no additions, no changes to existing properties. The first property modification in the solution code: test.value2 = "two"; results in the following error (in Chrome): 96 | Chapter 4: The Malleable JavaScript Object TypeError: Cannot assign to read only property 'value2' of # If we comment out the line, the next object adjustment: test.newProperty = "value"; throws the following error: TypeError: Can't add property newProperty, object is not extensible Commenting out this line leaves the use of defineProperty(): var val = 'test'; // and the following Object.defineProperty(test, "category", { get: function () { return test; }, set: function (value) { test = value; }, enumerable: true, configurable: true }); We get the final exception, for the use of defineProperty() on the object: TypeError: Cannot define property:category, object is not extensible. If we’re not using strict mode, the first two assignments fail silently, but the use of defineProperty() still triggers an exception (this mixed result is another good reason for using strict mode). Check if an object is frozen using the companion method, Object.isFrozen(): if (Object.isFrozen(obj)) ... 4.7. Namespacing Your JavaScript Objects Problem You want to encapsulate your data and functions in such a way as to prevent clashes with other libraries. Solution Use an object literal, what I call a one-off object, to implement the JavaScript version of namespacing. An example is the following: var jscbObject = { // return element getElem : function (identifier) { return document.getElementById(identifier); }, 4.7. Namespacing Your JavaScript Objects | 97 stripslashes : function(str) { return str.replace(/\\/g, ''); }, removeAngleBrackets: function(str) { return str.replace(//g,'>'); } }; var sample = "
testing\changes
"; var result = jscbObject.stripslashes(sample); result = jscbObject.removeAngleBrackets(result); console.log(result); //<div>testingchanges</div> Discussion As mentioned elsewhere in this book, all built-in objects in JavaScript have a literal representation in addition to their more formal object representation. For instance, an Array can be created as follows: var newArray = new Array('one','two','three'); or using the array literal notation: var newArray = ['one','two','three']; The same is true for objects. The notation for object literals is pairs of property names and associated values, separated by commas, and wrapped in curly brackets: var newObj = { prop1 : "value", prop2 : function() { ... }, ... }; The property/value pairs are separated by colons. The properties can be scalar data values or they can be functions. The object members can then be accessed using the object dot-notation: var tmp = newObj.prop2(); or: var val = newObj.prop1 * 20; or: getElem("result").innerHTML=result; Using an object literal, we can wrap all of our library’s functionality in such a way that the functions and variables we need aren’t individually in the global space. The only global object is the actual object literal, and if we use a name that incorporates 98 | Chapter 4: The Malleable JavaScript Object functionality, group, purpose, author, and so on, in a unique manner, we effectively namespace the functionality, preventing name clashes with other libraries. Advanced I use the term one-off with the object literal rather than the more commonly known singleton because, technically, the object literal doesn’t fit the singleton pattern. A singleton pattern is one where only one instance of an object can be created. We can say this is true of our object literal, but there’s one big difference: a singleton can be instantiated at a specific time rather than exist as a static construct, which is what the solution defines. I went to Addy Osmani’s JavaScript Design Patterns (O’Reilly) to get an example of a good implementation of a singleton: var mySingleton = (function () { // Instance stores a reference to the Singleton var instance; function init() { // Singleton // Private methods and variables function privateMethod(){ console.log( "I am private" ); } var privateVariable = "Im also private"; var privateRandomNumber = Math.random(); return { // Public methods and variables publicMethod: function () { console.log( "The public can see me!" ); }, publicProperty: "I am also public", getRandomNumber: function() { return privateRandomNumber; } }; }; 4.7. Namespacing Your JavaScript Objects | 99 return { // Get the Singleton instance if one exists // or create one if it doesn't getInstance: function () { if ( !instance ) { instance = init(); } return instance; } }; })(); singleA = mySingleton.getInstance(); var singleB = mySingleton.getInstance(); console.log( singleA.getRandomNumber() === singleB.getRandomNumber() ); The singleton uses an Immediately-Invoked Function Expression (IIFE) to wrap the object, which immediately returns an instance of the object. But not just any instance —if an instance already exists, it’s returned rather than a new instance. The latter is demonstrated by the object’s getRandomNumber() function, which returns a random number that is generated when the object is created, and returns the same random number regardless of which “instance” is accessed. Access Addy Osmani’s Learning JavaScript Design Patterns online, or you can purchase a digital and/or paper copy directly at O’Reilly, or from your favorite book seller. See Also Chapter 7 covers external libraries and packaging your code into a library for external distribution. Chapter 12 covers another important pattern, the module pattern, and how modularization works with JavaScript. 4.8. Rediscovering this with Prototype.bind Problem You want to control the scope assigned a given function. 100 | Chapter 4: The Malleable JavaScript Object Solution Use the bind() method: window.onload=function() { window.name = "window"; var newObject = { name: "object", sayGreeting: function() { alert("Now this is easy, " + this.name); nestedGreeting = function(greeting) { alert(greeting + " " + this.name); }.bind(this); nestedGreeting("hello"); } }; newObject.sayGreeting("hello"); }; Discussion this represents the owner or scope of the function. The challenge associated with this in JavaScript libraries is that we can’t guarantee which scope applies to a function. In the solution, the object has a method, sayGreeting(), which outputs a message and maps another nested function to its property, nestedGreeting. Without the Function’s bind() method, the first message printed out would say, “Now this is easy, object”, but the second would say, “hello window”. The reason the second printout references a different name is that the nesting of the function disassociates the inner function from the surrounding object, and all unscoped functions automatically become the property of the window object. What the bind() method does is use the apply() method to bind the function to the object passed to the object. In the example, the bind() method is invoked on the nested function, binding it with the parent object using the apply() method. bind() is particularly useful for timers, such as setInterval(). Example 4-3 is a web page with a script that uses setTimeout() to perform a countdown operation, from 10 to 0. As the numbers are counted down, they’re inserted into the web page using the element’s innerHTML property. 4.8. Rediscovering this with Prototype.bind | 101 Example 4-3. Demonstrating the utility of bind Using bind with timers
10
If the setTimeout() function in the code sample had been the following: setTimeout(this.countDown, 1000); the application wouldn’t have worked, because the object scope and counter would have been lost when the method was invoked in the timer. 102 | Chapter 4: The Malleable JavaScript Object Extra: self = this An alternative to using bind(), and one that is still in popular use, is to assign this to a variable in the outer function, which is then accessible to the inner. Typically this is assigned to a variable named that or self: window.onload=function() { window.name = "window"; var newObject = { name: "object", sayGreeting: function() { var self = this; alert("Now this is easy, " + this.name); nestedGreeting = function(greeting) { alert(greeting + " " + self.name); }; nestedGreeting("hello"); } }; newObject.sayGreeting("hello"); }; Without the assignment, the second message would reference “window”, not “object”. 4.9. Chaining Your Object’s Methods Problem You wish to define your object’s methods in such a way that more than one can be used at the same time, similar to the following, which retrieves a reference to a page element and sets the element’s style property: document.getElementById("elem").setAttribute("class","buttondiv"); Solution The ability to directly call one function on the result of another in the same line of code is known as method chaining. It requires specialized code in whatever method you want to chain. For instance, if you want to be able to chain the TechBook.changeAuthor() method in the following code snippet, you must also return the object after you perform whatever other functionality you need: 4.9. Chaining Your Object’s Methods | 103 function Book (title, author) { this.getTitle=function() { return "Title: " + title; }; this.getAuthor=function() { return "Author: " + author; }; this.replaceTitle = function (newTitle) { var oldTitle = title; title = newTitle; }; this.replaceAuthor = function(newAuthor) { var oldAuthor = author; author = newAuthor; }; } function TechBook (title, author, category) { this.getCategory = function() { return "Technical Category: " + category; }; Book.apply(this,arguments); this.changeAuthor = function(newAuthor) { this.replaceAuthor(newAuthor); return this; // necessary to enable method chaining }; } var newBook = new TechBook("I Know Things", "Smart Author", "tech"); console.log(newBook.changeAuthor("Book K. Reader").getAuthor()); Discussion The key to making method chaining work is to return a reference to the object at the end of the method, as shown in the changeAuthor() method in the solution: this.changeAuthor = function(newAuthor) { this.replaceAuthor(newAuthor); return this; // necessary to enable method chaining }; Chaining is used extensively in JavaScript objects, and demonstrated throughout this book when we see functionality such as: var result = str.replace(//g,'>'); 104 | Chapter 4: The Malleable JavaScript Object Libraries such as jQuery also make extensive use of method chaining, as we’ll see later in the book. 4.9. Chaining Your Object’s Methods | 105 CHAPTER 5 JavaScript and Directly Accessing the User Interface The user interface in JavaScript applications is typically the web page in which the script is embedded. The page may open in an Android tablet or a traditional computer brows‐ er, but the concepts are the same. Nowadays, most people use libraries and frameworks in order to manipulate the web page. However, no matter how helpful and sophisticated the library, you still need to have a good idea of what you can, and cannot, do to the web page before making effective use of a library. More importantly, you need to have a good idea of the best practices to use when modifying the web page. 5.1. Accessing a Given Element and Finding Its Parent and Child Elements Problem You want to access a specific web page element, and then find its parent and child elements. Solution Give the element a unique identifier:

This is text.

107 Use document.getElementById() to get a reference to the specific element: var demodiv = document.getElementById("demodiv"); Find its parent via the parentNode property: var parent = demodiv.parentNode; Find its children via the childNodes property: var children = demodiv.childNodes; Discussion A web document is organized like an upside-down tree, with the topmost element at the root and all other elements branching out beneath. Except for the root element (HTML), each element has a parent node, and all of the elements are accessible via the document. There are several different techniques available for accessing these document elements, or nodes as they’re called in the Document Object Model (DOM). Today, we access these nodes through standardized versions of the DOM, such as the DOM Levels 2 and 3. Originally, though, a de facto technique was to access the elements through the browser object model, sometimes referred to as DOM Level 0. The DOM Level 0 was invented by the leading browser company of the time, Netscape, and its use has been supported (more or less) in most browsers since. The key object for accessing web page elements in the DOM Level 0 is the document object. The most commonly used DOM method is document.getElementById(). It takes one parameter: a case-sensitive string with the element’s identifier. It returns an element object, which is referenced to the element if it exists; otherwise, it returns null. There are numerous ways to get one specific web page element, in‐ cluding the use of selectors, covered later in the chapter. But you’ll always want to use the most restrictive method possible, and you can’t get more restrictive than document.getElementById(). The returned element object has a set of methods and properties, including several inherited from the node object. The node methods are primarily associated with tra‐ versing the document tree. For instance, to find the parent node for the element, use the following: var parent = document.getElementById("demodiv").parentNode; // parent node You can find out the type of element for each node through the nodeName property: var type = parent.nodeName; // BODY 108 | Chapter 5: JavaScript and Directly Accessing the User Interface If you want to find out what children an element has, you can traverse a collection of them via a NodeList, obtained using the childNodes property: var demodiv = document.getElementById("demodiv"); var outputString = ""; if (demodiv.hasChildNodes()) { var children = demodiv.childNodes; for (var i = 0; i < children.length; i++) { outputString+="has child " + children[i].nodeName + " "; } } console.log(outputString); Given the element in the solution, the output would be: "has child #text has child P has child #text " You might be surprised by what appeared as a child node. In this example, whitespace before and after the paragraph element is itself a child node with a nodeName of #text. For the following div element:

Some text

Some more text

the demodiv element (node) has five children, not two: has child #text has child P has child #text has child P has child #text In the last code snippet, IE8 only picks up the two paragraph ele‐ ments, which demonstrates why it’s important to be specific with the queries and check nodeName to ensure you’re accessing the correct elements. The best way to see how messy the DOM can be is to use a debugger such as Firebug or the Chrome developer tools, access a web page, and then utilize whatever DOM in‐ spection tool the debugger provides. I opened a simple page in Chrome and used the developer tools to display the element tree, as shown in Figure 5-1. 5.1. Accessing a Given Element and Finding Its Parent and Child Elements | 109 Figure 5-1. Examining the element tree of a web page using Chrome’s developer tools 5.2. Accessing All Images in the Web Page Problem You want to access all img elements in a given document. Solution Use the document.getElementsByTagName() method, passing in img as the parameter: var imgElements = document.getElementsByTagName('img'); Discussion The document.getElementsByTagName() method returns a collection of nodes (a NodeList) of a given element type, such as the img tag in the solution. The collection can be traversed like an array, and the order of nodes is based on the order of the elements within the document (the first img element in the page is accessible at index 0, etc.): var imgElements = document.getElementsByTagName('img'); for (let i = 0; i < imgElements.length; i++) { var img = imgElements[i]; ... } 110 | Chapter 5: JavaScript and Directly Accessing the User Interface The NodeList collection can be traversed like an array, but it isn’t an Array object—you can’t use Array object methods, such as push() and reverse(), with a NodeList. Its only property is length, and its only method is item(), returning the element at the position given by an index passed in as parameter: var img = imgElements.item(1); // second image NodeList is an intriguing object because it’s a live collection, which means changes made to the document after the NodeList is retrieved are reflected in the collection. Example 5-1 demonstrates the NodeList live collection functionality, as well as getEle mentsByTagName. In the example, three images in the web page are accessed as a NodeList collection using the getElementsByTagName method. The length property, with a value of 3, is output to the console. Immediately after, a new paragraph and img elements are created, and the img is appended to the paragraph. To append the paragraph following the others in the page, getElementsByTagName is used again, this time with the paragraph tags (p). We’re not really interested in the paragraphs, but in the paragraphs’ parent elements, found via the parentNode property on each paragraph. The new paragraph element is appended to the paragraph’s parent element, and the previously accessed NodeList collection’s length property is again printed out. Now, the value is 4, reflecting the addition of the new img element. Example 5-1. Demonstrating getElementsByTagName and the NodeList live collection property NodeList

image description

image description

image description

In addition to using getElementsByTagName() with a specific element type, you can also pass the universal selector (*) as a parameter to the method to get all elements: var allelems = document.getElementsByTagName('*'); IE7, or IE8 running in IE7 mode, will return an empty NodeList if you use the universal selector with getElementsByTagName(). See Also In the code demonstrated in the discussion, the children nodes are traversed using a traditional for loop. Array functionality, such as forEach(), can’t be used directly with a NodeList because it’s not an Array. You can coerce the NodeList, as is demonstrated in Recipe 2.6, but this type of coercion has its own drawbacks. Extra: Namespace Variation Like most of the DOM API access methods, there is a variation of getElementsByTag Name(), getElementsByTagNameNS, which can be used in documents that support mul‐ tiple namespaces, such as an XHTML web page with embedded MathML or SVG. In Example 5-2, an SVG document is embedded in XHTML. Both the XHTML docu‐ ment and the embedded SVG make use of the title element. The title element in the XHTML document is part of the default XHTML namespace, but the title in the SVG is part of the Dublin Core namespace. When the title element is accessed, information about the title, including its name‐ space, the prefix, the localName, and the textContent, are printed out. The prefix is the dc component of dc:title, and the localName is the title part of dc:title. The textContent is a new property, added with the DOM Level 2, and is the text of the element. In the case of title (either the XHTML or the Dublin Core element), it would be the title text. Example 5-2. The differences between the namespace and nonnamespace variation of getElementsByTagName 112 | Chapter 5: JavaScript and Directly Accessing the User Interface Namespace

SVG

Sizing Red Circle circle red graphic 5.2. Accessing All Images in the Web Page | 113 Testing RDF in SVG Testing testing image/svg+xml en The result of the application can vary between browsers. When using Firefox and later versions of IE, and accessing title using getElementsByTagName(), the only title returned is the XHTML document title. However, when using the namespace varia‐ tion (getElementsByTagNameNS()), and specifying the Dublin Core namespace, all of the Dublin Core titles in the RDF within the SVG are returned. When using getElementsByTagName with title in Chrome and Opera, both the XHTML title and the Dublin Core titles are returned, as shown in Figure 5-2. 114 | Chapter 5: JavaScript and Directly Accessing the User Interface Figure 5-2. Using getElementsByTagNameNS to get namespaced elements Though IE8 doesn’t directly support the XHTML MIME type, if the page is served as text/html using some form of content negotiation, IE will process the page as HTML. However, though the getElementsByTagName() works with IE, the namespaced version of the method, getElementsByTagNameNS(), does not. All of the values are returned as undefined. IE8 doesn’t return the dc:title entries in the SVG, either. If the Dublin Core namespace is declared in the html element, instead of in the svg element, IE8 does return all of the dc:title entries, as well as the XHTML title: An alternative approach uses a tag name string that concatenates the prefix and localName. All of the browsers will find the dc:title using the following: var titles = document.getElementsByTagName("dc:title"); However, you can’t access the namespace-specific properties using the pseudoname‐ space method. Your applications can’t access the namespace properties using the IE approach of embedding all of the namespace declarations in the html tag, but you can find out the namespace URI via Microsoft’s tagURN property: console.log(title[i].tagURN); // for dc:title 5.2. Accessing All Images in the Web Page | 115 Browsers can use the following to get all elements with a given tag, regardless of name‐ space, if the document is served as application/xhtml+xml or other XML type: var titles = document.getElementsByTagNameNS("*","title"); This JavaScript returns both the default XHTML namespace title and the titles in the Dublin Core namespace. As mentioned earlier, IE8 doesn’t properly support namespaces, but later versions do, thanks to support for XHTML. 5.3. Discovering All Images in Articles Using the Selectors API Problem You want to get a list of all img elements that are descendants of article elements, without having to traverse an entire collection of elements. Solution Use the Selectors API and access the img elements contained within article elements using CSS-style selector strings: var imgs = document.querySelectorAll("article img"); Discussion The Selectors API has achieved support in all modern browser versions, though there are quirks in support among the browsers. There are two selector query API methods. The first, querySelectorAll(), is demon‐ strated in the solution; the second is querySelector(). The difference between the two is querySelectorAll(), which returns all elements that match the selector criteria, while querySelector() only returns the first found result. The selectors syntax is derived from CSS selector syntax, except that rather than style the selected elements, they’re returned to the application. In the example, all img ele‐ ments that are descendants of article elements are returned. To access all img elements regardless of parent element, use: var imgs = document.querySelectorAll("img"); 116 | Chapter 5: JavaScript and Directly Accessing the User Interface In the solution, you’ll get all img elements that are direct or indirect descendants of an article element. This means that if the img element is contained within a div that’s within an article, the img element will be among those returned:
If you want only those img elements that are direct children of an article element, use the following: var imgs = document.querySelectorAll("article> img"); If you’re interested in accessing all img elements that are immediately followed by a paragraph, use: var imgs = document.querySelectorAll("img + p"); If you’re interested in an img element that has an empty alt attribute, use the following: var imgs = document.querySelectorAll('img[alt=""]'); If you’re only interested in img elements that don’t have an empty alt attribute, use: var imgs = document.querySelectorAll('img:not([alt=""])'); The negation pseudoselector (:not) is used to find all img elements with alt attributes that are not empty. All of the queries demonstrated in this section should get the same result in all modern browsers. Unfortunately, IE8 has only limited support for selectors—the code in the solution does not work. Unlike the collection returned with getElementsByTagName() covered earlier, the col‐ lection of elements returned from querySelectorAll() is not a “live” collection. Up‐ dates to the page are not reflected in the collection if the updates occur after the collection is retrieved. Though the Selectors API is a wonderful creation, it shouldn’t be used for every document query. You should always use the most restric‐ tive query when accessing elements. For instance, it’s more efficient to use getElementById() to get one specific element given an identifier. 5.3. Discovering All Images in Articles Using the Selectors API | 117 Extra: Namespace Variation and CSS Selectors The following is how to define a namespace in CSS3, via the Namespace module: @namespace svg "http://www.w3.org/2000/svg"; If an element is given with a namespace prefix, such as the following: ... to style the element, use: @namespace q "http://example.com/q-markup"; q|elem { ... } and to style an attribute, you could use: @namespace foo "http://www.example.com"; [foo|att=val] { color: blue } Recipe 5.2 covered the concept of namespaces when querying against the document, and introduced the namespace-specific method getElementsByTagNameNS(). Because the CSS selectors allow for resolving namespaces, we might assume we could use name‐ spaces with querySelector() and querySelectorAll(). In fact, we could with earlier iterations of the API Selectors draft, but there is no way to do so today. Now, a namespace error will be thrown if the namespace is not resolved before using the Selectors API methods. Unfortunately, the Selectors API doesn’t provide an ap‐ proach to resolve the namespace before using one of the methods. Instead, the Selectors API specification recommends using JavaScript processing to handle namespaces. For instance, to find all of the dc:title elements within an SVG element in a document, use the following: var list = document.querySelectorAll("svg title"); var result = new Array(); var svgns = "http://www.w3.org/2000/svg" for(var i = 0; i < list.length; i++) { if(list[i].namespaceURI == svgns) { result.push(list[i]); } In the example code, querying for all of the titles that are descendants of the svg element will return both SVG titles and any Dublin Core or other titles used in the SVG block. In the loop, if the title is in the Dublin Core namespace, it’s pushed on to the new array; otherwise, if the title is in some other namespace, including the SVG namespace, it’s disregarded. The workaround is not an elegant approach, but it is serviceable, and also the only option available for namespaces and the Selectors API at this time. Luckily, having to deal with namespaces in web development happens rarely. 118 | Chapter 5: JavaScript and Directly Accessing the User Interface See Also There are three different CSS selector specifications, labeled as Selectors Level 1, Level 2, and Level 3. CSS Selectors Level 3 contains links to the documents defining the other levels. These documents provide the definitions of, and examples for, the different types of selectors. The CSS3 Namespace module is also located at the W3C. The Selectors API Level 1 is a W3C Recommendation. John Resig, the creator of the popular jQuery library, has provided a comprehensive test suite for selectors (GitHub source). The CSS3.info site also has a nice selectors test. This one is a little easier to view, and provides links with each test to the example code. 5.4. Setting an Element’s Style Attribute Problem You want to add or replace a style setting on a specific web page element. Solution To change one CSS property, modify the property value via the element’s style property: elem.style.backgroundColor="red"; To modify one or more CSS properties for a single element, you can use setAttri bute() and create an entire CSS style rule: elem.setAttribute("style", "background-color: red; color: white; border: 1px solid black"); Or you can predefine the style rule, assign it a class name, and set the class property for the element: .stripe{ background-color: red; color: white; border: 1px solid black; } ... elem.setAttribute("class", "stripe"); Discussion An element’s CSS properties can be modified in JavaScript using one of three ap‐ proaches. As the solution demonstrates, the simplest approach is to set the property’s value directly using the element’s style property: 5.4. Setting an Element’s Style Attribute | 119 elem.style.width = "500px"; If the CSS property contains a hyphen, such as font-family or background-color, use the CamelCase notation for the property: elem.style.fontFamily = "Courier"; elem.style.backgroundColor = "rgb(255,0,0)"; The CamelCase notation removes the dash, and capitalizes the first letter following the dash. You can also use setAttribute() to set the style property: elem.setAttribute("style","font-family: Courier; background-color: yellow"); The setAttribute() method is a way of adding an attribute or replacing the value of an existing attribute for a web page element. The first argument to the method is the attribute name (automatically lowercased if the element is an HTML element), and the new attribute value. When setting the style attribute, all CSS properties that are changed settings must be specified at the same time, as setting the attribute erases any previously set values. However, setting the style attribute using setAttribute() does not erase any settings made in a stylesheet, or set by default by the browser. A third approach to changing the style setting for the element is to modify the class attribute: elem.setAttribute("class", "classname"); Advanced Rather than using setAttribute() to add or modify the attribute, you can create an attribute and attach it to the element using createAttribute() to create an Attr node, set its value using the nodeValue property, and then use setAttribute() to add the attribute to the element: var styleAttr = document.createAttribute("style"); styleAttr.nodeValue = "background-color: red"; someElement.setAttribute(styleAttr); You can add any number of attributes to an element using either createAttribute() and setAttribute(), or setAttribute() directly. Both approaches are equally efficient, so unless there’s a real need, you’ll most likely want to use the simpler approach of setting the attribute name and value directly using setAttribute(). When would you use createAttribute()? If the attribute value is going to be another entity reference, as is allowed with XML, you’ll need to use createAttribute() to create an Attr node, as setAttribute() only supports simple strings. 120 | Chapter 5: JavaScript and Directly Accessing the User Interface Extra: Accessing an Existing Style Setting For the most part, accessing existing attribute values is as easy as setting them. Instead of using setAttribute(), use getAttribute(): var className = document.getElementById("elem1").getAttribute("class"); Getting access to a style setting, though, is much trickier, because a specific element’s style settings at any one time is a composite of all settings merged into a whole. This computed style for an element is what you’re most likely interested in when you want to see specific style settings for the element at any point in time. Happily, there is a method for that: getComputedStyle(). Unhappily, older versions of IE (IE8 and older) don’t support the method. Instead, you have to use Microsoft’s currentStyle to access the computed style. In addition to having to having to use a different technique with older versions of IE, you also have to change the property name. When accessing a CSS property using getComputedStyle(), you’ll use the CSS property name. However, with current Style, you have to use CamelCase. Example 5-3 demonstrates one cross-browser ap‐ proach to getting an element’s background-color. Example 5-3. Getting a computed CSS property that works cross-browser CSS Computed Style

Hi

5.4. Setting an Element’s Style Attribute | 121 In all versions of IE (old and new), the alert message reads red, while Firefox and Chrome return rgb(255,0,0), and Opera returns #ff0000. As I said, getting CSS style information is tricky. 5.5. Applying a Striped Theme to an Unordered List Problem You want to modify the appearance of unordered list items so that the list appears striped. Solution Use the Selectors API to query for every other item in the list, and then change the background color by changing the class for the element or setting the style attribute on the element using setAttribute(): var lis = document.querySelectorAll('li:nth-child(2n+1)'); for (var i = 0; i < lis.length; i++) { lis[i].setAttribute("style","background-color: #ffeeee"); } or: var lis = document.querySelectorAll('li:nth-child(odd)'); for (var i = 0; i < lis.length; i++) { lis[i].setAttribute("style","background-color: #eeeeff"); } or access the list parent element and then traverse its child nodes, changing the back‐ ground color of every other element, using the arithmetic modulo operator: var parentElement = document.getElementById("thelist"); var lis = parentElement.getElementsByTagName("li"); for (var i = 0; i < lis.length; i++) { if (i % 2 == 0) { lis[i].setAttribute("style","background-color: #eeffee"); } } Discussion The :nth-child() pseudoclass allows us to specify an algorithm pattern, which can be used to find elements that match a certain pattern, such as 2n+1, to find every other element. You can also use the odd and even arguments to access the odd or even elements of the type: var lis = document.querySelectorAll('li:nth-child(odd)'); 122 | Chapter 5: JavaScript and Directly Accessing the User Interface Not all browsers support this selector type. Firefox, Opera, Safari, and Chrome do, but IE8 doesn’t support the first two approaches given in the solution, and older versions of most other browsers don’t. If you need to support older browsers, you’ll want to use the third approach in the solutions: get access to all of the elements using whatever method, and then use the modulo arithmetic operator to filter the elements. modulo returns the remainder of dividing the first operand by the second. Dividing the numbers 0, 2, 4, 6, and so on by 2 returns 0, satisfying the conditional test, and the element’s style is altered. In the solution, the even elements are the ones altered. To alter the odd elements, use the following: if ((i + 1) % 2) { ... } See Also See Recipe 5.3 for more details on the Selectors API and the querySelector() and querySelectorAll() methods. See Recipe 5.4 for more on using setAttribute(). 5.6. Finding All Elements That Share an Attribute Problem You want to find all elements in a web document that share the same attribute. Solution Use the universal selector (*) in combination with the attribute selector to find all ele‐ ments that have an attribute, regardless of its value: var elems = document.querySelectorAll('*[class]'); The universal selector can also be used to find all elements with an attribute that’s as‐ signed the same value: elems = document.querySelectorAll('*[class="red"]'); Discussion The solution demonstrates a rather elegant query selector, the universal selector (*). The universal selector evaluates all elements, so it’s the one you want to use when you need to verify something about each element. In the solution, we want to find all of the ele‐ ments with a given attribute. 5.6. Finding All Elements That Share an Attribute | 123 To test whether an attribute exists, all you need to do is list the attribute name within square brackets ([attrname]). In the solution, we’re first testing whether the element contains the class attribute. If it does, it’s returned with the element collection: var elems = document.querySelectorAll('*[class]'); Next, we’re getting all elements with a class attribute value of red. If you’re not sure of the class name, you can use the substring-matching query selector: var elements = document.querySelectorAll('*[class*="red"]'); Now any class name that contains the substring “red” matches. You could also modify the syntax to find all elements that don’t have a certain value. For instance, to find all div elements that don’t have the target class name, use the :not negation operator: var elems = document.querySelectorAll('div:not(.red)'); The examples all work with all modern browsers. Both of the selec‐ tor syntax examples in the solution work with IE8, but the use of the negation operator, :not, does not. See Also See Recipe 5.3 for more details on the Selectors API and the querySelector() and querySelectorAll() methods. 5.7. Inserting a New Paragraph Problem You want to insert a new paragraph just before the third paragraph within a div element. Solution Use some method to access the third paragraph, such as getElementsByTagName(), to get all of the paragraphs for a div element. Then use the createElement() and insert Before() DOM methods to add the new paragraph just before the existing third paragraph: // get the target div var div = document.getElementById("target"); // retrieve a collection of paragraphs var paras = div.getElementsByTagName("p"); 124 | Chapter 5: JavaScript and Directly Accessing the User Interface // if a third para exists, insert the new element before // otherwise, append the paragraph to the end of the div var newPara = document.createElement("p"); if (paras[3]) { div.insertBefore(newPara, paras[3]); } else { div.appendChild(newPara); } Discussion The document.createElement() method creates any HTML element, which then can be inserted or appended into the page. In the solution, the new paragraph element is inserted before an existing paragraph using insertBefore(). Because we’re interested in inserting the new paragraph before the existing third para‐ graph, we need to retrieve a collection of the div element’s paragraphs, check to make sure a third paragraph exists, and then use insertBefore() to insert the new paragraph before the existing one. If the third paragraph doesn’t exist, we can append the element to the end of the div element using appendChild(). See Also Recipe 5.8 contains a complete example demonstrating how to access the div element and the paragraphs, and add a paragraph with text just before the second paragraph. 5.8. Adding Text to a New Paragraph Problem You want to create a new paragraph with text and insert it just before the second para‐ graph within a div element: // use getElementById to access the div element var div = document.getElementById("target"); // use getElementsByTagName and the collection index // to access the second paragraph var oldPara = div.getElementsByTagName("p")[1]; // zero based index // create a text node var txt = document.createTextNode("The new paragraph will contain this text"); // create a new paragraph var para = document.createElement("p"); 5.8. Adding Text to a New Paragraph | 125 // append the text to the paragraph, and insert the new para para.appendChild(txt); div.insertBefore(para, oldPara); Discussion The text within an element is, itself, an object within the DOM. Its type is a Text node, and it is created using a specialized method, createTextNode(). The method takes one parameter: the string containing the text. Example 5-4 shows a web page with a div element containing four paragraphs. The JavaScript creates a new paragraph from text provided by the user via a prompt. The text could just as easily have come from a server communication or other process. The provided text is used to create a text node, which is then appended as a child node to the new paragraph. The paragraph element is inserted in the web page before the first paragraph. Example 5-4. Demonstrating various methods for adding content to a web page Adding Paragraphs

There is a language 'little known,'
Lovers claim it as their own.

Its symbols smile upon the land,
Wrought by nature's wondrous hand;

And in their silent beauty speak,
Of life and joy, to those who seek.

For Love Divine and sunny hours
In the language of the flowers.

Inserting user-supplied text directly into a web page without scrub‐ bing the text first is not a good idea. When you leave a door open, all sorts of nasty things can crawl in. Example 5-4 is for demonstra‐ tion purposes only. 5.9. Deleting Rows from an HTML Table Problem You want to remove one or more rows from an HTML table. Solution Use the removeChild() method on an HTML table row, and all of the child elements, including the row cells, are also removed: var parent = row.parentNode; var oldrow = parent.removeChild(parent); Discussion When you remove an element from the web document, you’re not only removing the element, you’re removing all of its child elements. In this DOM pruning you get a ref‐ erence to the removed element if you want to process its contents before it’s completely discarded. The latter is helpful if you want to provide some kind of undo method in case the person accidentally selects the wrong table row. 5.9. Deleting Rows from an HTML Table | 127 To demonstrate the nature of DOM pruning, in Example 5-5, DOM methods crea teElement() and createTextNode() are used to create table rows and cells, as well as the text inserted into the cells. As each table row is created, an event handler is attached to the row’s click event. If any of the new table rows is clicked, a function is called that removes the row from the table. The removed table row element is then traversed and the data in its cells is extracted and concatenated to a string, which is printed out. Example 5-5. Adding and removing table rows and associated table cells and data Adding and Removing Elements
Value OneValue twoValue three
5.10. Adding a Page Overlay Problem You want to overlay the web page in order to display a message, photo, or form. Solution Provide a stylesheet setting for a div element that is sized and positioned to cover the entire web page. It could be completely opaque, but most overlays are transparent enough to see the underlying page material: 5.10. Adding a Page Overlay | 129 .overlay { background-color: #000; opacity: .7; filter: alpha(opacity=70); position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 10; } Create a div element (or other element) on demand, adding whatever other content is to be displayed to the element: function expandOverlay() { var overlay = document.createElement("div"); overlay.setAttribute("id","overlay"); overlay.setAttribute("class", "overlay"); document.body.appendChild(overlay); } When the overlay is no longer needed, remove it from the page: function restore() { document.body.removeChild(document.getElementById("overlay")); } Discussion Creating an overlay in a web page consists of creating an element set to a z-index higher than anything else in the page, absolutely positioned at the upper left of the page, and sized 100%. In the solution to this recipe, this is achieved more easily by creating a CSS style setting for the overlay class that manages the appearance of the element, and then using document.createElement() and appendChild() to add it to the page. To restore the page, the overlay element is removed. Page overlays are popular for displaying photos, videos, ads, logins, or providing im‐ portant site messages. Example 5-6 contains a web page with a message. Clicking on the message block removes the overlay and message. Example 5-6. Creating an overlay for displaying a message Overlay

Existing material.

Example 5-6 creates an overlay that fits the size of the page as it’s currently opened. Note the CSS setting for the overlay, in particular the fixed positioning. This ensures that the overlay fits the window even if the contents require you to scroll to the right, or down, to see all of the contents. The message is positioned in the center using a CSS trick: set the left and top position to 50% of the page’s width and height, and then set the block’s margins to a negative value equal to half the block’s height and width. We could append the message directly to the overlay, and when the overlay is removed, the message is also removed with removeChild(). However, the overlay’s opacity set‐ ting would impact on its child elements. To ensure the message is bright and easy to read, it’s appended to the web page document, but its z-index setting is set higher than the overlay, placing it on top of the overlay. Most publications that use overlays to display messages when the page is first accessed, use graphics with a “close this” or “X” to indicate where to click to close the message. We could also statically create a section to display and hide it when not in use, but this just clutters up the page contents and can, depending on how it’s formatted, have adverse impact on screen readers. In addition, if you want the same overlay on all pages, it’s simpler to create it as part of the site’s JavaScript library and automatically include it in each page that uses the library. See Also See Recipe 5.9 for more information on removeChild(), Recipe 5.8 about adding text to a web page element, and Recipe 5.7, which covers how to create new page elements. 132 | Chapter 5: JavaScript and Directly Accessing the User Interface 5.11. Creating Collapsible Form Sections Problem You have a large form that takes up a lot of space. You only want to display sections of the form as they are needed. Solution Split the form into display blocks using div elements, and then change the block’s styling to control the display of the form section. When the page is loaded, hide all of the form blocks by changing the display value to none using JavaScript: theformblock.setAttribute("style","display: none"); or: theformblock.style.display="none"; To expand the section, change the display setting to block using setAttribute: theformblock.setAttribute("style","block"); or set the value directly: theformblock.style.display="block"; Discussion There are multiple ways you can prevent form elements from taking up page space. For one, you can clip the element by setting the clipping area. Another approach is to resize the element to zero height. The best approach, though, and the one most applications use, is to employ a collapsible section. A collapsible section is a form of widget—a set of elements, CSS, and JavaScript packaged together and generally considered one object. The typical implementation consists of one element that acts as a label that is always displayed, another element that holds the content, and all contained within a third, parent element. The collapsible section may or may not be used with other collapsible sections to form a higher level widget, the accordion. The accordion widget is a grouping of collapsible sections with an additional behavior: depending on preference, any number of collaps‐ ible sections can be expanded, or only one section can be expanded at a time. To demonstrate how collapsible sections can be used with forms, Example 5-7 shows a form that’s split into two sections. Notice that each form block has an associated label that expands the collapsed form section when clicked. When the label is clicked again, the form section is collapsed again. 5.11. Creating Collapsible Form Sections | 133 Example 5-7. Collapsed form element Collapsed Form Elements

Checkboxes

- box one
- box one
- box one
- box one
- box one

Buttons

- button one
- button one
- button one
- button one
- button one
134 | Chapter 5: JavaScript and Directly Accessing the User Interface
There are numerous ways you can map the click activity in one element by changing the display in another. In Example 5-7, I wrapped both the label and the content elements in a parent element. When you click on a label, the parent to the label element is accessed in JavaScript and its children returned as an HTML collection. The second element’s display toggles—if the element’s display is none, it’s changed to block; if the display is block, it’s changed to none. In the example, notice that the form elements are displayed when the page loads, and only collapsed after the elements are loaded. This is because the form elements are displayed by default if JavaScript is disabled. Extra: JavaScript or HTML5? The example in this recipe uses JavaScript to collapse and display portions of a form. HTML5 provides two elements, details and summary, that implement this effect without any need for scripting. 5.11. Creating Collapsible Form Sections | 135 When should you use one over the other? If you want to add sections into the page that are displayed or hidden based on the user action, it’s just as simple to use the HTML5 elements. However, if you need special effects, or want to attach other behavior when the page section is displayed or hidden, you need to stick with JavaScript. 5.12. Hiding Page Sections Problem You want to hide an existing page element and its children until needed. Solution You can set the CSS visibility property to hide and show the message: msg.style.hidden="visible"; // to display msg.style.hidden="hidden"; // to hide or you can use the CSS display property: msg.style.display="block"; // to display msg.style.display="none"; // to remove from display Discussion Both the CSS visibility and display properties can be used to hide and show ele‐ ments. There is one major difference between the two that impacts which one you’ll use. The visibility property controls the element’s visual rendering, but its physical pres‐ ence still affects other elements. When an element is hidden, it still takes up page space. The display property, on the other hand, removes the element completely from the page layout. The display property can be set to several different values, but four are of particular interest to us: • none: When display is set to none, the element is removed completely from display. • block: When display is set to block, the element is treated like a block element, with a line break before and after. • inline-block: When display is set to inline-block, the contents are formatted like a block element, which is then flowed like inline content. • inherit: This is the default display, and specifies that the display property is in‐ herited from the element’s parent. 136 | Chapter 5: JavaScript and Directly Accessing the User Interface There are other values, but these are the ones we’re most likely to use within JavaScript applications. Unless you’re using absolute positioning with the hidden element, you’ll want to use the CSS display property. Otherwise, the element will affect the page layout, pushing any elements that follow down and to the right, depending on the type of hidden element. There is another approach to removing an element out of page view, and that is to move it totally offscreen using a negative left value. This could work, especially if you’re cre‐ ating a slider element that will slide in from the left. It’s also an approach that the ac‐ cessibility community has suggested using when you have content that you want ren‐ dered by assistive technology (AT) devices, but not visually rendered. See Also Speaking of accessibility, Recipe 6.5 demonstrates how to incorporate accessibility into forms feedback, and Recipe 6.6 touches on creating an updatable, accessible page region. 5.13. Creating Hover-Based Pop-Up Info Windows Problem You like the Netflix website’s pop-up window that displays when the mouse cursor is over a movie thumbnail, and you want to incorporate this functionality into your own application. Solution The Netflix-style of pop-up info window is based on four different functionalities. First, you need to capture the mouseover and mouseout events for each image thumbnail, in order to display or remove the pop-up window, respectively. In the following code, the cross-browser event handlers are attached to all images in the page: window.onload=function() { var imgs = document.getElementsByTagName("img"); for (var i = 0; i < imgs.length; i++) { imgs[i].addEventListener("mouseover",getInfo, false); imgs[i].addEventListener("mouseout",removeWindow, false); } } Second, you need to access something about the item you’re hovering over in order to know what to use to populate the pop-up bubble. The information can be in the page, or you can use web server communication to get the information: function getInfo() { 5.13. Creating Hover-Based Pop-Up Info Windows | 137 // prepare request if (!xmlhttp) { xmlhttp = new XMLHttpRequest(); } var value = this.getAttribute("id"); var url = "photos.php?photo=" + value; xmlhttp.open('GET', url, true); xmlhttp.onreadystatechange = showWindow; xmlhttp.send(null); return false; } Third, you need to either show the pop-up window, if it already exists and is not dis‐ played, or create the window. In the following code, the pop-up window is created just below the object, and just to the right when the web server call returns with the infor‐ mation about the item. The getBoundingClientRect() method is used to determine the location where the pop up should be placed, and createElement() and create TextNode() are used to create the pop up: // compute position for pop up function compPos(obj) { var rect = obj.getBoundingClientRect(); var height; if (rect.height) { height = rect.height; } else { height = rect.bottom - rect.top; } var top = rect.top + height + 10; return [rect.left, top]; } // process return function showWindow() { if(xmlhttp.readyState == 4 && xmlhttp.status == 200) { var response = xmlhttp.responseText.split("#"); var img = document.getElementById(response[0]); if (!img) return; // derive location for pop up var loc = compPos(img); var left = loc[0] + "px"; var top = loc[1] + "px"; // create pop up var div = document.createElement("popup"); div.id = "popup"; var txt = document.createTextNode(response[1]); div.appendChild(txt); 138 | Chapter 5: JavaScript and Directly Accessing the User Interface // style pop up div.setAttribute("class","popup"); div.setAttribute("style","left: " + left + "; top: " + top); document.body.appendChild(div); } } Lastly, when the mouseover event fires, you need to either hide the pop-up window or remove it—whichever makes sense in your setup. Since the application created a new pop-up window in the mouseover event, it removes the pop-up in the mouseout event handler: function removeWindow() { var popup = document.getElementById("popup"); if (popup) popup.parentNode.removeChild(popup); return false; } Discussion Creating a pop-up information or help window doesn’t have to be complicated if you keep the action simple and follow the four steps outlined in the solution. If the pop up provides help for form elements, then you might want to cache the information within the page, and just show and hide pop-up elements as needed. However, if you have pages like the ones at Netflix, which can have hundreds of items, you’ll have better perfor‐ mance if you get the pop-up window information on demand via a web service call (i.e., Ajax or WebSockets). The solution demonstrates that using web calls doesn’t add sig‐ nificant additional complexity to the application. When I positioned the pop up in the example, I didn’t place it directly over the object. The reason is that I’m not capturing the mouse position to have the pop up follow the cursor around, ensuring that I don’t move the cursor directly over the pop up. But if I statically position the pop up partially over the object, the web page readers could move their mouse over the pop up, which triggers the event to hide the pop up…which then triggers the event to show the pop up, and so on. This creates a flicker effect, not to mention a lot of network activity. If, instead, I allowed the mouse events to continue by returning true from either event handler function, when the web page readers move their mouse over the pop up, the pop up won’t go away. However, if they move the mouse from the image to the pop up, and then to the rest of the page, the event to trigger the pop-up event removal won’t fire, and the pop up is left on the page. 5.13. Creating Hover-Based Pop-Up Info Windows | 139 The best approach is to place the pop up directly under (or to the side, or a specific location in the page) rather than directly over the object. This is the approach Netflix uses on its site. 5.14. Displaying a Flash of Color to Signal an Action Problem Based on some action, you want to display a visual cue to signify the success of the action. Solution Use a flash to signal the success or failure of an action. While a red flash is standard for signaling either a successful deletion or an error, a yellow flash is typically used to signal a successful update or action: var fadingObject = { yellowColor : function (val) { var r="ff"; var g="ff"; var b=val.toString(16); var newval = "#"+r+g+b; return newval; }, fade : function (id,start,finish) { this.count = this.start = start; this.finish = finish; this.id = id; this.countDown = function() { this.count+=30; if (this.count >= this.finish) { document.getElementById(this.id).style.background= "transparent"; this.countDown=null; return; } document.getElementById(this.id).style.backgroundColor= this.yellowColor(this.count); setTimeout(this.countDown.bind(this),100); } } }; ... // fade page element identified as "one" fadingObject.fade("one", 0, 300); fadingObject.countDown(); 140 | Chapter 5: JavaScript and Directly Accessing the User Interface Discussion A flash, or fade as it is frequently called, is a quick flash of color. It’s created using a recurring timer that gradually changes the background color of the object being flashed. The color is varied by successively changing the values of the nondominant RGB colors, or colors from a variation of 0 to 255, while holding the dominant color or colors at FF. If, for some reason, the color can’t be perceived (because of color blindness or other factor), the color shows as successions of gray. As you progress down the figure, the color gets progressively paler, as the nondominant red and blue values are increased, from initial hexadecimal values of 00 (0) to FF (255). The color yellow used in the solution kept the red and green values static, while changing the blue. A red flash would keep the red color static, while adjusting both the green and blue. In the solution, I’m setting the beginning and ending colors for the flash when the application calls the fade method on the object, fadingObject. Thus, if I don’t want to start at pure yellow or end at white, I can begin or end with a paler color. A color flash is used to highlight an action. As an example, a red flash can signal the deletion of a table row just before the row is removed from the table. The flash is an additional visual cue, as the table row being deleted helps set the context for the flash. A yellow flash can do the same when a table row is updated. A flash can also be used with an alert message. In the following code snippet, I created an alert that displayed a solid color until removed from the page. I could also have used a red flash to highlight the message, and left the background a pale pink at the end: function generateAlert(txt) { // create new text and div elements and set // Aria and class values and id var txtNd = document.createTextNode(txt); msg = document.createElement("div"); msg.setAttribute("role","alert"); msg.setAttribute("id","msg"); // fade obj.fade("msg", 0, 127); obj.redFlash(); msg.setAttribute("class","alert"); // append text to div, div to document msg.appendChild(txtNd); document.body.appendChild(msg); } 5.14. Displaying a Flash of Color to Signal an Action | 141 The only requirement for the solution would be to either make the color-fade effect more generic, for any color, or add a new, specialized redFlash method that does the same as the yellow. Also note the use of the ARIA alert role in the code snippet. Including an accessible effect ensures all your users will benefit, and as the code demonstrates, it doesn’t add any extra effort. See Also Recipe 6.5 demonstrates how to incorporate accessibility into forms feedback, and Recipe 6.6 touches on creating an updatable, accessible page region. 142 | Chapter 5: JavaScript and Directly Accessing the User Interface CHAPTER 6 Preliminary Testing and Accessibility Best developer practices run the gamut from keeping the cruft out of your code to ensuring your code is accessible to all your application or site’s users. In this chapter, I’m focusing on two specific components: prelimary testing and accessibility. There are various forms of testing, including running tools that highlight bad practices, unit testing, performance testing, as well as testing your applications in a variety of environments. Regardless of how well the code tests out, if it isn’t accessible to all your audience, then it fails. At the end of the chapter, we’ll look at a couple of approaches you can take to ensure your code is accessible as well as accurate and efficient. 6.1. Cleaning Up Your Code with JSHint Problem You want to check your code for any gotchas that may not trigger a failure, but also aren’t the best coding practices. Solution Use a lint tool such as JSHint to highlight potential problems or less than optimal code. Discussion The simplest approach to ensuring your code meets a minimum standard of acceptable coding standards is to use the strict setting at the beginning of your code or function: 'use strict'; 143 This setting ensures certain programming standards are met, such as always defining variables, and never using eval(). And of course, the most reliable way of ensuring your code meets standards is whatever error mechanism is built into the tools you’re using to run the code, typically explored in depth using your favorite debugging tool. There are a set of coding practices, though, that don’t typically trigger an error or warn‐ ing, but should be avoided because they may introduce unwanted application behaviors or results that are hard to discover. That’s where a linting tool comes in. And the most popular of the JavaScript lint tools is JSHint. JSHint is a fork of a previously popular lint tool named JSLint. JSHint is now more popular because it is more configurable and less rigid—as well as being actively maintained. Documentation and installation source and instruction for JSHint can be found at the JSHint home page, which includes links and refer‐ ences to various JSHint plug-ins available in a variety of environments. JSHint can be installed using npm, part of the world of Node.js that is covered in Chapter 11: npm install jshint -g When JSHint is installed using npm, you can run it against any JavaScript using command-line syntax such as the following: jshint scriptfile.js Though workable, running JSHint at the command line is a bit tedious. A preferred option is to use a plug-in or browser tool. Both of the JavaScript online playgrounds— JS Bin and jsFiddle—I used to test most of the examples for this book either use JSHint by default or provide an option to run the tool. When running JSHint, you’re running with a default group of settings. Among some of the default settings are those that prevent bitwise operators from being used, requiring curly brackets ({}) for every block, and the use of strict equality (“===”). You can see all of the options in the .jshintrc file. If you want to change a default setting, use one of four techniques: • From the command line you can use the --config flag and specify the name of the file containing the configuration settings. • Create a local version of the .jshintrc file. • Add settings to your project’s package.json file (more on this in Chapter 11). 144 | Chapter 6: Preliminary Testing and Accessibility • Embed JSHint settings directly in your code. When you’re using an integrated tool that may not provide a way to add an override file for changing JSHint options, you can embed JSHint comments to change default settings directly in the JavaScript. For instance, one of my chronic code foibles is mixing quotes (single and double) in the same code. To prevent this, I can add the following to my JavaScript: /*jshint quotmark:true */ And I’ll get a warning that I used both types of quotes in the code. If I want to use standard equality operators without warnings, I can use the following: /*jshint quotmark:true, eqeqeq:false */ var test1 = "today"; var test2 = 'tomorrow'; if (test == tomorrow) console.log("nope"); Using a linting tool such as JSHint doesn’t mean you have to create perfect code based on some arbitrary standard. However, it does help ensure that when you decide to write code that bucks the standards, you do so deliberately, rather than accidentally. See Also See “Extra: Speaking of Strict Mode” on page 33 for more on use strict, and the concept of strict equality. See Chapter 11 for more on Node, and Recipe 12.14 about using JSHint with a task runner. 6.2. Unit Testing Your Code with QUnit Problem You want to ensure the robustness of your application or library. A part of this is per‐ forming unit testing, but you don’t want to create the tests manually. Solution Use a tool such as QUnit to incorporate unit testing into your application at the earliest possible stage. For instance, we’re interested in testing a new function, addAndRound(): function addAndRound(value1,value2) { return Math.round(value1 + value2); } 6.2. Unit Testing Your Code with QUnit | 145 A QUnit test case could look like the following, which performs two assertion tests: equal, to test whether the function returns a value given as the first parameter, and ok which just checks the truthfulness (the truthy value) of the function’s return: test( "testing addAndRound", function() { equal(6, addAndRound(3.55, 2.33), "checking valid"); ok(addAndRound("three", "4.12"), "checking NaN"); }); The first test succeeds because both parameters are numbers, or can be coerced into being numbers, and the function result is 6. The second fails when the function call returns NaN because the first parameter can’t be coerced into a number, as shown in Figure 6-1. Figure 6-1. Result of running QUnit assertion tests in JS Bin Discussion There are multiple types of tests, such as tests for security, usability, and performance, but the most basic form of testing is unit testing. Unit testing consists of performing tests of discrete source code units, verifying that the unit behaves as expected, and that operations on data provided to or returned from the unit also meet expectations. In JavaScript, a unit is typically a function. A good rule of thumb when it comes to unit testing is that every requirement or use case for a function should have an associated test case (or cases). The unit test checks 146 | Chapter 6: Preliminary Testing and Accessibility that the requirement is met, and the function performs as expected. You can develop your own tests, but using something like QUnit simplifies the test writing. You can download QUnit at the QUnit website, as well as the jQuery CDN (Content Delivery Network). You can also install it using npm, and it’s available as a library in JS Bin. Depending on how you’re using QUnit, you’ll need to add links to the library to your test page. If you’re using something like JS Bin, selecting the QUnit option adds all relevant script source files. At a minimum, you’ll need to add a link to the QUnit CSS file, as well as the script. You’ll also need to add two div elements for the QUnit output. QUnit gives the following as a minimum QUnit test page: QUnit Example
The unit tests will be in the tests.js file. The QUnit website provides very good documentation on using the product, so I’m only going to touch on some of the more common components, demonstrated in the solution. The tests are included within an outer function named test(). This is the most com‐ monly used way to trigger the QUnit testing, and is used to perform a synchronous test. The first parameter is a label for the test results, the second a callback function that includes the tests. QUnit supports several assertion tests, including the two demonstrated in the solution: • deepEqual: Tests for deep, strict equality • equal: Tests for standard equality • notDeepEqual: The inversion of deepEqual testing for deep, strict nonequality • notEqual: The inversion of equal testing for standard nonequality 6.2. Unit Testing Your Code with QUnit | 147 • notPropEqual: Tests an object’s properties for inequality • notStrictEqual: Tests for strict inequality • ok: Tests whether first argument equates to true • propEqual: Tests an object’s properties for equality • strictEqual: Tests for strict equality • throws: Tests if callback throws an exception, and can optionally compare thrown error When the QUnit test is run, a display is output to the page specifying which tests failed and succeeded, each associated with the test label passed to the assertions. Extra: Writing Tests First (((programming, TDD vs. BDD approach))Modern development practices have em‐ braced the idea of writing the tests before much of the functionality for the application (and libraries) is written. This Test-Driven Development (TDD) is a component of the Agile development paradigm. TDD takes some getting used to. Rather than a more formal structured programming or waterfall project design, with TDD you define the tests, do some coding, run the tests, do some refacturing to remove duplicate code, and then repeat the process until the entire functionality is finished. Previous approaches incorporated testing only at the very end of the process, when much of the code has already been created. The Microsoft Developer Network has a page describing a typical scenario. In addition, there are several books and publications online describing both TDD and Agile software development. In addition, another well-known Agile component is behavior-driven development (BDD), developed by Dan North. If you’re interested in utilizing BDD with your Java‐ Script applications, there is a tool, Cucumber.js, specifically designed for BDD in JS. 6.3. Testing Your Application in Various Environments Problem You have a set of environments (operating system and browser) you need to support, but you don’t have enough machines to test your application or library in each environment. 148 | Chapter 6: Preliminary Testing and Accessibility Solution Use an emulating tool or browser testing service that can test your application in all of your environments. These aids help you not only test the integrity of the code, but the appearance and behavior of your user interface. Discussion In-house testing works if your customer uses a finite set of machines and you can easily re-create the environments, or you work for a large enough corporation that can afford to set up one of everything. For most other situations, you either need to use some form of emulation tool or a service. This is especially critical when you’re testing client-side JavaScript. Web or mobile de‐ velopment environments are undergoing rapid change, and a technology you think is safe to use may end up blowing up in your face when you test the functionality in environments other than the ones you use for development. Emulators are a favorite for mobile developers. Some are specific to a browser, such as Ripple for Chrome. Others are standalone tools like Opera Mobile Classic Emulator, shown in Figure 6-2 or the Android Emulator (part of the Android SDK). Download the Opera emulator, or access Ripple for Chrome at the Google Web Store. An excellent resource for discovering emulators is the Emulator page at “Breaking the Mobile Web”. A variation for testing mobile applications is a simulator, which simulates some of the environment but doesn’t fully emulate it at the hardware level. An example of a simulator is Apple’s iOS Simulator. 6.3. Testing Your Application in Various Environments | 149 Figure 6-2. Snapshot of Opera Mobile Classic Emulator emulating my cellphone If you’re more interested in testing how the client interface works in different browsers and different environments, then you’ll want to look for a cross-browser testing service (BrowserStack or Sauce Labs), or an application like Ghostlab (demonstrated in Figure 6-3). You might also consider automated testing services, where you create a script that’s automatically run (Selenium is an example). The key is to look for a service or tool that provides interactive testing—not one that is primarily used to check out the design of the page in different environments. One advantage to some of these tools is they provide testing in both browser and mobile environments. The disadvantage is cost: either a one-time cost for an application, or a monthly or annual fee for a service. The only tool that didn’t have a price tag attached is IE NetRenderer, which allows you to test your website in every variation of IE, from versions 5.5 through 11. 150 | Chapter 6: Preliminary Testing and Accessibility Figure 6-3. Ghostlab Demonstration Photo: one computer, one tablet, and a smartphone One of the services I have used is BrowserStack. It, like most of the other tools and services, provides a trial period for testing the service. In addition, it also works with testing environments, such as QUnit, demonstrated earlier in the chapter. BrowserStack offers a variety of services, including screenshots of your site across sev‐ eral devices—desktop or mobile. From a client-side JavaScript developer’s perspective, the service we’re most interested in is the Live testing, where we can pick an OS and a browser and test our client application, directly. Figure 6-4 demonstrates running an application in Safari on OS X—an environment I don’t have access to. 6.3. Testing Your Application in Various Environments | 151 Figure 6-4. Testing an app on Safari in OS X using Browserstack BrowserStack also provides automated cloud-testing of JavaScript applications. The testing is free for open source software, and available for a fee for commercial and non- open source testing. You can incorporate automated testing with various tools, such as Yeti and TestSwarm. However, it’s primarily focused on testing Node.js applications. See Also I’m covering the Web APIs for mobile development in Chapter 18, and the frameworks and tools in Chapter 15. Integrating testing into a modular development with Node.js is covered in Chapter 12. 6.4. Performance Testing Different Coding Techniques Problem In JavaScript there are, typically, multiple ways you can code a solution. The problem then becomes determining which of the different ways performs best across different environments. 152 | Chapter 6: Preliminary Testing and Accessibility Solution One approach is to use a performance testing tool, such as jsPerf, to try out the different approaches and then proceed accordingly. For instance, in Recipe 2.9, I wanted to determine which had better performance—using an anonymous function or a named function—when passed as a callback function in an Array method. In jsPerf, I set up an array of string elements and created the named function, rpl(), in the Preparation code section: var charSet = ["**","bb","cd","**","cc","**","dd","**"]; function rpl (element) { return (element !== "**"); }; My first test case was using the anonymous function: var newArray = charSet.filter(function(element) { return (element !== "**"); }); My second test case was using the named function: var newArray = charSet.filter(rpl); Running the test in various browsers, shown in Figure 6-5, demonstrated that the anonymous function was the better-performing alternative. Figure 6-5. Results of running two test cases in jsPerf Discussion There are variations of performance testing, from the simple alternative testing demon‐ strated in the solution, to complex, involved load testing of entire systems. These types 6.4. Performance Testing Different Coding Techniques | 153 of testing aren’t used to discover whether there are bugs in the code, or if the code meets use requirements—unit testing should find the former, and some form of user compli‐ ance testing finds the latter. Performance testing is specifically for finding the best, most efficient approach to cre‐ ating your application, and then making sure it meets the demands placed on it when running at peak usage. JsPerf is built on Benchmark.js, which is also available for separate use. The test case in the solution can be accessed at http://jsperf.com/ anony. There are numerous performance and load testing tools to use with Node.js applications. One I’ve used is Selenium. Another approach to performance testing is profiling. Most browser debuggers have a built-in profiling capability. As an example, the popular Firebug debugger for Firefox has profiling built in and available with the click of the “Profile” button, shown in Figure 6-6. Once you turn profiling on, you can run your user compliance tests as a way of generating good usage statistics, and then click the “Profile” button again. Firebug then generates a listing of functions called any time for them to respond. Figure 6-6. An example of Firebug profiling in Firefox 154 | Chapter 6: Preliminary Testing and Accessibility Chrome also has extensive profiling capability, shown in Figure 6-7. To use it, open up the JavaScript Console, click the Profiles tab, and then start whichever profiling type you want to start. After you’ve used your application for some time, click the Profiles “Stop” button and view the results. Figure 6-7. An example of profiling in Chrome You do want to make sure all of your browser extensions are dis‐ abled when you run a profiling tool in your browser, as these will impact the results. In fact, you want to eliminate or minimize any extension when you’re testing your code. Extra: About That Use of jsPerf jsPerf is a fun tool to use, and can be very informative. However, there are issues asso‐ ciated with the tool that can impact the reliability of its tests. One such is the JIT (Just- In-Time) compiler optimizations, which can influence the results. Unless you’re really familiar with what’s happening with the JavaScript engine, you don’t know if your tests results have been skewed because of optimizations. The creator of jsPerf, Mathias Bynens, discusses some of the issues of benchmarking in “Bulletproof JavaScript Benchmarks”. Vyacheslav Egorov gave a talk on the subject, available on YouTube. 6.4. Performance Testing Different Coding Techniques | 155 As an aside, an interesting article, “Optimization Killers”, discusses coding techniques that kill optimization. 6.5. Highlighting Errors Accessibly Problem You want to highlight form field entries that have incorrect data, and you want to ensure the highlighting is effective for all web page users. Solution Use CSS to highlight the incorrectly entered form field, and use WAI-ARIA (Accessible Rich Internet Applications) markup to ensure the highlighting is apparent to all users: [aria-invalid] { background-color: #ffeeee; } For the fields that need to be validated, assign a function to the form field’s onchange event handler that checks whether the field value is valid. If the value is invalid, pop up an alert with information about the error at the same time that you highlight the field: document.getElementById("elemid").onchange=validateField; ... function validateField() { // check for number if (isNaN(this.value)) { this.setAttribute("aria-invalid, "true"); generateAlert("You entered an invalid value for A. Only numeric values such as 105 or 3.54 are allowed"); } } For the fields that need a required value, assign a function to the field’s onblur event handler that checks whether a value has been entered: document.getElementById("field").onblur=checkMandator; ... function checkMandatory() { // check for data if (this.value.length == 0) { this.setAttribute("aria-invalid", "true"); generateAlert("A value is required in this field"); } } If any of the validation checks are performed as part of the form submission, make sure to cancel the submission event if the validation fails. 156 | Chapter 6: Preliminary Testing and Accessibility Discussion The WAI-ARIA (Accessible Rich Internet Applications) provides a way of marking cer‐ tain fields and behaviors in such a way that assistive devices do whatever is the equivalent behavior for people who need these devices. If a person is using a screen reader, setting the aria-attribute attribute to true (or adding it to the element) should trigger a visual warning in the screen reader—comparable to a color indicator doing the same for people who aren’t using assistive technologies. Read more on WAI-ARIA at the Web Accessibility Initiative at the W3C. I recommend using NV Access, an open source, freely avail‐ able screen reader, for testing whether your application is respond‐ ing as you think it should with a screen reader. In addition, the role attribute can be set to several values of which one, “alert”, triggers a comparable behavior in screen readers (typically saying out the field contents). Providing these cues are essential when you’re validating form elements. You can vali‐ date a form before submission and provide a text description of everything that’s wrong. A better approach, though, is to validate data for each field as the user finishes, so they’re not left with a lot of irritating error messages at the end. As you validate the field, you can ensure your users know exactly which field has failed by using a visual indicator. They shouldn’t be the only method used to mark an error, but they are an extra courtesy. If you highlight an incorrect form field entry with colors, avoid those that are hard to differentiate from the background. If the form background is white, and you use a dark yellow, gray, red, blue, green, or other color, there’s enough contrast that it doesn’t matter if the person viewing the page is color blind or not. In the example, I used a darker pink in the form field. I could have set the color directly, but it makes more sense to handle both updates— setting aria-invalid and changing the color—with one CSS setting. Luckily, CSS at‐ tribute selectors simplify our task in this regard. In addition to using color, you also need to provide a text description of the error, so there’s no question in the user’s mind about what the problem is. How you display the information is also an important consideration. None of us really like to use alert boxes, if we can avoid them. Alert boxes can obscure the form, and the only way to access the form element is to dismiss the alert with its error message. A better approach is to embed the information in the page, near the form. We also want to ensure the error message is available to people who are using assistive technologies, 6.5. Highlighting Errors Accessibly | 157 such as a screen reader. This is easily accomplished by assigning an ARIA alert role to the element containing the alert for those using screen readers or other AT devices. One final bonus to using aria-invalid is it can be used to discover all incorrect fields when the form is submitted. Just search on all elements where the attribute is present and if any are discovered, you know there’s still an invalid form field value that needs correcting. Example 6-1 demonstrates how to highlight an invalid entry on one of the form ele‐ ments, and highlight missing data in another. The example also traps the form submit, and checks whether there are any invalid form field flags still set. Only if everything is clear is the form submission allowed to proceed. Example 6-1. Providing visual and other cues when validating form fields Validating Forms



158 | Chapter 6: Preliminary Testing and Accessibility

If either of the validated fields is incorrect in the application, the aria-invalid attribute is set to true in the field, and an ARIA role is set to alert on the error message, as shown in Figure 6-8. When the error is corrected, the aria-invalid attribute is re‐ moved, as is the alert message. Both have the effect of changing the background color for the form field. 160 | Chapter 6: Preliminary Testing and Accessibility Figure 6-8. Highlighting an incorrect form field Notice in the code that the element wrapping the targeted form field is set to its correct state when the data entered is correct, so that when a field is corrected it doesn’t show up as inaccurate or missing on the next go-round. I remove the existing message alert regardless of the previous event, as it’s no longer valid with the new event. When the form is submitted, the application uses the querySelectorAll() method call to check for all instances of aria-invalid set to true, and rejects the submission until these are corrected, as shown in Figure 6-9: var badFields = document.querySelectorAll("[aria-invalid='true']"); You can also disable or even hide the correctly entered form elements, as a way to accentuate those with incorrect or missing data. However, I don’t recommend this ap‐ proach. Your users may find as they fill in the missing information that their answers in other fields are incorrect. If you make it difficult for them to correct the fields, they’re not going to be happy with the experience—or the company, person, or organization providing the form. Another approach you can take is to only do validation when the form is submitted. Many built-in libraries operate this way. Rather than check each field for mandatory or correct values as your users tab through, you only apply the validation rules when the form is submitted. This allows users who want to fill out the form in a different order to do so without getting irritating validation messages as they tab through. This ap‐ proach is a friendlier technique for those people using a keyboard, rather than a mouse, 6.5. Highlighting Errors Accessibly | 161 to tab through the form. Or you can use a mix of both: field-level validation for correct data type and format, form-level validation for required values. Figure 6-9. Attempting to submit a form with inaccurate form field entries Using JavaScript to highlight a form field with incorrect and missing data is only one part of the form submission process. You’ll also have to account for JavaScript being turned off, which means you have to provide the same level of feedback when processing the form information on the server, and providing the result on a separate page. It’s also important to mark if a form field is required ahead of time. Use an asterisk in the form field label, with a note that all form fields with an asterisk are required. Use the aria-required attribute to ensure this information is communicated to those using assistive devices. I also recommend using the HTML5 required attribute when using aria-required. See Also In Recipe 17.1 I cover form validation libraries and modules to simplify form validation. I also touch on using HTML5’s declarative form validation techniques. 162 | Chapter 6: Preliminary Testing and Accessibility 6.6. Creating an Accessible Automatically Updated Region Problem You have a section of a web page that is updated periodically, such as a section that lists recent updates to a file, or one that reflects recent Twitter activity on a subject. You want to ensure that when the page updates, those using a screen reader are notified of the new information. Solution Use WAI-ARIA region attributes on the element being updated:
Discussion A section of the web page that can be updated after the page is loaded, and without direct user intervention, calls for WAI-ARIA Live Regions. These are probably the sim‐ plest ARIA functionality to implement, and they provide immediate, positive results. And there’s no code involved, other than the JavaScript you need to create the page updates. Recipe 8.8 updates the web page based on the contents of a text file on the server that the application retrieves using Ajax. I modified the code that polls for the updates to check how many items have been added to the unordered list after the update. If the number is over 10, the oldest is removed from the page: // process return function processResponse() { if(xmlhttp.readyState == 4 && xmlhttp.status == 200) { var li = document.createElement("li"); var txt = document.createTextNode(xmlhttp.responseText); li.appendChild(txt); var ul = document.getElementById("update"); ul.appendChild(li); // prune top of list if (ul.childNodes.length > 10) { ul.removeChild(ul.firstChild); } } else if (xmlhttp.readyState == 4 && xmlhttp.status != 200) { console.log(xmlhttp.responseText); 6.6. Creating an Accessible Automatically Updated Region | 163 } } With this change, the list doesn’t grow overly long. I made one more change, adding the ARIA roles and states to the unordered list that serves as the updatable live region: