Archive
JS 101 Week 5: Event handling
2011-02-23
Reflection
Why is it better to use either trickling or bubbling of events? Why not just give the event to the node on which it was invoked?
There are two main reasons to use the trickling down or bubbling up of events. The first is performance. If we have 100 different images on a page and we want each one to respond to the hover
event, it is very inefficient to create 100 different event listeners and attach them to each element. We can instead create a single hover event listener on the <div>
which contains all of these pictures, and from within that event handler, determine which of its enclosed <img>
elements was clicked. Additionally, when the event bubbles up the parent chain, the programmer is free to do interesting things like change the style of entire subtrees as a result of an event listener lower down in the hierarchy.
Can you think of one situation where you would want to prevent the browser from submitting a form after a user has clicked on the ‘submit’ button? How would you achieve this?
One example would be if we had some client side validation we wanted to take place before submitting a form to the server. For instance, we might want to validate that all the form fields are filled out, or that the values entered into these fields have a certain format (e.g. mm/dd/yyyy format for dates). When the form submit button is clicked and the validation routine takes place, if there is an error that function can call the preventDefault
method on the corresponding event object.
Homework
12.1 of Eloquent Javascript
Write a function asHTML which, when given a DOM node, produces a string representing the HTML text for that node and its children. You may ignore attributes, just show nodes as . The escapeHTML function from chapter 10 is available to properly escape the content of text nodes.
Hint: Recursion!
function isTextNode(node) { return node.nodeType == 3; } function asHTML(node) { // we’re done recursing if (node.childNodes.length === 0) { if (isTextNode(node)) { return node.nodeValue; // This is unavailable in the jsFiddle environment //return escapeHTML(node.nodeValue); } else { return "<" + node.nodeName + ">"; } } var returnString = "<" + node.nodeName + ">"; for (var i = 0; i < node.childNodes.length; i++) { returnString += asHTML(node.childNodes[i]) + "\n"; } return returnString; } alert(asHTML(document.body));
12.2 of Eloquent Javascript
Write the convenient function removeElement which removes the DOM node it is given as an argument from its parent node.
function removeElement(node) { node.parentNode.removeChild(node); }
13.1 of Eloquent Javascript
Write a function called registerEventHandler to wrap the incompatibilities of these two models. It takes three arguments: first a DOM node that the handler should be attached to, then the name of the event type, such as “click” or “keypress”, and finally the handler function.
To determine which method should be called, look for the methods themselves ― if the DOM node has a method called attachEvent, you may assume that this is the correct method. Note that this is much preferable to directly checking whether the browser is Internet Explorer. If a new browser arrives which uses Internet Explorer’s model, or Internet Explorer suddenly switches to the standard model, the code will still work. Both are rather unlikely, of course, but doing something in a smart way never hurts.
// eventname is the name of the event without the ‘on’ prefix. e.g. // to register for a click event, the eventname value should be "click" function registerEventHandler(node, eventname, handler) { // node has an attachEvent method; use that. This is how ie works if (node.attachEvent) { node.attachEvent("on" + eventname, handler); } // Mozilla model else if (node.addEventListener) { // false - use bubble up rather than trickle down node.addEventListener(eventname, handler, false); } else { node["on" + eventname] = handler; } }
JSFiddle example – when mouse enters element, it becomes bold. After it leaves, it becomes normal
Create an HTML page and some Javascript to allow a user to add n numbers.
First display a simple form with the question “How many numbers do you want to add (max is 10)”. The user should enter a number between 2 to 10 and click on a button in the form. You have to validate the answer. If the user has entered a correct value (between 2 and 10), then dynamically create a form with n text input fields and an “Add” button. Once the form is displayed the user will enter n numbers in the n input fields and when they click on the “Add” button, dynamically create a span element with the result. You will have to perform validation on the values entered in the input fields to make sure that they are numbers. If they are not numbers, display an alert dialogue with an error message.
JSFiddle solution
/*#p2pu-Jan2011-javascript101*/
EventBus – how to switch EventService implementations for unit testing
I’ve written previously about EventBus, a great open source Java library for pub-sub (publish subscribe). It’s a truly excellent way to write loosely coupled systems, and much preferable to having to make your domain models extends Observable and your listeners implement Observer. I’m writing today to describe some difficulties in incorporating EventBus into unit tests, and how to overcome that problem.
Test setup
I was attempting to test that certain messages were being published by a domain model object when they were supposed to. In order to test this, I wrote a simple class that did nothing more than listen to the topics I knew that my model object was supposed to publish to, and then increment a counter when these methods were called. It looked something like this:
class EventBusListener { private int numTimesTopicOneCalled = 0; private int numTimesTopicTwoCalled = 0; public EventBusListener() { AnnotationProcessor.process(this); } @EventTopicSubscriber(topic="topic_one") public void topicOneCalled(String topic, Object arg) { this.numTimesTopicOneCalled++; } @EventTopicSubscriber(topic="topic_two") public void topicTwoCalled(String topic, Object arg) { this.numTimesTopicTwoCalled++; } public int getNumTimesTopicOneCalled() { return this.numTimesTopicOneCalled; } public int getNumTimesTopicOneCalled() { return this.numTimesTopicTwoCalled; } }
The basic test routine looked something like this:
@Test public void testTopicsFired() { // Uses EventBus internally DomainObject obj = new DomainObject(); int count = 10; EventBusListener listener = new EventBusListener(); for (int i = 0; i < count; i++) { obj.doSomethingThatShouldFireEventBusPublishing(); } assertEquals(count, listener.getNumTimesTopicOneCalled()); assertEquals(count, listener.getNumTimesTopicTwoCalled()); }
This code kept failing, but in nondeterministic ways – sometimes the listener would report having its topic one called 4 times instead of 10, sometimes 7, but never the same issue twice. Stepping through the code in debug mode I saw that the calls to EventBus.publish
were in place, and sometimes they worked. Nondeterminism like this made me think of a threading issue, so I began to investigate.
Problem
After reading through the EventBus javadoc, I came upon the root of the problem:
The EventBus is really just a convenience class that provides a static wrapper around a global EventService instance. This class exists solely for simplicity. Calling EventBus.subscribeXXX/publishXXX is equivalent to EventServiceLocator.getEventBusService().subscribeXXX/publishXXX, it is just shorter to type. See EventServiceLocator for details on how to customize the global EventService in place of the default SwingEventService.
And from the SwingEventService javadoc (emphasis mine):
This class is Swing thread-safe. All publish() calls NOT on the Swing EventDispatchThread thread are queued onto the EDT. If the calling thread is the EDT, then this is a simple pass-through (i.e the subscribers are notified on the same stack frame, just like they would be had they added themselves via Swing addXXListener methods).
Here’s the crux of the issue: the EventBus.publish calls are not occurring on the EventDispatchThread, since the Unit testing environment is headless and this domain object is similarly not graphical. Thus these calls are being queued up using SwingUtilities.invokeLater
, and they have no executed by the time the unit test has completed. This leads to the non-deterministic behavior, as a certain number of the queued up messages are able to be processed before the end of execution of the unit test, but not all of them.
Solutions
Sleep Hack
One solution, albeit a terrible one, would be to put a hack in:
@Test public void testTopicsFired() { // same as before // Let the messages get dequeued try { Thread.sleep(3000); } catch (InterruptedException e) {} assertEquals(count, listener.getNumTimesTopicOneCalled()); assertEquals(count, listener.getNumTimesTopicTwoCalled()); }
This is an awful solution because it involves an absolute hack. Furthermore, it makes that unit test always take at least 3 seconds, which is going to slow the whole test suite down.
ThreadSafeEventService
The real key is to ensure that whatever we call for EventBus within our unit testing code is using a ThreadSafeEventService. This EventService implementation does not use the invokeLater
method, so you can be assured that the messages will be delivered in a deterministic manner. As I previously described, the EventBus static methods are convenience wrappers around a certain implementation of the EventService interface. We are able to modify what the default implementations will be by the EventServiceLocator class. From the docs:
By default will lazily hold a SwingEventService, which is mapped to
SERVICE_NAME_SWING_EVENT_SERVICE
and returned bygetSwingEventService()
. Also by default this same instance is returned bygetEventBusService()
, is mapped toSERVICE_NAME_EVENT_BUS
and wrapped by the EventBus.To change the default implementation class for the EventBus’ EventService, use the API:
EventServiceLocator.setEventService(EventServiceLocator.SERVICE_NAME_EVENT_BUS, new SomeEventServiceImpl());
Or use system properties by:
System.setProperty(EventServiceLocator.SERVICE_NAME_EVENT_BUS,
YourEventServiceImpl.class.getName());
In other words, you can replace the SwingEventService
implementation with the ThreadSafeEventService
by calling
EventServiceLocator.setEventService(EventServiceLocator.SERVICE_NAME_EVENT_BUS,
new ThreadSafeEventService());
An alternative solution is use an EventService instance to publish to rather than the EventBus singleton, and expose getters/setters to that EventService. It can start initialized to the same value that the EventBus would be wrapping, and then the ThreadSafeEventService can be injected for testing. For instance:
public class ClassToTest{ // Use the default EventBus implementation private EventService eventService = EventServiceLocator.getEventBusService(); public void setEventService(EventService service) { this.eventService = service; } public EventService getEventService() { return this.eventService; } public void doSomethingThatNotifiesOthers() { // as opposed to EventBus.publish, use an instance of EventService explicitly eventService.publish(...); } }
Conclusion
I have explained how EventBus static method calls map directly to a singleton implementation of the EventService interface. The default interface works well for Swing applications, due to its queuing of messages via the SwingUtilities.invokeLater
method. Unfortunately, it does not work for unit tests that listen for these EventBus publish events, since the behavior is nondeterministic and the listener might not be notified by the end of the unit test. I presented a solution for replacing the default SwingEventService implementation with a ThreadSafeEventService, which will work perfectly for unit tests.
JS 101 Week 4 – Intro to the DOM
JS 101 week 4 reflection and homework
Questions for reflection (please reflect on your blog and discuss in comments below):
There were two key innovations to the original (fetch-parse-flow-paint) linear workflow that the Mosaic browser used to render web pages. One allowed for a perception of faster rendering, and the other allowed for us to use AJAX. Explain both?
Early browsers had to wait for all the images on a page to be downloaded before they could complete their flow phase, as they needed to know how big the images were before they could lay out the page. The advance of Mosaic was to insert temporary placeholders for the images, layout the page, and display it to the user as soon as all the text was downloaded. As the images finished downloading asynchronously, the placeholder images were each swapped out, and the layout was recalculated and rerendered. While the overall time to finish completely rendering the page was higher (due to the increased number of flow/paint steps), the usability was much improved because people could begin reading the webpages almost immediately.
The second major change was that events could call scripts which in turn could cause the page to be rerendered (i.e. the flow algorithm would be re-run). This is the basis of dynamic HTML pages as we know them. See Blain Armstrong’s excellent blog post for more details.
What are the roles of ‘name’ and ‘id’ attributes in HTML tags? What is an appropriate use of both?
The video noted that ‘name’ and ‘id’ used to be synonymous, and in fact most of the browser implementations used them interchangeably. This has changed in recent years and now there are reasons to use one over the other.
The ‘id’ field must be the unique identifier for a given element, whereas the ‘name’ field is not necessarily unique. Name should be used to identify values in form data. The name you give a text field, for instance, determines the key=value pairing you’ll receive when the form is posted. For instance, if we have a text field whose name
is “firstName”, then when the form is posted, we’ll have firstName=blahblahblah
as one of the parameters.
Which pointers does each node in the DOM tree typically have?
Each node has a parent and an array of child nodes. The parent node is accessed via the parentNode
property, and the array of children can be accessed by childNodes
. Furthermore, there are explicit pointers to the first and last child, accessible via the firstChild
and lastChild
properties. Nodes also have pointers to their next sibling as well (via nextSibling
pointer).
Given a node object from a DOM tree, how do we determine if it is a text node or a regular node?
We can access the nodeName
property; if it’s “#text”, it’s a text node, else it’s a regular node. In code:
function isTextNode(node) { return node.nodeName == "#text"; }
Homework:
Download the source for the web page ‘http://www.useit.com/about/nographics.html’. In the source page itself, write a Javascript function which counts the number of text nodes in the document and shows the count in an alert dialogue. You can choose how you want to trigger the function (through a button click, link click, or any other event).
All of the following answers use the following basic code:
// Performs a depth first tree traversal rooted at the given rootNode. This // node and all of its child nodes will have visitorFunc called on it. function dfs(rootNode, visitorFunc) { var numChildren = rootNode.childNodes.length; visitorFunc(rootNode); for (var i = 0; i < numChildren; i++) { var childNode = rootNode.childNodes[i]; dfs(childNode, visitorFunc); } } var count = 0; function isTextNode(node) { return node.nodeName == "#text"; } function countTextNodes(node) { if (isTextNode(node)) { count+= 1; } } // After this call, count is set to the number of text nodes dfs(document, countTextNodes);
Through onload
With a button launching the count
Change the example above so that instead of displaying the count in an alert dialogue, it is displayed in a span tag in the HTML page itself.
Add a link besides the button, such that when the link is click, it changes the style on the span tag to make it’s contents bold.
/#p2pu-Jan2011-javascript101/
JS 101 – Week 3
In Javascript, functions are first class objects. What does it mean to be a first class object?
A function can be used anywhere an object can be used; in particular, you can pass functions as method arguments. (We did this in last week’s homework by passing the print
function into the iterateAndOperate
method). This is in contrast to a language like Java where functions are not first class objects, and the programmer must use interfaces and anonymous classes to get around this problem. For instance, in Java, to delay the invocation of a function, you might do the following:
SwingUtilities.invokeLater(new Runnable() { @Override public void run() { System.out.println("I was run!"); } })
whereas in Javascript, you could do something like
invokeLater(function() { print("I was run") });
assuming there was an invokeLater
function.
Functions and variables share the same namespace. What does this mean and what important implication does this have?
This means that you must take care in not defining variables in the global namespace that have the same name as a function. For instance,
// global variable x = 5; function x(args) { alert(args); } // The global variable is shadowing the function name; // the function is not called x(5);
Douglas Crockford equates Javascript functions with Lambdas, and also mentions that they are a secure construct. Can you do some research and reflect on what he means by ‘secure construct’?
A lambda basically means that you can pass a function as an argument to another function; this is due to the aforementioned first class nature of functions. They are a secure construct because the scope of the functions is such that private variables in function A cannot be accessed by function B when A is passed into B. In other words,
function outer(func) { // "undefined", since the ‘privateVariable’ // field is scoped only to the inner function. return func.privateVariable; } function inner(a,b,c) { var privateVariable = 25; return a + ", " + b + ", " + c; } alert(outer(inner("cat","dog","bear")));
f
Can you explain the concept of a closure.
A closure means that an inner function continues to have access to variables defined inside of an outer function, even after the outer function has finished. I wrote an example you can view on jsfiddle.
What is the difference between a function and a method?
A method is a function that is bound to an object, and thus it has an implicit “self” reference.
In Javascript there is no implicit type checking of arguments passed to functions. This could lead to bugs if programmers are not careful about what they pass to functions. If you create a function, how would you protect it from well meaning but careless programmers?
You can do defensive checks within the function. For instance, if a method is supposed to take in a number, you could do the following:
function numericalFunction(x) { if (typeof(x) != "number") { throw("Expected numerical value for numericalFunction; got " + x); } // Proceed as normal }
You can also use the arguments
implicit variable in the function to check that the number of arguments the user passed in is equal to the number of arguments that the method expects.
Javascript functions have implicit access to something called this
. this
points to different things depending on how the function was created. Can you explain why we need this
, and what it represents in different type of functions.
If a function is created globally, its this
pointer will be to the DOMObject. If it’s created and bound to an object, the this
pointer points to that object. The following illustrates this:
function f() { return "f's this: " + this; } function nested() { function inner() { return "inner's this:" + this; } return inner(); } // o is an object; add a method do it var o = {}; o.f = f; o.nested = nested; o.newFunc = function() { return "newFunc's this: " + this; }; o.nestedFunc = function() { var inner = function() { return "nestedFunc's inner this: " + this; } return inner(); } print(f()); print(nested()); print(o.f()); print(o.nested()); print(o.newFunc()); print(o.nestedFunc()); // prints // f's this: [object DOMWindow] // inner's this:[object DOMWindow] // f's this: [object Object] // inner's this:[object DOMWindow] // newFunc's this: [object Object] // nestedFunc's inner this: [object DOMWindow] //
Note that only the non nested functions that have been bound to object o
point to that object as opposed to the DOMWindow.
The reason we want the this
pointer is to be able to introspect on the type and contents of the object that is calling the function. For instance, in my last assignment, I used the this
variable to produce a nice representation of an object:
nick = {name:"Nick",age:23}; // This function returns a string representation of whatever // is invoking it function selfStringRep() { var str = ""; for (var v in this) { // This is not an inherited property if (this.hasOwnProperty(v)) { str += "’" + v + "’: " + this[v] +"\n"; } } return str; } Object.prototype.toString = selfStringRep; print(nick.toString()); // prints //’name’: Nick //’age’: 23
4.1
The solution for the cat problem talks about a ‘set’ of names. A set is a collection of values in which no value may occur more than once. If names are strings, can you think of a way to use an object to represent a set of names?
Show how a name can be added to this set, how one can be removed, and how you can check whether a name occurs in it.
var cat_set = {}; // Add a cat cat_set["Tigger"] = true; // remove a cat delete cat_set["Tigger"]; // Check if cat exists cat_set["Frog"] != undefined // Wrap it all up nicely: in an object with functions: var cat_set = {}; cat_set.add_cat = function(cat) { this[cat] = true; } cat_set.remove_cat = function(cat) { delete this[cat]; } cat_set.cat_exists = function(cat) { return this[cat] != undefined; } cat_set.add_cat("Frisky"); // prints "true" show(cat_set.cat_exists("Frisky")) cat_set.remove_cat("Frisky"); // "false" show(cat_set.cat_exists("Frisky"))
4.2
Write a function range that takes one argument, a positive number, and returns an array containing all numbers from 0 up to and including the given number.
An empty array can be created by simply typing []. Also remember that adding properties to an object, and thus also to an array, can be done by assigning them a value with the = operator. The length property is automatically updated when elements are added.
// Assumes n >= 0 function range(n) { var array = []; for (var i = 0; i <= n; i++) { array[i] = i; } return array; }
4.3
split
andjoin
are not precisely each other’s inverse.string.split(x).join(x)
always produces the original value, butarray.join(x).split(x)
does not. Can you give an example of an array where.join(" ").split(" ")
produces a different value?
[" ", " ", " "].join(" ").split(" "); // equals ["", "", "", "", "", ""]
4.4
Write a function called startsWith that takes two arguments, both strings. It returns true when the first argument starts with the characters in the second argument, and false otherwise.
function startsWith(string, pattern) { return string.slice(0,pattern.length) === pattern; }
4.5
Can you write a function
catNames
that takes a paragraph as an argument and returns an array of names?Strings have an
indexOf
method that can be used to find the (first) position of a character or sub-string within that string. Also, whenslice
is given only one argument, it will return the part of the string from the given position all the way to the end.It can be helpful to use the console to ‘explore’ functions. For example, type
"foo: bar".indexOf(":")
and see what you get.
// The sentence is in the form "Blah blah: cat1, cat2, …, catN." function catNames(paragraph) { var colonIndex = paragraph.indexOf(":"); // Skip the colon and the following space var catListString = paragraph.slice(colonIndex + 2); return catString.split(", "); }
/*#p2pu-Jan2011-javascript101*/