Museum Pieces:  on() and onClipEvent()

Flash ActionScript 2.0

ActionScript has evolved by leaps and bounds in the last few years.  The commercial release of Flex 2.0, and the public release of Flash Professional 9 ActionScript 3.0 Preview, in particular, nudge ActionScript 3.0 that much closer to the world in which we Flashers live.  The less-than-adventurous (nothing wrong with that!) won’t see AS3 until the commercial release of Flash 9 — still a long way off, as of this writing.  But when that does happen, our world will change.  That really isn’t an exaggeration.  There will be those who code in AS1/AS2 — there will still be plenty of room for that — and those who code in the jaw-droppingly more complex AS3.  The Flash 9 Player, already upon us, now features two virtual machines:  one that displays Flash 8 and prior content, and one that displays Flex and Flash 9 content.  That’s a first.

And yet, there’s still an awful lot of on() and onClipEvent() going on out there.  Is that so terrible?  Well, those event handling mechanisms were introduced in Flash 5.  In spite of their age, they haven’t been formally deprecated, so they’re perfectly “legal,” strictly speaking.  I can even muster a pragmatic smile for anyone who strongly feels that “if it ain’t broke, don’t fix it.”  But honestly, dot syntax event handling offers more control than that directly applied to the object.  Not only that, but the newer approach lets you organize your code into one place.  Let’s take a look. 

The old way

The on() handler is primarily aimed at button symbols, but it works with movie clips, too.  In Flash 5, it was necessary to attach the following to one of those objects (that is, you would actually click the button to select it, then open the Actions panel and enter code):

on(release) {
 // instructions here
}

This configuration specifically handles the release event, which occurs when the mouse is clicked, then released, over the button (or movie clip).  Other events include press, releaseOutside, rollOut, rollOver, dragOut, dragOver, and keyPress.

The onClipEvent() handler is geared at movie clip symbols, and only works with movie clips.  The code arrangement for this handler is the same:

onClipEvent(enterFrame) {
 // instructions here
}

This configuration handles the enterFrame event, which occurs at a rate more or less equivalent to the SWF’s framerate.  Other events include load, unload, mouseMove, mouseDown, mouseUp, keyDown, keyUp, and data.  Note that mouseUp is not the same as releaserelease is only fired when the mouse is released while hovering over the object in question; mouseUp is fired if the mouse is released anywhere.

Just remember, you can use on() with movie clips if you like. In effect, move clips handle all the events available to buttons, and then some.  But it gets even better.

The “new” way (new since Flash MX)

The dot notation approach appears as if it requires more typing — and truthfully, it does — but not by much.  In any case, it’s just as easy to code.  In this scenario, the ActionScript goes in a frame script rather than direct attachment.  Make sure your button or movie clip has an instance name (select it and look at the Property inspector; you’ll find it), then use that instance name in the following manner:

instanceName.onRelease = function() {
 // instructions here
}

In your code, “instanceName” will be replaced with the actual instance name you chose.  You’re simply calling the object by name and telling it what to do when a certain event is raised.  You may either use a function literal, as shown, or declare a named function and assign that, instead.

instanceName.onRelease = namedFunction;
function namedFunction():Void {
 // instructions here
}

Benefits of the new way

Again, why is this better?  Let me count the ways.

  1. 11 button events, versus the previous 8
  2. 18 movie clip events, versus the previous 9
  3. Code may be maintained in a single frame, if desired
  4. Events may be assigned, deleted, and changed programmatically

Okay, okay, four bullet points isn’t a boat load, but it’s certainly enough reason to make the switch, if you haven’t.

Points 1 and 2 speak for themselves.  To see all the events available to either object, look up the Button class and MovieClip class entries, respectively, in the ActionScript 2.0 Language Reference.

Point 3, to my thinking, is a significant improvement over the old Flash 5 approach.  When you have twenty buttons in front of you, it’s much more convenient to click inside a single frame to see everything you’re after than be obliged to click on each button individually.

Point 4 is perhaps the least obvious, but don’t overlook it!  Remember those twenty buttons?  What if most of them do essentially the same thing, with the one difference depending only on labeling?  What if you have forty buttons?  Is there an easier way than to write forty event handlers?

With the new approach, if you like, you can assign similar event handler functions with a for statement.  For example, if your buttons send the playhead to respective frame labels, get creative and match your instance names with those labels:

var buttons:Array = new Array("uno", "dos", "tres");
for (var i:Number = 0; i<buttons.length; i++) {
 this[buttons[i]].onRelease = function():Void {
  _root.gotoAndPlay(this._name);
 }
}

In the above, an arbitrarily named variable, buttons, points to an Array instance that contains three strings.  A for statement uses another arbitrarily named variable, i, to count from zero to the number of items in the array (thanks to the Array.length property).  Inside this loop, a pair of nested array access operators, [], resolves the array’s strings into button instance names; the Button.onRelease handler of each button, in turn, is instructed to send the main timeline to a frame label that shares the same name as the button instance itself.  (For further details on this technique, see this article on referencing objects dynamically).

In addition, you can delete event handlers, simply by using the delete statement or by setting the object’s event to null.  The following lines are functionally identical.

delete uno.onRelease;
uno.onRelease = null;

Why is this useful?  Well, if you’re using the MovieClip.onEnterFrame event to animate a clip …

clip.onEnterFrame = function():Void {
 this._x += 5;
}

… you can stop animating it by removing the function.  If you want the clip to stop moving when it hits the right site of the Stage, for example …

clip.onEnterFrame = function():Void {
 if (this._x >= Stage.width) {
  this.onEnterFrame = null;
 } else {
  this._x += 5;
 }
}

With onClipEvent(enterFrame), you’d have to change that += 5 increment to an increment of zero, instead.  Yes, the enterFrame function would keep ticking — after all, there’s nothing stopping it — but that’s just how it used to be.  It’s unnecessary processing, and enough of that sort of thing can slow a SWF down.

So when you’re digging through FlashKit.com or any other online sample code repository, keep in mind, you don’t have to use what you find 100% as is.  Break it apart, use it for inspiration, sure, but by all means, feel free to update it.  ;)

19 Responses to “Museum Pieces:  on() and onClipEvent()”

  1. NSurveyor Says:

    Also, knowing what this is in a on() handler can be tricky. With the code:

    on(release){
      trace(this);
    }

    on a MovieClip, it will trace that movieclip, but on a Button, it will trace the parent clip.

    Similarly, when you don’t use this,

    on(release){
      trace(_x);
    }

    on a MovieClip, will return the _x of that movieclip, while on a Button, it will trace the _x of the parent clip.

    So for buttons, how do you make them reference themselves? The only way is to put the instance name within the handler, e.g. for my_btn:

    on(release){
      my_btn._alpha-=10;
    }

    will decrease the _alpha of the button everytime you press it.

    In dot-syntax, it makes more sense. this will reference the instance that the event handler belongs to, whether it be a Button, a MovieClip, or a TextField. However, when you drop the this, it’s a little different. It will look from the timeline on which you have placed the code. For example, say you have the clip, asdf_mc, in which you have placed the following code:

    _root.abc_btn.onPress = function(){
      trace(_x);
    }

    and of course, abc_btn on the _root timeline, it will output the _x of asdf_mc, not the _root’s and not abc_btn’s (whether it is a button or a movieclip).

    The nice thing about this is that it is constant with MovieClips, Buttons, and TextFields, unlike the on() handler.

  2. David Stiller Says:

    All the more reason to leave on() behind. :) Great points, NSurveyor. Thanks for the input!

  3. Bowser Says:

    delete uno.onRelease;
    uno.onRelease = null;

    Actually, these two produce different results… I’m not sure if it’s a bug or not, but in any case the NULL statement will remove the functionality of the onRelease, but still shows the flash “hand” on rollover.

    You have to use the DELETE statement to get rid of the whole shebang.

    Granted it took some anal-retentiveness to care enough to find out about this problem and its solution.

  4. David Stiller Says:

    Bowser,

    Wow, good catch! That’s definitely a point worth making. In my own experience, both delete and null leave the hand cursor intact, so I’m curious how you saw a difference. To my thinking, the best way to avoid the hand cursor when it isn’t wanted is to set the Button.enabled (or MovieClip.enabled) property to false for the object in question. That leaves the event handler intact, but disables it (and the hand) until that property is again set to true.

  5. Nuander Says:

    However in MX 2004, mc.eventhandler does not work with mc.loadMovie, while onClipEvent does. example
    mc.onLoad = function() {trace(this._width)}
    mc.loadMovie(”images/image.jpg”)
    …will not work. In fact …
    mc.loadMovie(”images/image.jpg”)
    trace(mc._width)
    …may not work, even if you wait for the image to complete loading. But…
    onClipEvent(load) { trace this._width }
    will always work.

    Comments?

  6. David Stiller Says:

    Nuander,

    However in MX 2004, mc.eventhandler does not work with mc.loadMovie, while onClipEvent does.

    As it turns out, MovieClip.onLoad works just fine. It’s just that this event works differently from the others — and not the way most people expect it to. The documentation isn’t especially clear in this case (my opinion, of course), but look carefully at the entry for MovieClip.onLoad: ” … You use this handler only with movie clips for which you have a symbol in the library that is associated with a class.” In other words, this event must be handled in collaboration with a class that extends MovieClip. I don’t know why, in particular, that one event is different, but them’s the brakes. ;)

  7. 3D Says:

    i got this problem!it might be a nooby problem!

    The below code is an action of a movie clip with instance name of “button_mc”,the problem is when its click,it should stay highlight unless a onReleaseOut had been made,it returns to its normal state.

    the problem is :
    When the button is hightlight and i rolledOut then Over again!the button_mc results a mili-second glitch of OVER state Befor seeing its action script!

    ill copy paste the report down under the actionsciprt,it had been driving me crazy!

    ========================================
    var Hit:Boolean;

    this.button_mc.onEnterFrame = function() {
    if (Hit == true) {
    button_mc.gotoAndStop(”_on”);
    }
    };

    this.button_mc.onReleaseOutside = function() {
    trace(”——–OUT RELEASE——–”);
    Hit = false;
    button_mc.gotoAndStop(”_Out”);
    }

    _root.button_mc.onRollOut = function() {
    if (Hit != true) {
    trace(”—-(Hit != true) is TRUE—> _OUT”);
    button_mc.gotoAndStop(”_out”);
    } else {
    trace(”Disable _OVER”);
    button_mc.gotoAndStop(”_over”);
    } else {
    trace(”Disable “+Hit);
    };

    ======================================

    ——————OUTPUT————————

    Button Status: _Out
    Button Status: _OVER
    —-(Hit != true) is TRUE—> _OVER
    HIT ====>true
    Button Status: Enabled
    Button Status: _OVER

  8. David Stiller Says:

    3D,

    My hunch is, your problem is the result of an unnecessary use of the MovieClip.onEnterFrame event. You have so many other useful mouse-related events! — in fact, you’re already using many of them. ;)

    Rather than waste CPU cycles on a constant if (Hit == true) loop, check for that Boolean in either onRelease, onReleaseOutside, or maybe both. That’s the only time it matters, right? And that way, your code doesn’t depend on a repeating loop, but rather, on a nice, orderly sequence of events, as needed.

  9. 3D Says:

    Woh!what a nooby mistake :P so i guess i was looping the whole onEnterFrame code!with its button movie clip code re-checking!!
    damn :P well ppl learn from their mistakes! :D

  10. 3D Says:

    Well i’d like to ask something!but since this isnt a forum!i was wondern where can i feel free to ask stuff..?

  11. David Stiller Says:

    3D,

    Haha, that’s how it works … if you find yourself making a mistake, just pick yourself up and keep trying. You’ll figure it out, with enough determination. :) The official Adobe forums are good, for sure. And you may also want to check out the new Keyframer forums, hosted by my friend Chris Georgenes.

  12. Elkapitan Says:

    Hi,

    I hope im allowed to ask this here. My question would be how to ‘reenable’ that null button? for example i have a button with rollover effect that nulls after user press. but I want it reactivated again to see that rollover effect.

  13. David Stiller Says:

    Elkapitan,

    That’s a good question. Since you used the word “reenable,” I’d like to point out that the MovieClip and Button classes each feature an enabled property, which can be used to toggle an instance’s responsiveness. That isn’t the same as deleting or nulling the function assigned to an event, because the event still occurs when enabled is false; it’s just that the object ignores it.

    In order to re-assign an event handler you’ve deleted or nulled, you merely have to repeat what you did in the first place (namely, to assign the event to a function). Obviously, this can get a bit tedious, depending on how often it needs to happen. To save on retyping, I recommend you use a named function rather than a literal function.

    function doSomething():Void {
      // code here
      // This is where you tell this function
      // what what to do.
    }
    
    mcButton.onRelease = doSomething;
    // Now we've assigned the doSomething()
    // function to the MovieClip.onRelease
    // event of the mcButton instance.

    At some point later in the ActionScript, you may get rid of the event handler by deleting it …

    delete mcButton.onRelease;

    And if you ever want to add it again, simply to this …

    mcButton.onRelease = doSomething;

    Notice the lack of parentheses after doSomething, both here and above (the first time the event is handled). That’s because the parentheses actually cause the function to run — and you don’t want to trigger the function here; at this point, you’re merely assigning it to something.

  14. markc Says:

    Looks like flash is moving towards developers rather than designers.

  15. David Stiller Says:

    markc,

    It’s hard for me to judge that assessment, because I really do enjoy both aspects of Flash: designing and developing. If Flash has been moving toward developers (programmers) — and I certainly don’t deny that; just not sure how strongly I would affirm it — the steps away from on() and onClipEvent() began three full versions ago, back with Flash MX in 2002.

    The nice part is, if you prefer that paradigm, all you have to do is configure your publish settings for AS1 or 2. Even in Flash CS3, you can stick with the approach that makes you the most comfortable.

  16. Battletards Says:

    Hey, so how would you code a
    onClipEvent(enterFrame) {
    using your method?

    I see you give an example of a mouseUp command, but not an enterFrame command.

  17. Battletards Says:

    and what about onClipEvent (load)?

    Thanks

  18. David Stiller Says:

    Battletards,

    To handle the enterFrame event as described above, you would reference the movie clip by its instance name and pretty much code it up the same way:

    instanceName.onEnterFrame = function():Void {
      // instructions here
    }

    It works the same in all cases except, as coincidence would have it, the MovieClip.onLoad method. For that, check out my “Is the MovieClip.onLoad Event Broken?” article.

  19. Johny Says:

    Hello ive read your tutorials, and yes it works will it open the page, but not on the target iframe,

    on (release) {
    getURL(”products.html”, “main”);
    }

    this codes here opens another page but not on the target iframe, i was wondering where did i went wrong coz most forum say that this codes work,

    well what i want is the page to be open in the iframe now on another window, well thanks for the big help if you can correct this codes thanks, send me a email.

Leave a Reply