home | O'Reilly's CD bookshelfs | FreeBSD | Linux | Cisco | Cisco Exam  


Book HomeActionScript: The Definitive GuideSearch this book

13.5. Referring to Instances and Main Movies

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.

13.5.2. Referring to the Current Instance or Movie

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".

13.5.2.1. Self-references with the this keyword

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.

13.5.3. Referring to Nested Instances

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.

Figure 13-5

Figure 13-5. A sample clip hierarchy

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.

13.5.4. Referring to Main Movies with _root and _leveln

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.

13.5.4.1. Referencing the current level's main movie using _root

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.

13.5.4.2. Referencing other documents in the Player using _leveln

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.

13.5.5. Authoring Instance References with Insert Target Path

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.

Figure 13-6

Figure 13-6. The Insert Target Path button

To use Insert Target Path, follow these steps:

  1. Position the cursor in your code where you want a clip reference to be inserted.

  2. Click the Insert Target Path button, shown in Figure 13-6.

  3. In the Insert Target Path dialog box, select the clip to which you want to refer.

  4. 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.

  5. 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.

13.5.6. Dynamic References to Clip Objects

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.

13.5.6.1. Using the array-element access operator

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!

13.5.6.3. Using for-in to access movie clips

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.

Example 13-2. Finding Movie Clips on a 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.

Example 13-3. Recursively Finding All Movie Clips on a Timeline

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".

13.5.6.4. The _name property

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.



Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.