Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 8 Next »

Callbacks for events on webpages

Closures are extremely useful when callbacks are needed for event handling.

Consider the example from MDN of changing text size on a webpage at the press of a button.

First we define the CSS

css snippet
body {
  font-family: Helvetica, Arial, sans-serif;
  font-size: 12px;
}

h1 {
  font-size: 1.5em;
}

h2 {
  font-size: 1.2em;
}

In the example the interactive text size buttons can change the font-size property of the body element, and the adjustments will be picked up by other elements on the page thanks to the relative units.

The javascript to do this is shown here

example js code
function makeSizer(size) {
  return function() {
    // This changes the font size of the entire <body> not to be confused with <header> 
    document.body.style.fontSize = size + 'px';
  };
}

var size12 = makeSizer(12);
var size14 = makeSizer(14);
var size16 = makeSizer(16);


document.getElementById('size-12').onclick = size12;
document.getElementById('size-14').onclick = size14;
document.getElementById('size-16').onclick = size16;

The callbacks are setup on lines 13-15.

The HTML that this script manipulates is shown here

example html
    <p>Some paragraph text</p>
    <h1>some heading 1 text</h1>
    <h2>some heading 2 text</h2>

    <a href="#" id="size-12">12</a>
    <a href="#" id="size-14">14</a>
    <a href="#" id="size-16">16</a>

Do not confuse <h1>, <h2> etc with <header>.  <h1> through to <h6> are termed heading elements and are part of the <body>.  So changing the style of the body, changes the style of all child elements.

Public and Private functions - the Module pattern

Javascript doesn't naturally support the idea of public and private functions but using closures we can quite easily emulate the idea.

Consider the following snippet of code

example js code
var counter = (function() {
  var privateCounter = 0;
  function changeBy(val) {
    privateCounter += val;
  }
  return {
    increment: function() {
      changeBy(1);
    },
    decrement: function() {
      changeBy(-1);
    },
    value: function() {
      return privateCounter;
    }
  };   
})();

console.log(counter.value()); // logs 0
counter.increment();
counter.increment();
console.log(counter.value()); // logs 2
counter.decrement();
console.log(counter.value()); // logs 1

The return statement at line 6 returns a set of named closures.  What is interesting an annoying is that line 3 breaks javascripts own rules on scope.  If you create a variable without the var keyword it has global scope, with var it has local scope.  For the closure changeBy it has local scope it would seem.  So let's try an experiment - 

If we change the above to the following

example js code
var counter = (function() {
  var privateCounter = 0;
  bb = function changeBy(val) {
    privateCounter += val;
  }
  bb(3);
  return {
    increment: function() {
      changeBy(1);
    },
    decrement: function() {
      changeBy(-1);
    },
    value: function() {
      return privateCounter;
    }
  };   
})();

alert(counter.value()); // logs 0
counter.increment();
counter.increment();
alert(counter.value()); // logs 2
counter.decrement();
alert(counter.value()); // logs 1

The calls to changeBy (lines 9 and 12) all fail, the exception thrown is that changeBy is undefined.  How strange is that?  I'm not sure what the lexical rules are but what I've observed are 

Lexical rules through observation

  • within a lexical environment, a value assigned to a variable that s not prefixed with var is globally available, so it pollutes the global namespace
  • within a lexical environment, a value assigned to a var variable is not globally available
  • within a lexical environment, a function that is assigned to variable (with or without var) is not available outside of the lexical environment unless it is returned using the return keyword



  • No labels