This is an archived tutorial from the kirupa.com legacy collection. It covers software that may no longer be available, but it is kept online because the ideas still hold up.
Adobe's (formerly Macromedia's) Flash
application is, according to them, "The industry's most
advanced authoring environment for creating interactive web
sites and digital experiences." However, many users have
trouble structuring their site efficiently within Flash.
When Flash is opened, a blank canvas appears, and a single
layer with multiple blank frames displays, waiting to be
used, as you can see in the image below. There's little
guidance on how to proceed, and many different directions
that one can take. So it's easy to see why users can become
confused:

Over the past six years of working with Flash and helping
others with their own Flash projects, one thing has become
clear to me: no two Flash web sites are laid out the same.
Sadly, over 90% of the Flash sites I've seen are not
structured efficiently; some are horribly inefficient. While
there may be no one right way to structure a Flash web site,
some ways certainly are better than others.
Keep in mind that when I say "structure" I'm not talking
about how your web site looks; we don't care how your web
site looks, at least, not in this article. Structure is
about setting up your Flash site with Keyframes,
ActionScript and MovieClips, and controlling the Flash
playhead. Good structure can make your site load faster,
make it easier to manage and update, and keep unexpected
things from happening. A bad structure can increase load
times, make it painful to make future changes, and cause
unexpected headaches. The principles explained in this
article apply to all Flash site designs.
In this article, we'll develop a navigation system. Though
it might seem trivial, this will serve as a good example for
this article and the techniques can be applied to any
project you're working on, be it a full Flash web site, a
Flash poll, or a Flash RSS reader. Incidentally, I've
created all of these in the last month, and all exhibit the
structure that we'll see here.
Now is a good time to go ahead and download the necessary
files so that you can follow along. The code archive below
contains a Flash FLA file, an XML file, a CSS file and a PNG
image:
Once you open the navigation.fla file, you'll immediately notice how empty it looks. This file contains but 2 layers spanning 4 frames. Almost all my Flash 'creations' have a similar structure: typically no more than 5 frames and a handful of layers. If your Flash structure looks like the one shown in the image below, you're doing something wrong -- it's a good thing you're reading this article.

Let's start with a tip: Never use Scenes. Scenes can cause unexpected results from the playhead. The playhead doesn't really move from scene-to-scene gracefully. Depending on your Flash site, you may or may not notice this. But, if you're playing a music file and your playhead moves to another scene, you can witness problems as the song skips. For this reason, you won't find more than one scene in our file.
Let's examine each frame one by one.
Frame 1 is always -- and only -- used for setting global variables. This works well, as the playhead hits this frame first, sets our globals and moves on to the next frame. We never return to frame 1, ever. It's important always to control the Flash playhead and know where it's located at all times. Now, let's look at the code:
var xmlFile:String = "navigation.xml";
var cssFile:String = "navigation.css";
var nCounter:Number = 0;
var yPosition:Number = 0;
var nTotalButtons:Number = 0;
var astrImages:Array = new Array();
var astrText:Array = new Array();
var astrLinks:Array = new Array();
var image_mcl:MovieClipLoader = new MovieClipLoader();
var mclListener:Object = new Object();
// Formats our textField
var my_css = new TextField.StyleSheet();
// fun with filters (new in Flash 8)
import flash.filters.DropShadowFilter;
var distance:Number = 2;
var angleInDegrees:Number = 45;
var color:Number = 0x000000;
var alpha:Number = .8;
var blurX:Number = 2;
var blurY:Number = 2;
var strength:Number = 1;
var quality:Number = 3;
var inner:Boolean = false;
var knockout:Boolean = false;
var hideObject:Boolean = false;
var filter:DropShadowFilter = new DropShadowFilter(distance, angleInDegrees, color, alpha, blurX, blurY, strength, quality, inner, knockout, hideObject);
var filterArray:Array = new Array();
filterArray.push(filter);
Here we see our global variables: some
strings, some numbers, and some arrays -- your typical
variables. But, wait. Some not-so-typical variables are also
present: a MovieClipLoader, a StyleSheet and a Filter.
The MovieClipLoader class allows us to implement listener
callbacks that provide status information while SWF, JPEG,
GIF, and PNG files are being loaded into movie clips. This
is a lot handier than the dated loadMovie() class, which
failed to provide us with any information on how much of our
file had already been downloaded. We'll use the
MovieClipLoader class later to load our button image.
The StyleSheet class lets you create a StyleSheet object
that contains text formatting rules for font size, color,
and other styles. We'll use this StyleSheet in frame 4 to
format the text on our navigation buttons.
Filters are new to Flash 8; they allow us to add some cool
effects to our objects. For example, we can apply drop
shadows, blurs, glows and bevels to objects. We'll apply the
drop shadow filter to our text field later. Some will say,
"Big deal. If I want a drop shadow I could use Photoshop or
Fireworks for that." You could, but then your text would no
longer be dynamic, as ours is.
Keep in mind that all the components that make up our Flash
site don't reside in Flash. Perhaps I should have used an
exclamation point instead of a period for that last
sentence? Let me say it again, "All the components that make
up our Flash site don't reside in Flash!" This approach
allows for easy modification later. If, for example, I no
longer like the image chosen for my buttons and I want to
swap it out, that's no problem. Or maybe I want to have the
button say something different, or perhaps I want to add or
remove a button later. Neither will be a problem; in fact, I
won't even have to open Flash to make these changes. That's
very handy indeed, especially when you're creating a project
for someone who doesn't have Flash, or much in the way of
technical knowledge.
Before we move on to frame 2, notice our 3 arrays. These
arrays will store the data for our buttons. Once our XML
file has been parsed successfully, each array will be filled
in with the proper data. This happens in frame 2.
To recap, what was the main purpose of frame 1? If you said,
"Global variables", you get a gold star. If not, run down to
Starbucks and get some coffee and come back.
The main purpose of frame 2 is to gather external data --
typically, literal files in the form of XML. If you haven't
looked at our navigation XML file, this would be a good time
to do so. This article is not intended to teach you XML, so
just take a look at the structure and what's in there. We
have a total of 4 buttons; each button is assigned an image,
some text and a link.
A note to all you non-Flash 8 users: you won't be able to
use a PNG image. You'll have to use the provided JPEG image
and make the changes in the XML file. Previous versions of
Flash are only able to load JPEG files.
Now, let's look at the code in frame 2. I'll break it down
as we go.
var xmlData:XML = new XML();
We start by instantiating a new XML object and call it xmlData. The XML object contains all the methods and properties we will need in order to work with XML data in Flash.
xmlData.ignoreWhite = true;
Flash will read all spacing contained within the XML file, including empty text nodes. As this is not the desired result, we simply tell Flash to ignore white space.
xmlData.load( xmlFile );
This line loads an XML document from the
specified URL. Remember, xmlFile was declared in frame 1 and
set to "navigation.xml." Because our FLA file resides in the
same directory as our XML file, we need only give the name.
Had our XML file been located in a directory other than the
one that houses our FLA file, we'd have included a relative
path.
The load process is asynchronous; it does not finish
immediately after the load() method is executed. When the
load() method is executed, the XML object property loaded is
set to false. When the XML data finishes downloading, the
loaded property is set to true, and the onLoad event handler
is invoked.
xmlData.onLoad = loadXML;
Here we set the onLoad event handler to call our function loadXML. Let's look at the function:
This function runs only when the XML file
has finished downloading and the loaded property is set to
true.
If the XML file has completed loading, we execute the
following code:
var xnRootNode:XMLNode = this;
We create an XML node variable (xnRootNode) and set it equal to this; this being the XML data that was just loaded.
nTotalButtons = xnRootNode.firstChild.childNodes.length;
At this point we need to talk about searching our XML to locate the values we need.
This is a good time to revisit our navigation XML file.
<nav>
<button>
<image>button1.png</image>
<text>Articles</text>
<link>articles.swf</link>
</button>
<button>
<image>button1.png</image>
<text>Books</text>
<link>books.swf</link>
</button>
<button>
<image>button1.png</image>
<text>Blog</text>
<link>blog.swf</link>
</button>
<button>
<image>button1.png</image>
<text>Contact 'company name' today for a free estimate</text>
<link>forum.swf</link>
</button>
</nav>
In order to retrieve values from an XML
file, we step through the family tree until you find the
desired value. So in our code,
xnRootNode.firstChild.childNodes.length;, the first child,
is <nav> and it has 4 child nodes -- you guessed it, they're
our 4 <button> nodes. So nTotalButtons is now equal to 4.
Next we use a 'for loop' to gather all the data from each of
our buttons and add it to our arrays.
astrImages.push(xnRootNode.firstChild.childNodes[i].childNodes[0]
z.firstChild.nodeValue);
astrImages is our array for images; the push method adds our
XML value to this array.
xnRootNode.firstChild.childNodes[i].childNodes[0].firstChild.nodeValue;
This may look scary at first, but let's
break it down.
xnRootNode.firstChild is <nav> its first
child is the first occurrence of <button> and <button>'s
first child is <image>. So far, so good, but what is
<image>'s first child? If you said, "button1.png" then pat
yourself on the back: that's correct. Even the text value
within the node is considered a child node in Flash. Lastly,
we use the nodeValue property to retrieve the node value of
the XML object, in this case "button1.png." Have a look at
the following table, as it may help:
| xnRootNode.firstChild | <nav> |
| childNodes[i] | <button> |
| childNodes[0] | <image> |
| firstChild.nodeValue | <button1.png> |
We continue to fill in all our arrays, then
move on. Only after our XML data has completed loading and
has been pushed into our arrays are we ready to move the
playhead onward. Now, we move to our next frame.
Frame 3 is used to load our CSS file. Have a look at our navigation.css file:
.buttonStyle {
font-family: tahoma, verdana, arial, helvetica;
font-size: 16px;
color: #000000;
text-align: center;
}
It looks like any other CSS file. We use CSS
to set the formatting on our buttons, which just keeps with
our paradigm of keeping everything external and dynamic.
Now that our CSS has loaded successfully, let's move on to
frame 4.
Before we look at the code for frame 4, let's review. When
our Flash file loads, it hits frame 1 and sets all the
variables that we will need throughout our site. Next, in
frame 2, we gather external data. We got a little bonus and
learned how to import XML code which we then parsed and
stored in 3 arrays that we'll use in frame 4. In frame 3, we
imported the CSS that we'll use in frame 4.
What I want you to notice is that we're at our last frame
and, at this point, nothing has been built. Up to this point
we've only gathered the data that we'll need. We are now at
frame 4, where the playhead will rest indefinitely and our
Flash file will take form.
We start by tracing our arrays just to reassure ourselves
that we have everything we need. If you're not familiar with
the trace() function, then you won't get far in Flash.
Trace() is used to display messages in the Output panel
while testing your SWF file. Do a CTRL + ENTER right now and
it should pop up the Output panel, displaying the data from
our arrays.
for (var i = 0; i<nTotalButtons; i++) {
navHolder_mc.createEmptyMovieClip("navButton"+i, i );
// make new MovieClip and set to newly created button
var navButton:MovieClip = navHolder_mc["navButton"+i];
// load images
image_mcl.loadClip( astrImages[i], navButton );
}
Recall that nTotalButtons was set in frame 2
and is equal to 4 -- the total number of buttons. Also
notice that navHolder_mc is already on our canvas in the
navHolder layer.
Now, we use a 'for loop' to create our 4 buttons. Within
navHolder_mc, we use the createEmptyMovieClip() method to
create a movie clip that will hold our buttons.
var navButton:MovieClip = navHolder_mc["navButton"+i];
This creates a movie clip called navButton and sets it equal to your newly created movie clip. This just makes life easier: we can simply refer to navButton, instead of navHolder_mc["navButton"+i], now.
image_mcl.loadClip( astrImages[i], navButton );
We tell the loadClip() method to load the
button image (from our array) into the newly created movie
clip.
Now, remember that image_mcl was defined in frame 1 as a
MovieClipLoader, and MovieClipLoader allows us to implement
listener callbacks that let us know when our image has
completed loading.
mclListener.onLoadInit = function(target_mc:MovieClip) {
The onLoadInit handler is invoked after the
actions in the first frame of the clip have executed, so you
can begin to manipulate the loaded clip. This handler is
called after the onLoadComplete handler. For most purposes,
use the onLoadInit handler.
At this point, we have a new movie clip for our button with
our image loaded into it. Now let's set some properties.
target_mc.linkURL = astrLinks[nCounter];
This attaches a variable called 'linkURL' to
target_mc movie clip; target_mc is automagically set to the
object that called this function; in this case it's our
movie clip button. Don't believe me? Trace it!
Note: 'Automagically' is a technical term for when something
happens automatically without you doing anything and you
can't really explain how it happens! Feel free to use it
around the office -- it will catch on.
Next, we set linkURL equal to the first link in our array.
target_mc.onPress = buttonClick;
This attaches the onPress event handler to our button. In other words, when the button is clicked, it will call the buttonClick function.
var buttonImgHeight:Number = target_mc._height;
var buttonImgWidth:Number = target_mc._width;
We define a couple of variables: one stores our button's height, while the other stores its width.
target_mc._y = Math.round( yPosition );
yPosition += buttonImgHeight+3;
We move our button's vertical position (y position) down by 3 pixels each time we create a new button.
target_mc.createTextField( "buttonText", nCounter, 0, 0, buttonImgWidth, buttonImgHeight );
This creates a new, empty text field within our button movie clip. The createTextField() method takes 6 parameters:
createTextField(instanceName:String, depth:Number, x:Number, y:Number, width:Number, height:Number);
Now that we have created a new text field (buttonText), we're going to assign it a few properties.
target_mc.buttonText.border = false;
target_mc.buttonText.selectable = false;
target_mc.buttonText.wordWrap = true;
The border property for our text field is currently set to false; however, during development I often set this to true, as it makes positioning things a little easier. In addition, we want our text to wrap, but we don't want it to be selectable.
target_mc.buttonText.styleSheet = my_css;
This line assigns our CSS style, which we imported already. Notice the next line:
target_mc.buttonText.htmlText = "<.buttonStyle>"+ astrText[nCounter] +"</.buttonStyle>";
Here, you can see how to implement our CSS style. Recall that .buttonStyle is defined in our external CSS file. Also notice that we're using the htmlText property -- not just text.
target_mc.buttonText.filters = filterArray;
This line assigns our filter to the text
field. Recall that we created a drop shadow filter back in
frame 1. If you don't have Flash 8, you won't be using any
filters.
These next few lines of code are pretty cool; they come in
very handy when trying to align text vertically.
target_mc.buttonText.autoSize = true;
var nMiddle:Number = ( buttonImgHeight/2 );
target_mc.buttonText._y = nMiddle - ( target_mc.buttonText._height/2 );
We first set the autoSize property of our
text field to true. This allows the text field size to match
the height and width of the text that's placed in it. Next,
we find the middle of our button and store it in nMiddle. If
we divide buttonImgHeight by 2, we should find the middle of
our button. Now we can set the y position of our text field
to be in the middle of our button. To do so, take nMiddle
and subtract our text field's height divided by 2.
Now we've vertically aligned our text in our buttons
dynamically.
And now, the previous section! You are almost done.
nCounter += 1;
We increment nCounter by 1 for each button that's generated.
image_mcl.addListener(mclListener);
The addListener method attached to our
MovieClipLoader object allows it to receive notification
when our image is fully loaded.
The last bit of code is our button click event handler.
function buttonClick():Void {
trace( this.linkURL );
}
This function executes when a button has
been pressed. We run a trace on the button's LinkURL
property, which we set earlier. If you CTRL + ENTER to pop
up the Output panel, then proceed to click some buttons, you
should see the associated SWF file displayed in the Output
panel.
This is as far as we'll go in this article. If we were to
continue, the next step would be to load the SWF file using
another MovieClipLoader, just as we did earlier for the
image files. However, we've now done enough to get the
point. What is the point? Well, let's review.
We've achieved a lot in this article. We've learned how to
import XML into Flash, then parse it and store it in arrays.
We learned how to import CSS and assign it to text fields to
apply formatting. We also have learned how to use the new
filter feature in Flash 8 to apply a drop shadow effect. All
this is good, but it's not the main point.
What I want you to take away from this article is the way in
which we structured our Flash site. Everything that makes up
our Flash site resides outside Flash: our external XML file
holds the text that's used for the buttons, the image that's
used for the buttons, and the link reference for the
buttons. Our external CSS file holds formatting information
for the buttons.
Keeping everything outside Flash allows our site to be
completely dynamic -- we can make changes at any time and
not ever have to touch our Flash file. As a bonus, it also
keeps our SWF file extremely small and fast loading.
Also, we were in complete control of the Flash playhead the
entire time. We moved the Flash playhead to the next frame
on our own terms, not Flash's, which can help to avoid a lot
of frustration. Many errors are made in Flash because
developers allow the playhead to run wild. We never allowed
that to happen; we systematically controlled the playhead.
Frame 1 set global variables, then we stopped at frame 2,
where we loaded in XML data. Once complete, we stopped at
frame 3 and loaded CSS data, and on completion, we stop at
frame 4 and build our application.
I hope the principles presented in this article will allow
you to better structure your next Flash site. If you have
any questions, please post on the forums at
http://www.kirupa.com/forum
Mark runs Search-This.com, a resource site for Internet marketing and web development. He can also be found at his blog, What a Savage.
|
|
Mark Angeletti |
Just a final word before we wrap up. What you've seen here is freshly baked content without added preservatives, artificial intelligence, ads, and algorithm-driven doodads. A huge thank you to all of you who buy kirupa's books, became a paid subscriber, watch the videos, and/or interact on the forums.
Your support keeps this site going! 😇
:: Copyright KIRUPA 2026 //--