In the earlier sections, we learned how to create and layer movie clip instances and external .swf files in the Flash Player. We must be able to refer to that content in order to effectively control it with ActionScript.
We refer to instances and main movies under four general circumstances, when we want to:
Get or set a property of a clip or a movie
Create or invoke a method of a clip or a movie
Apply some function to a clip or a movie
Manipulate a clip or a movie as data, for example, by storing it in a variable or passing it as an argument to a function
While the circumstances under which we refer to clip instances and movies are fairly simple, the tools we have for making references are many and varied. We'll spend the rest of this section exploring ActionScript's instance- and movie-referencing tools.
Earlier, we learned that movie clips are referred to by their instance names. For example:
trace(myVariable); // Refer to a variable trace(myClip); // Refer to a movie clip
In order to refer to an instance directly (as shown in the preceding trace( ) example), the instance must reside on the timeline to which our code is attached. For example, if we have an instance named clouds placed on the main timeline of a document, we may refer to clouds from code attached to the main timeline as follows:
// Set a property of the instance clouds._alpha = 60; // Invoke a method on the instance clouds.play( ); // Place the instance in an array of other related instances var background = [clouds, sky, mountains];
If the instance we want to reference does not reside on the same timeline as our code, we must use a more elaborate syntax, as described later under Section 13.5.3, "Referring to Nested Instances".
We don't always have to use an instance's name when referring to a clip. Code attached to a frame in an instance's timeline may refer to that instance's properties and methods directly, without any instance name.
For example, to set the _alpha property of a clip named cloud, we could place the following code on a frame in the cloud timeline:
_alpha = 60;
Similarly, to invoke the play( ) method on cloud from a frame in the cloud timeline, we could simply use:
play( );
This technique may be used on any timeline, including timelines of main movies. For example, the following two statements would be synonymous if attached to a frame on the main timeline of a Flash document. The first refers to the main movie implicitly, whereas the second refers to the main movie explicitly via the global _root property:
gotoAndStop(20); _root.gotoAndStop(20);
As we learned in Chapter 10, "Events and Event Handlers", code in an instance's event handler may, like timeline code, also refer to properties and methods directly. For example, we could attach the following event handler to cloud. This handler sets a property of, and then invokes a method on, cloud without referring to the cloud instance explicitly:
onClipEvent (load) { _alpha = 60; stop( ); }
However, not all methods may be used with an implicit reference to a movie clip. Any movie clip method that has the same name as a corresponding global function (such as duplicateMovieClip( ) or unloadMovie( ) ) must be invoked with an explicit instance reference. Hence, when in doubt, use an explicit reference. We'll have more to say about method and global function conflicts later in Section 13.8.3.1, "Method versus global function overlap issues".
When we want to explicitly refer to the current instance from a frame in its timeline or from one of its event handlers, we may use the this keyword. For example, the following statements would be synonymous when attached to a frame in the timeline of our cloud instance:
_alpha = 60; // Implicit reference to the current timeline this._alpha = 60; // Explicit reference to the current timeline
There are two reasons to use this to refer to a clip even when we can just refer to the clip directly. When used without an explicit instance reference, certain movie clip methods are mistaken for global functions by the interpreter. If we omit the this reference, the interpreter thinks we're trying to invoke the analogous global function and complains that we're missing the "target" movie clip parameter. To work around the problem, we use this, as follows:
this.duplicateMovieClip("newClouds", 0); // Invoke a method on an instance // If we omit the this reference, we get an error duplicateMovieClip("newClouds", 0); // Oops!
Using this, we can conveniently pass a reference to the current timeline to functions that operate on movie clips:
// Here's a function that manipulates clips function moveTo (theClip, x, y) { theClip._x = x; theClip._y = y; } // Now let's invoke it on the current timeline moveTo(this, 150, 125);
If you do a lot of object-oriented programming, be cautious when using the this keyword to refer to instances and movies. Remember that inside a custom method or an object constructor, this has a very different meaning and is not a reference to the current timeline. See Chapter 12, "Objects and Classes" for details.
As we learned in the introduction to this chapter, movie clip instances are often nested inside of one another. That is, a clip's canvas may contain an instance of another clip, which may itself contain instances of other clips. For example, a game's spaceship clip may contain an instance of a blinkingLights clip or a burningFuel clip. Or a character's face clip may include separate eyes, nose, and mouth clips.
Earlier, we saw briefly how we could navigate up or down from any point in the hierarchy of clip instances, much like you might navigate up and down a series of subdirectories on your hard drive. Let's examine this in more detail and see some more examples.
Let's first consider how to refer to a clip instance that is nested inside of the current instance. When a clip is placed on the timeline of another clip, it becomes a property of that clip, and we can access it as we would access any object property (with the dot operator). For example, suppose we place clipB on the canvas of clipA. To access clipB from a frame in clipA's timeline, we use a direct reference to clipB:
clipB._x = 30;
Now suppose clipB contains another instance, clipC. To refer to clipC from a frame in clipA's timeline, we access clipC as a property of clipB like this:
clipB.clipC.play( ); clipB.clipC._x = 20;
Beautiful, ain't it? And the system is infinitely extensible. Because every clip instance placed on another clip's timeline becomes a property of its host clip, we can traverse the hierarchy by separating the instances with the dot operator, like so:
clipA.clipB.clipC.clipD.gotoAndStop(5);
Now that we've seen how to navigate down the instance hierarchy, let's see how we navigate up it to refer to the instance or movie that contains the current instance. As we saw earlier, every instance has a built-in _ parent property that refers to the clip or main movie containing it. We use the _ parent property like so:
myClip._ parent
Recalling our recent example with clipA on the main timeline, clipB inside clipA, and clipC inside clipB, let's see how to use _ parent and dot notation to refer to the various clips in the hierarchy. Assume that the following code is placed on a frame of the timeline of clipB:
_ parent // A reference to clipA this // A reference to clipB (the current clip) this._ parent // Another reference to clipA // Sweet Sheila, I love this stuff! Let's try some more... _ parent._ parent // A reference to clipA's parent (clipB's grandparent), // which is the main timeline in this case
Note that although it is legal to do so, it is unnecessarily roundabout to traverse down the hierarchy using a reference to the clipC property of clipB only to traverse back up the hierarchy using _ parent. These roundabout references are unnecessary but do show the flexibility of dot notation:
clipC._ parent // A roundabout reference to clipB (the current timeline) clipC._ parent._ parent._ parent // A roundabout reference to the main timeline
Notice how we use the dot operator to descend the clip hierarchy and use the _ parent property to ascend it. If this is new to you, you should probably try building the clipA, clipB, clipC hierarchy in Flash and using the code in our example. Proper instance referencing is one of the fundamental skills of a good ActionScript programmer.
Note that the hierarchy of clips is like a family tree. Unlike a typical family tree of a sexually reproducing species in which each offspring has two parents, our clip family tree expands asexually. That is, each household is headed by a single parent who can adopt any number of children. Any clip (i.e., any node in the tree) can have one and only one parent (the clip that contains it) but can have multiple children (the clips that it contains). Of course, each clip's parent can in turn have a single parent, which means that each clip can have only one grandparent (not the four grandparents humans typically have). See Figure 13-5.
Therefore, no matter how far you go down the family tree, if you go back up the same number of steps you will always end up in the same place you started. It is therefore pointless to go down the hierarchy only to come back up. However, it is not pointless to go up the hierarchy and then follow a different path back down. For example, suppose that the main timeline also contains clipD, which would make clipD a "sibling" of clipA because both would have the main timeline as their _ parent. In that case, you can refer to clipD from a script attached to clipB as follows:
_ parent._ parent.clipD // This refers to clipD, a child of the main // timeline (clipA's _ parent) and therefore // a sibling of clipA
Note that the main timeline does not have a _ parent property (main movies are the top of any clip hierarchy and cannot be contained by another timeline); references to _root._ parent yield undefined.
Now that we've seen how to navigate up and down the clip hierarchy relative to the current clip, let's explore other ways to navigate along absolute pathways and even among other documents stored in other levels of the Player's document stack. In earlier chapters, we saw how these techniques applied to variables and functions; here we'll learn how they can be used to control movie clips.
When an instance is deeply nested in a clip hierarchy, we can repeatedly use the _ parent property to ascend the hierarchy until we reach the main movie timeline. But in order to ease the labor of referring to the main timeline from deeply nested clips, we can also use the built-in global property _root, which is a shortcut reference to the main movie timeline. For example, here we play the main movie:
_root.play( );
The _root property is said to be an absolute reference to a known point in the clip hierarchy because unlike the _ parent and this properties, which are relative to the current clip, the _root property is the same no matter which clip it is referenced from. These are all equivalent:
_ parent._root this._root _root
Therefore, you can and should use _root when you don't know where a given clip is nested within the hierarchy. For example, consider the following hierarchy in which circle is a child of the main movie timeline and square is a child of circle:
main timeline circle square
Now consider this script attached to a frame in both circle and square:
_ parent._x += 10 // Move this clip's parent clip 10 pixels to the right
When that code is executed from within circle, it will cause the main movie to move 10 pixels to the right. When it is executed from within square, it will cause circle (not the main movie) to move 10 pixels to the right. In order for the script to move the main movie 10 pixels regardless of where the script is executed from, it should be rewritten as:
_root._x += 10 // Move the main movie 10 pixels to the right
Furthermore, the _ parent property is not valid from within the main timeline; the version of the script using _root would be valid when used in a frame of the main timeline.
The _root property may happily be combined with ordinary instance references to descend a nested-clip hierarchy:
_root.clipA.clipB.play( );
References that start with _root refer to the same, known, starting point from anywhere in a document. There's no guessing required.
If we have multiple .swf files loaded in the document stack of the Flash Player, we may refer to the main movie timelines of the various documents using the built-in series of global properties _level0 through _leveln, where n represents the level of the document we want to reference.
Therefore, _level0 represents the document in the lowest level of the document stack (documents in higher levels will be rendered in the foreground). Unless a movie has been loaded into _level0 via loadMovie( ), _level0 is occupied by the movie that was initially loaded when the Player started.
Here is an example that plays the main movie timeline of the document in level 3 of the Player's document stack:
_level3.play( );
Like the _root property, the _leveln property may be combined with ordinary instance references via the dot operator:
_level1.clipA.stop( );
As with references to _root, references to _leveln properties are called absolute references because they lead to the same destination from any point in a document.
Note that _leveln and _root are not synonymous. The _root property is always the current document's main timeline, regardless of the level on which the current document resides, whereas the _leveln property is a reference to the main timeline of a specific document level. For example, suppose we place the code _root.play( ) in myMovie.swf. When we load myMovie.swf onto level 5, our code plays _level5's main movie timeline. By contrast, if we place the code _level2.play( ) in myMovie.swf and load myMovie.swf into level 5, our code plays _level2's main movie timeline not _level5's. Of course, from within level 2, _root and _level2 are equivalent.
When the instance structure of a movie gets very complicated, composing references to movie clips and main movies can be laborious. We may not always recall the exact hierarchy of a series of clips, and, hence, may end up frequently selecting and editing clips in the authoring tool just to determine their nested structure. The ActionScript editor provides an Insert Target Path tool (shown in Figure 13-6) which lets us generate a clip reference visually, relieving the burden of creating it manually.
To use Insert Target Path, follow these steps:
Position the cursor in your code where you want a clip reference to be inserted.
Click the Insert Target Path button, shown in Figure 13-6.
In the Insert Target Path dialog box, select the clip to which you want to refer.
Choose whether to insert an absolute reference, which begins with _root, or a relative reference, which expresses the reference to the target clip in relation to the clip that contains your code.
If you are exporting to Flash 4 format, choose the Slashes Notation button for Flash 4 compatibility. (The Dot Notation button, selected by default, composes references that won't work in Flash 4). See Table 2-1.
The Insert Target Path tool cannot generate references that ascend a hierarchy of clips. That is, the tool cannot be used to refer to a clip that contains the current clip (unless you want to begin the path from _root and proceed downward). To create references that ascend the clip hierarchy, we must manually type the appropriate references in our code using the _ parent property.
Normally, we know the name of the specific instance or movie we are manipulating, but there are times when we'd like to control a clip whose name we don't know. We may, for example, want to scale down a whole group of clips using a loop or create a button that refers to a different clip each time it is clicked. To handle these situations, we must create our clip references dynamically at runtime.
As we saw in Chapter 5, "Operators", and Chapter 12, "Objects and Classes", the properties of an object may be retrieved via the dot operator or through the array-element access operator, [ ]. For example, the following two statements are equivalent:
myObject.myProperty = 10; myObject["myProperty"] = 10;
The array-element access operator has one important feature that the dot operator does not; it lets us (indeed requires us to) refer to a property using a string expression rather than an identifier. For example, here's a string concatenation expression that acts as a valid reference to the property myProperty:
myObject["myProp" + "erty"];
We can apply the same technique to create our instance and movie references dynamically. We already learned that clip instances are stored as properties of their parent clips. Earlier, we used the dot operator to refer to those instance properties. For example, from the main timeline we can refer to clipB, which is nested inside of another instance, clipA, as follows:
clipA.clipB; // Refer to clipB inside clipA clipA.clipB.stop( ); // Invoke a method on clipB
Because instances are properties, we can also legitimately refer to them with the [ ] operator, as in:
clipA["clipB"]; // Refer to clipB inside clipA clipA["clipB"].stop( ); // Invoke a method on clipB
Notice that when we use the [ ] operator to refer to clipB, we provide the name of clipB as a string, not an identifier. That string reference may be any valid string-yielding expression. For example, here's a reference to clipB that involves a string concatenation:
var clipCount = "B"; clipA["clip" + clipCount]; // Refer to clipB inside clipA clipA["clip" + clipCount].stop( ); // Invoke a method on clipB
We can create clip references dynamically to refer to a series of sequentially named clips:
// Stop clip1, clip2, clip3, and clip4 for (var i = 1; i <= 4; i++) { _root["clip" + i].stop( ); }
Now that's powerful!
I started this chapter by saying that movie clips are effectively data objects in ActionScript. We can store a reference to a movie clip instance in a variable, an array element, or an object property.
Recall our earlier example of a nested instance hierarchy (clipC nested inside clipB nested inside clipA) placed on the main timeline of a document. If we store those various clips in data containers, we can control them dynamically using the containers instead of explicit references to the clips. Example 13-1, which shows code that would be placed on a frame in the main timeline, uses data containers to store and control instances.
var x = clipA.clipB; // Store a reference to clipB in the variable x x.play( ); // Play clipB // Now let's store our clips in the elements of an array var myClips = [clipA, clipA.clipB, clipA.clipB.clipC]; myClips[0].play( ); // Play clipA myClips[1]._x = 200; // Place clipB 200 pixels from the Stage's left edge // Stop all the clips in our array using a loop for (var i = 0; i < myClips.length; i++) { myClips[i].stop( ); }
By storing clip references in data containers, we can manipulate the clips (such as playing, rotating, or stopping them) without knowing or affecting the document's clip hierarchy.
In Chapter 8, "Loop Statements", we learned how to enumerate an object's properties using a for-in loop. Recall that a for-in loop's iterator variable automatically cycles through all the properties of the object, so that the loop is executed once for each property:
for (var prop in someObject) { trace("the value of someObject." + prop + " is " + someObject[prop]); }
Example 13-2 shows how to use a for-in loop to enumerate all the clips that reside on a given timeline.
for (var property in myClip) { // Check if the current property of myClip is a movie clip if (typeof myClip[property] == "movieclip") { trace("Found instance: " + myClip[property]._name); // Now do something to the clip myClip[property]._x = 300; myClip[property].play( ); } }
The for-in loop gives us enormously convenient access to the clips contained by a specific clip instance or main movie. Using for-in we can control any clip on any timeline, whether we know the clip's name or not and whether the clip was created manually or programmatically.
Example 13-3 shows a recursive version of the previous example. It finds all the clip instances on a timeline, plus the clip instances on all nested timelines.
function findClips (myClip, indentSpaces) { // Use spaces to indent the child clips on each successive tier var indent = " "; for (var i = 0; i < indentSpaces; i++) { indent += " "; } for (var property in myClip) { // Check if the current property of myClip is a movie clip if (typeof myClip[property] == "movieclip") { trace(indent + myClip[property]._name); // Check if this clip is parent to any other clips findClips(myClip[property], indentSpaces + 4); } } } findClips (_root, 0); // Find all clip instances descended from main timeline
For more information on function recursion, see Section 9.9, "Recursive Functions" in Chapter 9, "Functions".
As we learned earlier in Section 13.3.3, "Instance Names", every instance's name is stored as a string in the built-in property _name. We can use that property, as we saw in Example 13-2, to determine the name of the current clip or the name of some other clip in an instance hierarchy:
_name; // The current instance's name _ parent._name // The name of the clip that contains the current clip
The _name property comes in handy when we want to perform conditional operations on clips according to their identities. For example, here we duplicate the seedClip clip when it loads:
onClipEvent (load) { if (_name == "seedClip") { this.duplicateMovieClip("clipCopy", 0); } }
By checking explicitly for the seedClip name, we prevent infinite recursion -- without our conditional statement, the load handler of each duplicated clip would cause the clip to duplicate itself.
Every movie clip instance has a built-in _target property, which is a string specifying the clip's absolute path using the deprecated Flash 4 "slash" notation. For example, if clipB is placed inside clipA, and clipA is placed on the main timeline, the _target property of those clips is as follows:
_root._target // Contains: "/" _root.clipA._target // Contains: "/clipA" _root.clipA.clipB._target // Contains: "/clipA/clipB"
The targetPath( ) function returns a string that contains the clip's absolute reference path, expressed using dot notation. The targetPath( ) function is the Flash 5-syntax equivalent of _target. It takes the form:
targetPath(movieClip)
where movieClip is the identifier of the clip whose absolute reference we wish to retrieve. Here are some examples, using our now familiar example hierarchy:
targetPath(_root); // Contains: "_level0" targetPath(_root.clipA); // Contains: "_level0.clipA" targetPath(_root.clipA.clipB); // Contains: "_level0.clipA.clipB"
The targetPath( ) function gives us the complete path to a clip, whereas the _name property gives us only the name of the clip. (This is analogous to having a complete file path versus just the filename.) So, we can use targetPath( ) to compose code that controls clips based not only on their name but also on their location. For example, we might create a generic navigational button that, by examining its targetPath( ), sets its own color to match the section of content within which it resides. See the example under Reference 20.206 in Part III, "Language Reference" for a demonstration of targetPath( ) in action.
In Flash 4, Tell Target was our main tool for referring to movie clips. Tell Target, bless its soul, was an unwieldy tool and is rendered obsolete by the much more elegant object model introduced in Flash 5. The Tell Target function has been deprecated (i.e., retired from recommended use). Although we may still use the tellTarget( ) function to code in a Flash 4 manner, tellTarget( ) will likely disappear in the future.
Consider the following code, which uses Tell Target to play an instance named closingSequence:
Begin Tell Target ("closingSequence") Play End Tell Target
As of Flash 5, we simply invoke the much more convenient and readable play( ) method on the closingSequence instance:
closingSequence.play( );
Tell Target could also perform multiple operations on an instance within a code block, like so:
Begin Tell Target ("ball") (Set Property: ("ball", x Scale) = "5") Play End Tell Target
As of Flash 5, the with( ) statement, described in Chapter 6, "Statements", is the preferred way to achieve similar results:
with (ball) { _xscale = 5; play( ); }
See Appendix C, "Backward Compatibility", for more details on deprecated Flash 4 ActionScript and the preferred equivalents in Flash 5.
Copyright © 2002 O'Reilly & Associates. All rights reserved.