1

I have 4 instances of the same basic button object (a rectangle). The button Class is called LinkButton, with the Name of LinkButton, and each instance is button_1, _2, _3, and _4. I am using the following ActionScript:

import flash.net.navigateToURL;
import flash.net.URLRequest;
import flash.events.Event;
import flash.display.MovieClip;
import flash.events.MouseEvent;
import fl.motion.MotionEvent;

this.button_1.addEventListener(MouseEvent.CLICK, fl_ClickToGoToWebPage("http://google.com"));
this.button_2.addEventListener(MouseEvent.CLICK, fl_ClickToGoToWebPage("http://adobe.com"));
this.button_3.addEventListener(MouseEvent.CLICK, fl_ClickToGoToWebPage("http://amazon.com"));
this.button_4.addEventListener(MouseEvent.CLICK, fl_ClickToGoToWebPage("http://ask.com"));

function fl_ClickToGoToWebPage(url:String) {
    navigateToURL(new URLRequest(url), "_blank");
}

When I publish and run from a web server, a new (blank) page opens to google.com - I don't click, it just opens automatically. None of the other pages open, however. And, of course, I really only want these URLs to open on a click event. What have I done wrong here?

Jon Mitten
  • 1,965
  • 4
  • 25
  • 53

1 Answers1

3

The reason for this, is when you add a callback for an event, you pass it a reference to a function.

What you are doing however, is passing the result of the function. (so at the time you add the listener, you are actually running the method fl_ClickToGoToWebPage with the supplied values.)

What you want is this:

this.button_1.addEventListener(MouseEvent.CLICK, fl_ClickToGoToWebPage);

You can pass the data (url) a few different ways.

  1. use a switch statement in the handler:

    This solution is pretty good, it's simple though perhaps not as encapsulated as other ways.

    //you get a reference to the button that was clicked with the event's current target property.
    function fl_ClickToGoToWebPage(event:Event) {
        var url:String;
        switch(event.currentTarget){
            case button_4:
                url = "http://ask.com";
                break;
    
            case button_3:
                url = "http://amazon.com";
                break;
    
            //etc for the other buttons
        }
        navigateToURL(new URLRequest(url), "_blank");
    }
    
  2. Assign a dynamic property to each button.

    This solution is the most encapsulated, as the data is attached to the actual button, but dynamic vars are prone to spelling mistakes etc and won't be checked by the compiler.

    //if button_1 is a MovieClip or other dynamic class, you can just make up a property on it like this:
    this.button_1.url = "http://google.com";
    this.button_1.addEventListener(MouseEvent.CLICK, fl_ClickToGoToWebPage);
    
    //..repeat for the rest of the buttons
    
    
    //then, in the handler, the event.currentTarget is a reference to the button that was clicked:
    
    function fl_ClickToGoToWebPage(event:Event) {
        navigateToURL(new URLRequest(event.currentTarget.url), "_blank");
    }
    
  3. Create a class file for your LinkButton and add a public url property

    This is most elegant solution, but for just one property it may be a bit overkill. Create a file called LinkButton.as and place it next to your .fla.

    Add this to the file:

    package {
        public class LinkButton extends MovieClip {
            public var url:String;
        }
    }
    

    Though, even better, would be the encapsulate all the functionality of the LinkButton into it's own class file:

    package {
        public class LinkButton extends MovieClip {
            public var url:String;
    
            public function LinkButton(){
                this.addEventListener(MouseEvent.CLICK, clickHandler);
            }
    
            private function clickHandler(e:MouseEvent):void {
                if(!url) return;
                navigateToURL(new URLRequest(url), "_blank");
            }
        }
    }
    
    //this way, all you have to do in your timeline is set the url of each button.
    

    Now you have a url property for all LinkButton's, use this property the same way as the previous solution. The difference, is now you have compile time checking.

  4. Use an annoymous function as the return value

    This solution is dangerous and prone to memory leaks if you have the need to add listeners dynamically or need to remove/re-add listeners.

    The idea is, you call a function with your parameters, that then returns another function used as the handler.

      this.button_1.addEventListener(MouseEvent.CLICK, createButtonHandler("http://google.com"));
    
      function createButtonHandler(url:String):void {
          return function(e:Event){
              navigateToURL(new URLRequest(url), "_blank");
          }
      }
    

    The problem here, is that you have no way of referencing this function again. As such, it will stay in memory forever even if you remove your button from the screen and null it's placeholders (vars).

BadFeelingAboutThis
  • 14,445
  • 2
  • 33
  • 40
  • How can I pass a variable URL argument for each of the buttons I want? – Jon Mitten Aug 04 '15 at 19:09
  • Okay, thanks. With your solution and looking up http://stackoverflow.com/questions/13486230/to-pass-a-parameter-to-event-listener-in-as3-the-simple-way-does-it-exist , I discovered that a calledback function would require a return value in order to accept arguments from the callback. New code is now `function fl_ClickToGoToWebPage(url:String):Function { return function(e:MouseEvent):void { navigateToURL(new URLRequest(url), "_blank"); }; }` – Jon Mitten Aug 04 '15 at 19:19
  • Yes, that works, but anonymous functions attached to listeners is a bad bad idea unless you like memory leaks. If your app is simple and only ever attach the listener one time, it won't be a problem, but they are best avoided and not a good habit to get into. – BadFeelingAboutThis Aug 04 '15 at 19:23
  • What would you do instead? – Jon Mitten Aug 04 '15 at 19:25
  • 1
    I updated with loads more details about various way to accomplish this. – BadFeelingAboutThis Aug 04 '15 at 19:47
  • The switch statement option works for the most part. – Jon Mitten Aug 04 '15 at 21:33
  • It's not a dynamic solution, which is a future step. – Jon Mitten Aug 04 '15 at 23:48
  • 1
    if you're loading those urls dynamically, you're going to want to want to use one of the middle two solutions. (creating a `url` property on the button). – BadFeelingAboutThis Aug 04 '15 at 23:58