As with any function, the statements in an event handler or listener execute within a predefined scope. Scope dictates where the interpreter looks to resolve unqualified variable references in an event handler's body. We'll consider event handler scope in relation to the four event implementations supported by Flash.
Event listener methods and event handler properties are scoped exactly as functions are scoped. An event callback function has a scope chain that is defined when the function is defined. Furthermore, event handler properties and event listener methods define a local scope. All the rules of function scope described in Chapter 9 apply to event handler properties and event listener methods.
Unlike event handler properties and event listener methods, on( ) and onClipEvent( ) handlers do not define a local scope! When we attach an onClipEvent( ) handler to a clip, the scope of the handler is the clip, not the event handler. This means that all variables are retrieved from the clip's timeline. For example, if we attach an onClipEvent(enterFrame) handler to a clip named navigation and write trace(x); inside the handler, the interpreter looks for the value of x on navigation's timeline:
onClipEvent (enterFrame) { trace(x); // Displays the value of navigation.x }
The interpreter does not consult a local scope first, because there is no local scope to consult. If we write var y = 10; in the onClipEvent( ) handler, y is defined on navigation's timeline, even though the var keyword ordinarily declares a local variable when used in a function.
The easiest way to remember the scope rules of an onClipEvent( ) handler is to treat the handler's statements as though they were attached to a frame of the handler's clip. For example, suppose we have a clip named ball on the main timeline that has a variable called xVelocity in it. To access xVelocity from inside ball's onClipEvent( ) handler, we simply refer to it directly, like this:
onClipEvent (mouseDown) { xVelocity += 10; }
Supplying the path to the variable as _root.ball.xVelocity is optional, because the interpreter already assumes we mean the variable xVelocity in ball. The same is true of properties and methods; instead of using ball._x, we can use _x, and instead of using ball.gotoAndStop(5), we can use gotoAndStop(5). For example:
onClipEvent (enterFrame) { _x += xVelocity; // Move the ball gotoAndPlay(_currentframe - 1); // Do a little loop }
We can even define a function on ball using a function declaration statement in an onClipEvent( ) handler, like this:
onClipEvent (load) { function hideMe () { _visibility = 0; } }
However, unqualified variable references are ambiguous and behave differently in an event handler property or event listener method than they do in an onClipEvent( ) handler. Therefore, it's prudent to always use the this keyword when referring to the movie clip that defines the handler:
// Preferred reference to the clip with the onClipEvent( ) handler: this._x // Legal, but discouraged reference to the clip with the onClipEvent( ) handler: _x // Preferred: this.gotoAndStop(12); // Discouraged: gotoAndStop(12);
Additionally, use of this is required when we're dynamically generating the name of one of the current clip's properties (either a variable name or a nested clip). Here we tell one of the nested clips in the series ball.stripe1, ball.stripe2, etc. to start playing, depending on the current frame of the ball clip:
onClipEvent (enterFrame) { this["stripe" + this._currentframe].play(); }
The this keyword should also be used with movie clip methods that demand an explicit reference to a movie clip object upon invocation. Any movie clip method with the same name as an ActionScript global function must be used with an explicit clip reference. The this keyword is therefore necessary when invoking the following functions as methods inside an onClipEvent( ) handler:
For example:
this.duplicateMovieClip("ball2", 1); this.loadVariables("vars.txt"); this.startDrag(true); this.unloadMovie();
You'll learn all about the dual nature of these functions in Chapter 13 under Section 13.7.
Note that the this keyword allows us to refer to the current clip even when that clip has no assigned instance name in the authoring tool or when we don't know the clip's name. In fact, using this, we can even pass the current clip as a reference to a function without ever knowing the current clip's name. Here's some code to demonstrate:
// CODE ON MAIN TIMELINE // Here is a generic function that moves any clip function move (clip, x, y) { clip._x += x; clip._ y += y; } // CODE ON CLIP // Call the main timeline function and tell it to move the // current clip by passing a reference with the this keyword onClipEvent (enterFrame) { _root.move(this, 10, 15); }
|
|
For example, suppose we place our ball clip on the main timeline of a movie, and the main timeline (not ball's timeline) has a moveBall( ) function defined on it. We may absent-mindedly call moveBall( ) from an event handler on ball, like this:
onClipEvent (enterFrame) { moveBall( ); // Does nothing! There's no moveBall( ) function in ball. // The moveBall( ) function is defined on _root. }
We must refer to the moveBall( ) function explicitly on the main timeline using _root like this:
onClipEvent (enterFrame) { _root.moveBall( ); // Now it works! }
The scope of an on( ) handler depends on the type of the object to which it is attached. When attached to a button, an on( ) handler is scoped to the timeline upon which the button resides. When attached to a movie clip, an on( ) handler is scoped to the clip that bears the handler, as with an onClipEvent( ) handler. Prior to Flash MX, movie clips did not support on( ) handlers (only Flash 5 buttons did).
For example, if we place a button on the main timeline and declare the variable speed in a handler on that button, speed will be scoped to the main timeline ( _root):
// CODE FOR BUTTON HANDLER on (release) { var speed = 10; // Defines speed on _root }
In contrast, if we place an on( ) handler on a movie clip object named ball and declare the variable speed in the handler, speed is scoped to ball:
// CODE FOR ball HANDLER on (release) { var speed = 10; // Defines speed on ball, not _root }
Inside an on( ) handler for a button object, the this keyword refers to the timeline on which the button resides:
// CODE FOR BUTTON HANDLER on (release) { // Make the clip on which this button resides 50% transparent this._alpha = 50; // Move the clip on which this button resides 10 pixels to the right this._x += 10; }
But inside a movie clip's on( ) handler, the this keyword refers to the clip that bears the handler:
// CODE FOR MOVIE CLIP HANDLER on (release) { // Make the clip with the handler 50% transparent this._alpha = 50; // Move the clip with the handler 10 pixels to the right this._x += 10; }
See the MovieClip and Button classes in the Language Reference for details on movie clip and button events.
The various scopes in the previous discussion can be confusing. Use the general rules outlined in Table 10-1 to determine the scope of a specific handler in Flash MX. Note that of the following event handling mechanisms, Flash 5 supported only "MovieClip with onClipEvent( ) handler" and "Button with on( ) handler."
Event handling mechanism |
Example |
Scope |
---|---|---|
Event handler property |
someTextField.onSetFocus = function ( ) { statements } |
The clip in which function declaration occurred.[1] |
Event listener method |
GUIManager.onResize = function ( ) { statements } Stage.addListener(GUIManager); |
The clip in which function declaration occurred.[1] |
MovieClip with event handler property |
theClip_mc.onPress = function ( ) { statements } |
The clip in which function declaration occurred.[1] |
MovieClip with on( ) handler |
on (press) { statements } |
The clip that bears the handler in the authoring tool. |
MovieClip with onClipEvent( ) handler |
onClipEvent (enterFrame) { statements } |
The clip that bears the handler in the authoring tool. |
Button with event handler property |
submit_btn.onRelease = function ( ) { statements } |
The clip in which function declaration occurred.[1] |
Button with on( ) handler |
on (release) { statements } |
The clip on whose timeline the button resides. |
[1] For handlers and listeners assigned within a function, the scope is that function (in accordance with the rules of nested function scope, discussed in Chapter 9).
Finally, here's a scenario to test your understanding of event handler scope. Before reading the answer, see if you can determine it yourself.
Suppose a movie clip instance named intro_mc is placed on the main timeline of a movie, and the following code is attached to the main timeline:
intro_mc.onPress = function ( ) { trace(this); // Here, this refers to intro_mc. this.play( ) // This line makes intro_mc play. play( ); // Why won't this line make intro_mc play? }
Question: In intro_mc's onPress( ) callback function, why does this refer to intro_mc, whereas unqualified statements do not? In other words, why doesn't line 3 of the handler body make intro_mc play?
Answer: this is a reference to the object through which onPress( ) was invoked: intro_mc. But statements inside the onPress( ) function are executed in the scope of the timeline on which the function was defined—the main timeline. Hence, the standalone play( ) command plays the main timeline, not intro_mc.