Events
Lecture Notes for CS 142
Fall 2010
John Ousterhout
- Readings for this topic:
- Pages 161-194 of the
online supplement to Dynamic HTML: The Definitive Reference,
by Danny Goodman.
- Event types:
- Mouse-related: mouse movement, enter/leave element, button click
- Keyboard-related: down, up, press
- Focus-related: focus in, focus out (blur)
- Timer events
- Miscellaneous:
- Content of an element has changed.
- Page loaded/unloaded.
- Uncaught error.
- Creating an event handler: must specify 3 things:
- What happened: the event of interest.
- Where it happened: an element of interest.
- Javascript to invoke when the event occurs on the element.
- Option #1: in the HTML:
<div onclick="mouseClick('id42');">...</div>
- Option #2: from Javascript using the DOM:
element.onclick = mouseClick;
- When invoking a handler, the browser provides an event object
containing various information about the event:
- button: mouse button that was pressed
- charCode: integer Unicode value corresponding to key,
if there is one.
- keyCode: identifier for the keyboard key that was pressed (not
necessarily an ASCII character!)
- clientX, clientY: mouse position relative to the
browser window
- screenX, screenY: mouse position in screen coordinates
- How is event object passed to the event code?
- HTML: event variable refers to event:
<div onclick="mouseClick(event);">
- DOM (Firefox, Chrome, etc.): event passed as argument to function:
element.onclick = mouseClick;
function mouseClick(evt) {
...
x = evt.clientX;
...
}
- DOM (IE) a global variable event holds the current event:
element.onclick = mouseClick;
function mouseClick() {
...
x = window.event.clientX;
...
}
- Example: dragExample.html:
<body>
<div id="div1" onmousedown="mouseDown(event);"
onmousemove="mouseMove(event);"
onmouseup="mouseUp(event);">Drag Me!</div>
<script type="text/javascript">
//<![CDATA[
isMouseDown = false;
function mouseDown(event) {
oldX = event.clientX;
oldY = event.clientY;
isMouseDown = true;
}
function mouseMove(event) {
if (!isMouseDown) {
return;
}
element = document.getElementById("div1");
element.style.left = (element.offsetLeft +
(event.clientX - oldX)) + "px";
element.style.top = (element.offsetTop +
(event.clientY - oldY)) + "px";
oldX = event.clientX;
oldY = event.clientY;
}
function mouseUp(event) {
isMouseDown = false;
}
//]]>
</script>
</body>
- A cleaner version of the same functionality:
dragExample2.html
<body>
<div id="div1">Drag Me!</div>
<div id="div2">Drag Me Too!</div>
<script type="text/javascript" src="dragger.js"></script>
<script type="text/javascript">
//<![CDATA[
new Dragger("div1");
new Dragger("div2");
//]]>
</script>
</body>
- Put the Javascript code in a separate file dragger.js:
function Dragger(id) {
this.isMouseDown = false;
this.element = document.getElementById(id);
this.element.onmousedown = this.wrap(this, "mouseDown");
}
Dragger.prototype.wrap = function(obj, method) {
return function(event) {
obj[method](event);
}
}
Dragger.prototype.mouseDown = function(event) {
this.oldMoveHandler = document.body.onmousemove;
document.onmousemove = this.wrap(this, "mouseMove");
this.oldUpHandler = document.body.onmouseup;
document.onmouseup = this.wrap(this, "mouseUp");
this.oldX = event.clientX;
this.oldY = event.clientY;
this.isMouseDown = true;
}
Dragger.prototype.mouseMove = function(event) {
if (!this.isMouseDown) {
return;
}
this.element.style.left = (this.element.offsetLeft
+ (event.clientX - this.oldX)) + "px";
this.element.style.top = (this.element.offsetTop
+ (event.clientY - this.oldY)) + "px";
this.oldX = event.clientX;
this.oldY = event.clientY;
}
Dragger.prototype.mouseUp = function(event) {
this.isMouseDown = false;
document.onmousemove = this.oldMoveHandler;
document.onmouseup = this.oldUpHandler;
}
- Event-based programming is different from traditional imperative
programming:
- Must wait for someone to invoke your code.
- Key is to maintain control through events: make sure you have
declared enough handlers; last resort is a timer.
- How does the browser decide which handler(s) are invoked for each event?
- Complicating factor: elements can contain or overlap other elements
(containment more common than overlap in the Web). Suppose I click
with the mouse on "xyz" in the following example:
<body>
<table>
<tr>
<td>xyz</td>
</tr>
</table>
</body>
- Sometimes only the innermost element should handle the event
- Sometimes it's more convenient for an outer element to handle
the event
- Approach #1: bubbling:
- Invoke handlers on the innermost nested element under the mouse;
- Then repeat on its parent, grandparent, etc.
- Any given element can stop the bubbling, so that no ancestors will
see the event (set cancelBubble on the event object).
- Approach #2: capture (or "trickle-down"):
- Start at the outermost element and work down to the innermost nested
element.
- Each element can stop the capture, so that its children never
see the event.
- The official DOM standard: first trickle-down, then bubble up.
- IE implements only bubbling, no trickle-down.
- Firefox and other browsers implement both (addEventListener).
- Timer events:
- Used for animations, automatic page refreshes.
- Run myfunc once, 50 milliseconds from now:
setTimeout("myfunc()", 50);
- Run myfunc every 50 milliseconds:
id = setInterval("myfunc()", 50);
- Cancel a repeating timer:
- Concurrency model for events:
- Events are serialized and processed one-at-a-time.
- Event handling does not interleave with other Javascript execution;
no multi-threading.
- Watch out for browser incompatibilities related to events.
Custom form elements with Javascript and events
- Use HTML to display custom controls (images, tables, etc.).
- Respond to events with Javascript.
- Store the form data in hidden elements.