The power and genius of mouseChildren and mouseEnabled.
Jan 20
I admit, this post will be useless for most readers of this blog. But if you happen to be an Actionscript programmer… I had a minor epiphany today from which you might benefit.
Since I started writing AS3 code, I’ve generally viewed the mouseChildren and mouseEnabled properties of the InteractiveObject class (and by extension the Sprite class) as relatively superfluous. They existed as little more than a recourse for when something accidentally got ‘in between’ the mouse and a button that needed to be pressed. And in a sense that is true. But I realized today that those crafty AS3 creators had bestowed upon me a much more substantial tool than I’d first realized.
Fundamentally, these two properties are straightforward. Setting a Sprite’s mouseEnabled property to false tells that Sprite to not bother interacting with the mouse. In other words, to stop dispatching MouseEvents such as MouseEvent.CLICK. Likewise, setting a Sprite’s mouseChildren property to false tells the children of that Sprite to stop dispatching MouseEvents. By default, both properties are set to true.
The magic comes by virtue of the fact that these two properties work independently of each other. If a Sprite has children, setting that Sprite’s mouseEnabled to false does not prevent the Sprite’s children from dispatching MouseEvents. Similarly, setting the Sprite’s mouseChildren to false doesn’t prevent the sprite itself from dispatching MouseEvents. Why is this cool? Let me count the reasons:
- Sprite ‘flattening’. It used to drive me nuts that that a button made of multiple sub-elements (label, hilight, border, icon, etc) would have an indeterminate MouseEvent ‘originator’. That is, the
targetproperty of the MouseEvent would be ‘label’ or ‘highlight’ or whatever depending on the exact pixel over which the mouse rested, rather than simply ‘myButton’. I found myself putting invisible ‘mousetrap’ layers in my buttons to sit on top of everything to fix this. It turns out I don’t need to. By settingmyButton.mouseChildren = falseall the button parts become a single unit, collectively dispatching MouseEvents with atargetof ‘myButton’. - Group disabling. Need to disable a whole group of buttons? Make them siblings and set the parent Sprite’s
mouseChildrentofalse. - Event-bubbling simplification. By setting a container Sprite’s
mouseEnabled = falsebut leavingmouseChildren = true, you prevent the ‘middleman’ from sending unnecessary MouseEvents to high-level MouseEvent handlers. Imagine a calculator, with multiple keys, a display screen, a surrounding ‘case’, a nice little Texas Instruments logo, whatever. If I add aMouseEvent.CLICKlistener to the high-level calculator object, I will receive all of the various key clicks, but I will also receive events from the other child elements… the display, the case, the logo. Even if I set those elements tomouseEnabled = false, the calculator Sprite itself will still trigger its own MouseEvents when I roll over them. By following up withcalculator.mouseEnabled = false, the high-level listener will now only receive events from the children that matter, and will not be encumbered with ‘false positives’.
Given this greater understanding, I almost wonder why the AS3 creators chose to set the default of mouseEnabled to true. After all… aren’t there generally far more ‘passive’ display objects on the stage than interactive ones? I’m sure they had a reason, and it’s probably one that I could find by Googling around a bit… but enough is enough. Hardcore geek session over.
@astorria:
If you’re working in AS3, and you’re using a Loader instance to load the sub-movie, you can do it like this:
var myLoader:Loader = new Loader(); // this represents your loader instance
stage.addChild(myLoader);
By using the addChild method of the stage, you are adding your Loader instance (and the movie it contains) to the front of the stage’s display list, which will put it in front of the clickTag. Of course, that brings the ENTIRE subclip to the front, which may not be what you intend. In that case, you’d have to ‘drill down’ into the subclip to grab the DisplayObject instance that represents your button, and add THAT to the stage. Something like this:
function onloadingComplete(e:Event):void {
stage.addChild(e.target.content.myButton);
}
var myLoader:Loader = new Loader();
myLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onloadingComplete);
myLoader.load(new URLRequest("mySubClip.swf"));
I can’t find an answer to a simple issue. I have a shell movieclip that loads a subclip. This is for a banner. The shell has a clicktag button that covers the entire banner. The subload has a small button in it that dispatches an event. I need them both to be clickable. Normally I would drag the small button on a layer on top of the clicktag but since they are in seperate files I can’t do that. Is there a way to have both buttons enabled and clickable? thanks!
Nice post, very informative and I like your blog for it’s many articles on as3 bugbears which are hidden from mortals, avoided by Adobe and which pounce like ninja’s when least expecting…
Θ)
Also would like to add that you must set “mouseEnabled = false” to all parent clips in order to set that property on a child movieclip.
Eg. Make myMc.bg not register mouse clicks.
// Doesn’t work
myMc.addChild(bg);
myMc.bg.mouseEnabled = false;
// Works!
myMc.addChild(bg);
myMc.mouseEnabled = false;
myMc.bg.mouseEnabled = false;
Thanks dude! This was driving me nuts. An artist had provided me with beautiful multi-layered buttons, but they would not pass the event – mouseChildren = false did it!
Those darn mouse children!
Glad to have helped!
This post was life-saver tonight on a deadline! thanks!
Hey Edd, if I understand your description correctly, the problem is that the TextField is still mouseEnabled, and therefore when the mousepointer moves onto it, you have ‘exited’ the containing MovieClip in terms of mouseEvent detection. So, the solution would be:
myOuterMovieClip.mouseEnabled = true;
myOuterMovieClip.mouseChildren = true;
myOuterMovieClip.myNestedTextField.mouseEnabled = false;
Once you have turned off mouseEnabled on the TextField, it essentially ‘flattens’ into it’s parent Movieclip’s mouseEvent detection.
Hi.
So…can you answer this one?
I have a movieclip with 3 children: a Sprite as background, a dynamic TextField and a Button. I want to be able to add MOUSE_OVER eventlisteners to the movieclip, while keeping the handcursor when I roll over the textfield and CLICK events from the button.
The thing is, if you set:
movieclip.mouseEnabled = true;
movieclip.mouseChildren = false; //to keep the handcursor while over ther textfield
.. the button will not work (mouseChildren is false).
If you set movieclip.mouseChildren = true then the button will work BUT handcursor will not show when rolling over the textfield.
Any ideas?
thanks!
Hi, just wanted to thank you for this post. I was having an issue, found your site, and was able to fix the problem. I kept wondering why just the mouseEnabled set to false alone would not work.
+1 for sprite flattening. good work man
Thanks for this detailed analysis. It really helps understand the nuances.
Yes, these are handy but bear in mind that you can still access the buttons using the keyboard. As the properties names specify they purely disable the mouse interaction.