javascript intermediate

  1. JSON

  2. XMLHttpRequest - sync GET
  3. AJAX - async
  4. XMLHttpRequest - async POST

  5. closures, part 1
  6. closures, part 2
  7. closures, part 3
  8. closures, part 4 - as State Monad
  9. closures, part 5 - as Write Monad
  10. closures, part 6 - as Read Monad

  11. events, part 1
  12. events, part 2 - example
  13. events, part 3 - Buble vs Capture
  14. events, part 4 - prevent default actions
  15. events, part 5 - targets
  16. events, part 6 - some DOM object's events

JSON (JavaScript Object Notation)

JSON is built on two structures:

a collection of name/value pairs
in various languages, this is realized as
object | record | struct | dict | hash | keyed list | assoc array
an ordered list of values
in most languages, this is realized as
array | vector | list | sequence

in JSON, they take on these forms:

object - unordered set of name/value pairs. object begins with { (left brace) and ends with } (right brace). Each name is followed by : (colon) and the name/value pairs are separated by , (comma)


  { key1:value1, key2:value2, ... , keyN:valueN }

array - ordered collection of values. array begins with [ (left bracket) and ends with ] (right bracket). values are separated by , (comma)


  [ value1, value2, ... , valueN ]


  a value can be:
    string in double quotes
    number
    true or false
    null
    object
    array.

  these structures can be nested.

string - collection of zero or more Unicode characters, wrapped in double quotes, using backslash escapes. character is represented as a single character string. string is very much like a C string

number is very much like a C number, except that the octal and hexadecimal formats are not used

whitespace can be inserted between any pair of tokens

excepting a few encoding details, that completely describes the language:


  object
    {}
    { members }

  members
    pair
    pair , members

  pair
    string : value

  value
    string
    number
    object
    array
    true
    false
    null

  array
    []
    [ elements ]

  elements
    value
    value , elements

  string
    ""
    chars

  chars
    char
    char chars

  char
    any-Unicode-character                          (except " or \)
    control-character  "  \  /  \b  \f  \r  \t
    \u[0-9,a,b,c,d,f]{4} 

  number
    int
    int frac
    int exp
    int frac exp

  int
    digit
    digit1-9 digits
    -digit
    -digit1-9 digits

  frac
    .digits

  exp
    e digits

  digits
    digit
    digit digits

  e
    e
    e+
    e-
    E
    E+
    E-

JSON does not support objects with methods. attempting to convert such an object into JSON format will result in a TypeError exception

* * *

the native JSON object includes two methods:

string-to-object conversion


  JSON.parse (str)
  parses a JSON string, reconstructing the original JavaScript object.

example:


  js> var a = "{ \"key1\":false, \"key2\":\"mystring\" }";
  js> a
   "{ \"key1\":false, \"key2\":\"mystring\" }"
  js> typeof (a);
   "string"
  js> var x = JSON.parse (a);
  js> x
   ({key1:false, key2:"mystring"})
  js> typeof (x);
   "object"
  js>

object-to-string conversion


  JSON.stringify (obj)
  accepts a JavaScript object and returns its JSON equivalent.

example:


  js> var b = { key1:false, key2:"mystring" };
  js> b
   ({key1:false, key2:"mystring"})
  js> typeof (b);
   "object"
  js> var y = JSON.stringify (b);
  js> y
   "{\"key1\":false,\"key2\":\"mystring\"}"
  js> typeof (y);
   "string"
  js>


XMLHttpRequest - sync calls GET

the XMLHttpRequest object is used to exchange data with a server

to send a request to a server, we use the open () and send () methods of the XMLHttpRequest object

if you want to send information with the GET method, add the information to the URL:


  var data = "somedatahere";
  var hReq = new XMLHttpRequest ();

  hReq.open ("GET", "some.uri.string/?param=" + data, false);
  hReq.send (null);
  var serverResponse = hReq.responseText;

the sequence begins by creating a new instance of XMLHttpRequest. then prepares a call:

  "GET"      signifies the request method to be used.
  "false"    says the call is synchronous.

you can reuse an XMLHttpRequest object. just make sure it's either delivered a full response or it's been reset using the abort () method


AJAX - async calls

AJAX stands for Asynchronous JavaScript and XML, and for the XMLHttpRequest object to behave as AJAX, the async parameter of the open () method has to be set to true

in practice, XMLHttpRequest calls should almost always be asynchronous. that means the browser and the user can continue working on other things while waiting for a response to come back

how will browser know when the response is ready?

the readyState property always reflects the current point in the call's lifecycle. when the object is born, it's at 0. after open () has been called, it's 1. this continues until the response is back, at which point the value is 4

so, to catch the response, you need to watch for a readyState of 4. that's an easy chore, because XMLHttpRequest fires readystatechange events. you can declare a callback function using the onreadystatechange field. the callback will then receive all state changes. the states below 4 aren't especially useful and are somewhat inconsistent across browser types anyway. so most of the time, all we're interested in is state 4, when the response is complete

based on all that, here's an asynchronous version of the code:


  function onSumResponse () {
    if (hReq.readyState != 4) { return; }
    var serverResponse = hReq.responseText;
    ...
  }

  var data = "somedatahere";
  var hReq = new XMLHttpRequest ();

  hReq.open ("GET", "some.url.to.server/?param=" + data, true);
  hReq.onreadystatechange = onSumResponse;
  hReq.send (null);

you declare the callback method in XMLHttpRequest's onreadystatechange property. in addition, the third argument of open () is now true. this argument is actually called the asynchronous flag, which explains why we're now setting it to true

JavaScript also supports closures - a form of anonymous functions - which suggests a more concise boilerplate structure for asynchronous calls:


  var hReq = new XMLHttpRequest ();

  hReq.open ("GET", "some.url.addr?with=params", true);

  hReq.onreadystatechange = function () {
    if (hReq.readyState != 4) { return; }
    var serverResponse = hReq.responseText;
  ...
  };

  hReq.send (null);


XMLHttpRequest - async POST


  var data = JSON.stringify (some_object);
  var hReq = new XMLHttpRequest ();

  hReq.open ("PUT", "some.uri.to.server", true);

  hReq.setRequestHeader ("Content-Type", "application/json");
  hReq.setRequestHeader ("Content-length", data.length);
  hReq.setRequestHeader ("Connection", "close");

  hReq.onreadystatechange = function () {
    if (hReq.readyState == 4 && hReq.status == 200 ) {
      x = JSON.parse (hReq.responseText);
      ...
    }
  }

  hReq.send (data);

GET is simpler and faster than POST, and can be used in most cases. however, always use POST requests when:

- a cached file is not an option (update a file or database on the server)
- sending a large amount of data to the server (POST has no size limitations)
- sending user input (POST is more robust and secure than GET)


closures, part 1

closure is an expression (typically - function) that can have free variables together with an environment that binds those variables

example:


  /*     file clos1.js      */

  function outer (outerVar) {
    var inner = function (innerVar) {
      var x = innerVar + outerVar;
      return x;
    }
    return inner;
  }

  var a = outer (5);

  print (a (4));

  print (typeof (outerVar));
  print (typeof (innerVar));

  print (a (100));


when 'outer' code has evaluated and died then 'inner' stay alive and may be called later and variable 'outerVar' will be available to 'inner'

watch:


  $> js clos0.js
   9
   undefined
   undefined
   105
  $>


closures, part 2


  //   file clos2.js

  var x = function () {
       var y = ["Alice", "Bob", "Charly"];

       return function (n) { return y[n]; };
  } ();

  print (x);
  print (x (1));

a function object x contains:
- a function (parameters and body)
- a reference to the environment in which it was created (context)

so, closure = function + variables from context

now:


  $> js clos2.js
   function (n) {
     return y[n];
   }
   Bob
  $>


closures, part 3

this example shows that each call creates a separate closure for the local variables. there is not a single closure per function declaration - there is a closure for each call to a function


  //  file clos3.js

  function c (k, z) {
    var a = [1,2];

    return function (x) {
      k += x;
      a.push (k);
      print ( "name: "       +   z.name        +
             "\nnumber: "   +   k             +
             "\tarray: "    +   a.toString ()    );
    }
  }


  $> js
  js> load ("clos3.js");
   function load () {[native code]}
  js> var t1 = c (10,{"name" : "alfa"});
  js> var t2 = c (20,{"name" : "beta"});
  js> t1 (3);
   name: alfa
   number: 13      array: 1,2,13
  js> t2 (4);
   name: beta
   number: 24      array: 1,2,24
  js> t1 (100);
   name: alfa
   number: 113     array: 1,2,13,113
  js> t2 (200);
   name: beta
   number: 200     array: 1,2,24,224
  js> quit ();
  $>


closures, part 4 - as State Monad

using closures we can emulate immutable variables of pure fp. not in full scale, but... so we have variable, which can be changed from any place of the script, but not by any code

closure as State Monad

example - simpliest FSM:

         -0- .      . -1-
        |    |      |    |
        . -> F -1-> T <- . 
             |      |
              <-0-  .


  //   file clos4.js

  function automata (st, id) {
    var s = { "state" : st, "name" : id };

    return function (x) {
      return (
        (x == 0) ? s.state = false :
        (x == 1) ? s.state = true  :
                   s                 );
    }
  }

and now:


  js> load ("clos4.js");
   function load () {[native code]}
  js> var a = automata (true, "one");
  js> var b = automata (true, "two");
  js> [1,1,0,1].map (a);
   [true, true, false, true]
  js> [1,1,1,0].map (b);
   [true, true, true, false]
  js> a (3);
   ({state:true, name:"one"})
  js> b (4);
   ({state:false, name:"two"})
  js>


closures, part 5 - as Write Monad

closure as Write Monad

example - simpliest operations' log:


  //   file  clos5.js

  function log () {
    var mylog = "";
    var sum   = 0;

    return function (x) {
      sum  += x;
      mylog = mylog + "adding " + x + "\n";
      return [sum, mylog];
    }
  }

now:


  js> load ("clos5.js");
   function load () {[native code]}
  js> var a = log ();
  js> a (1);
   [1, "adding 1\n"]
  js> a (2);
   [3, "adding 1\nadding 2\n"]
  js> var x = a (3);
  js> print (x[1]);
   adding 1
   adding 2
   adding 3

  js>


closures, part 6 - as Read Monad

closure as Read Monad


  //   file clos6.js

  function param () {
    var minVal = 10;
    var maxVal = 90;

    return function (x) { return (x == 0) ? minVal : maxVal; }
  }

and now:


  js> load ("clos6.js");
   function load () {[native code]}
  js> var p = param ();
  js> var f = function (x,y,z) { k=x+y; return k>z (0) && k<z (1); };
  js> f (3, 4, p);
   false
  js> f (13, 14, p);
   true
  js> f (13, 114, p);
   false
  js>


events, part 1

event handlers may be attached to various elements in the DOM with method addEventListener. when an event occurs, an event object is created by browser and passed sequentially to the event listeners that are allowed to handle the event

the DOM Event interface is accessible from within the handler function, via the event object passed as the first argument. once you have a reference to the event object, you can access all of its properties and methods

DOM also provides method removeEventListener for detaching event listeners

the function that handles the event is passed the event object as the first argument. you can give this parameter any name you wish in the event handler, but 'e', 'evt' or 'event' are usually used. you can pass other parameters to the event handling function as well, if required

simple example:


  <html>
  <head>
  <script type="text/javascript">

    function foo (evt){
      alert ( " X coordinate : " + evt.clientX +
             " Y coordinate : " + evt.clientY );
      this.removeEventListener ('click', foo);
    }

    document.addEventListener ('click', foo, false);

  </script>
  </head>
  <body>
  to display the mouse coordinates click anywhere on the page.
  </body>
  </html>


Properties


event.timeStamp
the time that the event was created

event.target
a reference to the target to which the event was originally dispatched

event.currentTarget
a reference to the currently registered target for the event

event.relatedTarget
identifies a secondary target for the event

event.defaultPrevented
indicates whether or not event.preventDefault () has been called on the event

event.bubbles
a boolean indicating whether the event bubbles up through the DOM or not

event.cancelable
a boolean indicating whether the event is cancelable

event.clientX
the horizontal position of the event

event.clientY
the vertical position of the event

event.layerX
the horizontal coordinate of the event relative to the current layer

event.layerY
the vertical coordinate of the event relative to the current layer

event.screenX
the horizontal position of the event on the screen

event.screenY
the vertical position of the event on the screen

event.pageX
the horizontal coordinate of the event relative to the page

event.pageY
the vertical coordinate of the event relative to the page

event.charCode
the Unicode value of a character key that was pressed as part of a keypress event

event.isChar
a boolean indicating whether the event produced a key character or not

event.keyCode
the Unicode value of a non-character key in a keypress event or any key in any other type of keyboard event

event.ctrlKey
a boolean indicating whether the <ctrl> key was pressed during the event

event.altKey
a boolean indicating whether the <alt> key was pressed when the event was raised

event.shiftKey
a boolean indicating whether the <shift> key was pressed when the event was fired

event.metaKey
a boolean indicating whether the meta key was pressed during the event

event.which
the Unicode value of a key in a keyboard event, regardless of which type of key is pressed

event.button
a value indicating which, if any, mouse button (s) were pressed when the event was raised

event.type
the name of the event (case-insensitive)

event.detail
detail about the event, depending on the type of event

event.eventPhase
indicates which phase of the event flow is being processed


Methods



event.preventDefault
cancels the event (if it is cancelable)

event.stopPropagation
stops the propagation of events further along in the DOM


events, part 2 - example


  <html>

  <head>

  <meta http-equiv="Content-Type"  content="text/html;charset=utf-8">
  <meta http-equiv="Cache-Control" content="no-store">
  <meta http-equiv="Cache-Control" content="no-cache">

  <style>

    body {
       color: blue;
       background-color:black;
    }
    .parag {
      margin:70;
      padding:10;
      background:white;
      width:500;
      text-align:center;
    }

  </style>

  </head>

  <body>

  <p id="id1" class="parag">click on me!
  <br>paint all in green
  <p id="id2" class="parag">click on me!
  <br>paint all in red and don't tell parents about it!
  <p id="id3" class="parag">click on me!
  <br>paint all in yellow and cancel this event

  </body>

  <script>

    myObj = {
      fun:function (e) {
        switch (e.target.id) {
          case "id1" :
            myColor = "green";
            break;
          case "id2" :
            myColor = "red";
            e.stopPropagation ();
            break;
          case "id3" :
            myColor = "yellow";
            e.target.innerHTML = "click on me. don't paint now : (";
            e.target.removeEventListener ("click", myObj.fun, false);
            break;
          default    :
            break;
        }
        document.body.style.backgroundColor = myColor;
      }
    }

    var elem0 = window;
    var elem1 = document.getElementById ("id1");
    var elem2 = document.getElementById ("id2");
    var elem3 = document.getElementById ("id3");

    elem0.addEventListener ("click",
      function () { alert ("hello from Parent")},
      false);
    elem1.addEventListener ("click", myObj.fun, false);
    elem2.addEventListener ("click", myObj.fun, false);
    elem3.addEventListener ("click", myObj.fun, false);

  </script>

  </html>


events, part 3 - Buble vs Capture

if third parameter of addEventListener is true then event will trigger on Capture phase

if third parameter of addEventListener is false then event will trigger on Buble phase

example:


  <html>
  <head>
  <style type="text/css">

    body { background:gray; }
    .number { background:blue; margin:10; padding:1; text-align:center; width:200; }

  </style>

  <script type="text/javascript">

    function highlightMe (x) {
        x.style.backgroundColor = 'yellow';
        alert ("event on element " + x.id); 
        x.style.backgroundColor = '';
    }

  </script>
  <head>

  <body>

  <fieldset border=2 title="Bubbling">  <legend>Bubbling</legend>
  <div id="bubl1"> <p class="number"> element 1 </p>
    <div id="bubl2"> <p class="number"> element 2 </p>
      <div id="bubl3"> <p class="number"> element 3 </p> </div>
    </div>
  </div>
  </fieldset>

  <p>

  <fieldset border=2 title="Capturing">  <legend>Capturing</legend>
  <div id="capt1"> <p class="number"> element I </p>
    <div id="capt2"> <p class="number"> element II </p>
      <div id="capt3"> <p class="number"> element III </p> </div>
    </div>
  </div>
  </fieldset>

  </body>

  <script>

    function highlightMe2 (e) { highlightMe (e.currentTarget); }

    document.getElementById ("bubl1").addEventListener ("click", highlightMe2, false); 
    document.getElementById ("bubl2").addEventListener ("click", highlightMe2, false);
    document.getElementById ("bubl3").addEventListener ("click", highlightMe2, false);

    document.getElementById ("capt1").addEventListener ("click", highlightMe2, true);
    document.getElementById ("capt2").addEventListener ("click", highlightMe2, true);
    document.getElementById ("capt3").addEventListener ("click", highlightMe2, true);

  </script>
  </html>


events, part 4 - prevent default actions

return false from event handler prevents browser's default action. but others event handlers for this element will start anywhere

example:


  <html>

  <body>
    <a id=1 href="http://ya.ru">click here</a>
  </body>

  <script>
    document.getElementById ("1").onclick = function (evt) {
      return confirm ("going to " + evt.target + ". are you sure?");
    }
  </script>

  </html>

sometimes the browser's default action may be blocked in event handler

some default actions take place before event handler's call and so they cannot be blocked (onfocus - as example)

transfer by href takes place after event handler's activation, so it can be canceled:


  <html>

  <body>
    <div id=0>
      <a id=1 href="http://ya.ru">click here</a>
    </div>
  </body>

  <script>
    document.getElementById ("0").onclick = function (ev) {
      alert ("fm parent, type : " + ev.type);
    }

    document.getElementById ("1").onclick = function (ev) {
      alert ("reference : " + ev.target);
      event.preventDefault ();
    }
  </script>

  </html>


events, part 5 - targets

here are three targets to be considered:

event.target
the DOM element that triggered this event
event.currentTarget
the DOM element whose event listeners are currently being processed. as the event capturing and bubbling occurs this value changes

event.relatedTarget
MouseEvent only. identifies a secondary target for the event

the relatedTarget property for the mouseover event holds the node that the mouse was previously over. for the mouseout event, it holds the node that the mouse moved to

event typeevent.target event.currentTargetevent.relatedTarget
mouseoverthe elem which mouse enteredthe elem which mouse exited
mouseoutthe elem which mouse exitedthe the elem which mouse entered

example:


  <html>
  <head>
  <style type="text/css">
    body  { background:gray; }
    .one  { background:blue; text-align:center; width:200; }
    .zero { background:red;  text-align:center; width:200; padding:30; }
  </style>
  <head>

  <body>
    <div id="0" class="zero"> element zero
      <p id="1" class="one" > element one </p>
    </div>
  </body>

  <script>
    function func (x) {
      alert ("mouse on element with id " + x.id + " now");
    }

    function fOver (e) {
      alert ("event " + e.type + " on element " + e.target.className);
      func (e.currentTarget);
    }

    function fOut (e) {
      alert ("event " + e.type + " on element " + e.target.className);
      func (e.relatedTarget);
    }

    document.getElementById ("1").addEventListener ("mouseover", fOver, false);
    document.getElementById ("1").addEventListener ("mouseout",  fOut,  false);
  </script>
  </html>


events, part 6 - some DOM object's events

Window events:

load
invoked when the page has been loaded

Input Text events:


change
invoked when an element lost the focus since it was changed

select
invoked if some text is selected when input lost focus

Form events

reset
invoked when the user reset the form

submit
invoked when the user submitted the form

Input Button events

click
invoked when the user clicked the button

blur
invoked when focus away from

focus
invoked when focus to

Select events

change
invoked when a new option is selected

example:


  <html>

  <body onload="alert ('hi');">
    <input type=text id=1 width=16/>
    <p>
    <select id="2">
      <option>one</optiotn>
      <option>two</optiotn>
    </select>
    <p>
    <input type=button value=ok id=3>
    </div>
  </body>

  <script>
    function func1 (x) { alert (this.value); }
    function func2 (x) { alert (document.getSelection ()); }

    document.getElementById ("1").addEventListener ("select", func2, false);
    document.getElementById ("2").addEventListener ("change", func1, false);
    document.getElementById ("3").addEventListener ("click", func1, false);
  </script>

  </html>