Book Image

Object-Oriented JavaScript - Third Edition

By : Ved Antani, Stoyan STEFANOV
5 (1)
Book Image

Object-Oriented JavaScript - Third Edition

5 (1)
By: Ved Antani, Stoyan STEFANOV

Overview of this book

JavaScript is an object-oriented programming language that is used for website development. Web pages developed today currently follow a paradigm that has three clearly distinguishable parts: content (HTML), presentation (CSS), and behavior (JavaScript). JavaScript is one important pillar in this paradigm, and is responsible for the running of the web pages. This book will take your JavaScript skills to a new level of sophistication and get you prepared for your journey through professional web development. Updated for ES6, this book covers everything you will need to unleash the power of object-oriented programming in JavaScript while building professional web applications. The book begins with the basics of object-oriented programming in JavaScript and then gradually progresses to cover functions, objects, and prototypes, and how these concepts can be used to make your programs cleaner, more maintainable, faster, and compatible with other programs/libraries. By the end of the book, you will have learned how to incorporate object-oriented programming in your web development workflow to build professional JavaScript applications.
Table of Contents (25 chapters)
Object-Oriented JavaScript - Third Edition
Credits
About the Authors
About the Reviewer
www.PacktPub.com
Customer Feedback
Preface
Built-in Functions
Regular Expressions

Chapter 7, The Browser Environment


Lets practice the following exercise:

Exercises

  1. The title clock program is as follows:

            setInterval(function () { 
              document.title = new Date().toTimeString(); 
            }, 1000); 
    
  2. To animate resizing of a 200 x 200 pop up to 400 x 400, use the following code:

            var w = window.open( 
                'http://phpied.com', 'my', 
                 'width = 200, height = 200'); 
     
            var i = setInterval((function () { 
              var size = 200; 
              return function () { 
                size += 5; 
                w.resizeTo(size, size); 
                if (size === 400) { 
                  clearInterval(i); 
                } 
              }; 
            }()), 100); 
    

    Every 100 ms (1/10th of a second) the pop-up size increases by five pixels. You keep a reference to the interval i so you can clear it once done. The variable size tracks the pop-up size (and why not keep it private inside a closure).

  3. The earthquake program is as follows:

           var i = setInterval((function () { 
              var start = +new Date(); // Date.now() in ES5 
              return function () { 
                w.moveTo( 
                  Math.round(Math.random() * 100), 
                  Math.round(Math.random() * 100)); 
                if (new Date() - start > 5000) { 
                  clearInterval(i); 
                } 
              }; 
             }()), 20); 
    

    Try all of them, but using requestAnimationFrame() instead of setInterval().

  4. A different walkDOM() with a callback is as follows:

            function walkDOM(n, cb) { 
              cb(n); 
              var i, 
                  children = n.childNodes, 
                  len = children.length, 
                  child; 
              for (i = 0; i < len; i++) { 
              child = n.childNodes[i]; 
                if (child.hasChildNodes()) { 
                  walkDOM(child, cb); 
                } 
              } 
            } 
    

    Testing:

            > walkDOM(document.documentElement,
            console.dir.bind(console)); 
           html 
           head 
           title 
           body 
           h1 
           ... 
    
  5. To remove content and clean up functions, use the following code:

            // helper 
            function isFunction(f) { 
              return Object.prototype.toString.call(f) === 
                "[object Function]"; 
            } 
     
            function removeDom(node) { 
              var i, len, attr; 
     
              // first drill down inspecting the children 
              // and only after that remove the current node 
              while (node.firstChild) { 
                removeDom(node.firstChild); 
              } 
     
              // not all nodes have attributes, e.g. text nodes don't 
              len = node.attributes ? node.attributes.length : 0; 
     
              // cleanup loop 
              // e.g. node === <body>,  
              // node.attributes[0].name === "onload" 
              // node.onload === function()... 
              // node.onload is not enumerable so we can't use  
              // a for-in loop and have to go the attributes route 
              for (i = 0; i < len; i++) { 
                attr = node[node.attributes[i].name]; 
                if (isFunction(attr)) { 
                  // console.log(node, attr); 
                  attr = null; 
                } 
              } 
     
              node.parentNode.removeChild(node); 
            } 
    

    Testing:

            > removeDom(document.body); 
    
  6. To include scripts dynamically, use the following code:

            function include(url) { 
              var s = document.createElement('script'); 
              s.src = url; 
              document.getElementsByTagName('head')[0].
              appendChild(s); 
            } 
    

    Testing:

            > include("http://www.phpied.com/files/jinc/1.js"); 
            > include("http://www.phpied.com/files/jinc/2.js"); 
    
  7. Events: The event utility program is as follows:

            var myevent = (function () { 
     
              // wrap some private stuff in a closure 
              var add, remove, toStr = Object.prototype.toString; 
     
              // helper 
              function toArray(a) { 
                // already an array 
                if (toStr.call(a) === '[object Array]') { 
                  return a; 
               } 
         
                // duck-typing HTML collections, arguments etc 
                var result, i, len; 
                if ('length' in a) { 
                  for (result = [], i = 0, len = a.length; i < len; i++)
                  { 
                    result[i] = a[i]; 
                  } 
                  return result; 
               } 
     
                // primitives and non-array-like objects 
                // become the first and single array element 
                return [a]; 
              } 
     
              // define add() and remove() depending 
              // on the browser's capabilities 
              if (document.addEventListener) { 
                add = function (node, ev, cb) { 
                  node.addEventListener(ev, cb, false); 
                }; 
                remove = function (node, ev, cb) { 
                  node.removeEventListener(ev, cb, false); 
                }; 
              } else if (document.attachEvent) { 
                add = function (node, ev, cb) { 
                  node.attachEvent('on' + ev, cb); 
                }; 
                remove = function (node, ev, cb) { 
                  node.detachEvent('on' + ev, cb); 
                }; 
              } else { 
                add = function (node, ev, cb) { 
                  node['on' + ev] = cb; 
                }; 
                remove = function (node, ev) { 
                  node['on' + ev] = null; 
                }; 
              } 
     
              // public API 
              return { 
     
                addListener: function (element, event_name, callback) { 
                  // element could also be an array of elements 
                  element = toArray(element); 
                  for (var i = 0; i < element.length; i++) { 
                    add(element[i], event_name, callback); 
                  } 
                }, 
     
               removeListener: function (element, event_name, callback) { 
                  // same as add(), only practicing a different loop 
                  var i = 0, els = toArray(element), len = els.length; 
                 for (; i < len; i++) { 
                    remove(els[i], event_name, callback); 
                  } 
               }, 
     
                getEvent: function (event) { 
                  return event || window.event; 
                }, 
      
                getTarget: function (event) { 
                  var e = this.getEvent(event); 
                  return e.target || e.srcElement; 
                }, 
     
                stopPropagation: function (event) { 
                  var e = this.getEvent(event); 
                  if (e.stopPropagation) { 
                    e.stopPropagation(); 
                  } else { 
                    e.cancelBubble = true; 
                  } 
                }, 
     
                preventDefault: function (event) { 
                  var e = this.getEvent(event); 
                  if (e.preventDefault) { 
                    e.preventDefault(); 
                  } else { 
                    e.returnValue = false; 
                  } 
                } 
     
              }; 
            }()); 
    

    Testing: Go to any page with links, execute the following, and then click any link:

            function myCallback(e) { 
              e = myevent.getEvent(e); 
              alert(myevent.getTarget(e).href); 
              myevent.stopPropagation(e); 
              myevent.preventDefault(e); 
            } 
            myevent.addListener(document.links, 'click', myCallback); 
    
  8. Move a div around with the keyboard using the following code:

            // add a div to the bottom of the page 
            var div = document.createElement('div'); 
            div.style.cssText = 'width: 100px; height:
             100px; background: red; position: absolute;'; 
            document.body.appendChild(div); 
     
            // remember coordinates 
            var x = div.offsetLeft; 
            var y = div.offsetTop; 
     
            myevent.addListener(document.body, 
             'keydown', function (e) { 
             // prevent scrolling 
              myevent.preventDefault(e); 
     
              switch (e.keyCode) { 
                case 37: // left 
                  x--; 
                  break; 
                case 38: // up 
                  y--; 
                  break; 
                case 39: // right 
                  x++; 
                  break; 
                case 40: // down 
                  y++; 
                  break; 
                default: 
                  // not interested 
              } 
     
              // move 
              div.style.left = x + 'px'; 
              div.style.top  = y + 'px'; 
     
            }); 
    
  9. Your own Ajax utility:

            var ajax = { 
              getXHR: function () { 
                var ids = ['MSXML2.XMLHTTP.3.0', 
                 'MSXML2.XMLHTTP', 'Microsoft.XMLHTTP']; 
                var xhr; 
                if (typeof XMLHttpRequest === 'function') { 
                  xhr = new XMLHttpRequest(); 
                } else { 
                  // IE: try to find an ActiveX object to use 
                  for (var i = 0; i < ids.length; i++) { 
                    try { 
                      xhr = new ActiveXObject(ids[i]); 
                      break; 
                    } catch (e) {} 
                  } 
                } 
                return xhr; 
     
              }, 
              request: function (url, method, cb, post_body) { 
                var xhr = this.getXHR(); 
                xhr.onreadystatechange = (function (myxhr) { 
                  return function () { 
                    if (myxhr.readyState === 4 && myxhr.status === 200) { 
                      cb(myxhr); 
                    } 
                  }; 
                }(xhr)); 
                xhr.open(method.toUpperCase(), url, true); 
                xhr.send(post_body || ''); 
              } 
            }; 
    

    When testing, remember that same origin restrictions apply, so you have to be on the same domain. You can go to http://www.phpied.com/files/jinc/, which is a directory listing and then test in the console:

            function myCallback(xhr) { 
              alert(xhr.responseText); 
            } 
            ajax.request('1.css', 'get', myCallback); 
            ajax.request('1.css', 'post', myCallback,
             'first=John&last=Smith'); 
    

    The result of the two is the same, but if you look into the Network tab of the Web Inspector, you can see that the second is indeed a POST request with a body.