AS3 delete object when clicked - actionscript-3

I want to set an object to null when it's being clicked and I'm trying to implement this code:
public function onClick(evt:MouseEvent):void{
var thisPatient = evt.target;
thisPatient = null;
}
However, the element is still on the stage.

public function onClick(evt:MouseEvent):void{
var thisPatient = evt.target;
(thisPatient as DisplayObject).parent.removeChild(thisPatient);
//or if thisPatient is this
parent.removeChild(this);
}
But it's bad practive to allow children to remove itself. More right solution is dispathing event because parent must decide remove or not remove child.
public function onClick(evt:MouseEvent):void{
dispatchEvent(new Event("removeMe", true));
}
//parent's code...
child.addEventListener("removeMe", removeHandler);

Setting it to null doesn't suffice. You also have to remove it from its parent container using removeElement() or removeChild() depending on what kind of container you're using.

You just have to do removeChild(thisPatient) and if you put the object inside another object you have to do parent.removeChild(thisPatient)!

In my experience, Register for a events object, you must remove all of the events. all events will be removed completely manually. removeChild on the object that will not release all of the events. the removeChild but, finely memory leak occurs. This is because you did not remove the event. Before you remove an object, you must remove the event.

Related

AS3 MouseEvent and weakReference

Ok, here's a weird thing:
I have a class, which is a MovieClip that has 2 children, MovieClips also.
I add the children to him and base MovieClip to stage.One of the children is animated.
All is perfect.
Now when I add MouseEvent.MOUSE_UP on the children, all works fine.
Yet if I set useWeakReference to true (the 5th parameter) mouse event does not fire anymore,but the items are on stage. Basically, somehow, they are not in the memory.
Of course if I add a simple onEnterFrame that does nothing to base MovieClip, it traces the MovieClip, yet the MouseEvents does not trigger. That means the object is still there, but somehow for flash is not
Now, this is a simplified concept, that is easy to clean, but my code is very big and a simple removeEventListener is not a solution. At least not a simple one.
What are your suggestions to work around this?
I'm not sure how complex your code is, but if each movieclip has MOUSE_UP event handler - some function, you could indeed use removeEventListener MOUSE_UP function. For instance:
var mc:MovieClip = new MovieClip();
mc.addEventListener( MouseEvent.MOUSE_UP, onMU );
function onMU(e:MouseEvent){
var target = MovieClip(e.currentTarget);
target.removeEventListener( MouseEvent.MOUSE_UP, onMU );
}
This way you can have multiple movieclips and remove listeners without knowing object name.
Alternatively you could modify your code to add aray of all added events and then listen to REMOVE_FROM_STAGE event. Something like this:
var mc:MovieClip = new MovieClip();
mc.events = [];
mc.events.push( { evt: MouseEvent.MOUSE_UP, fn: onMU } );
mc.addEventListener( MouseEvent.MOUSE_UP, onMU } )
//or use events array reference to keep events and functions in one place.
//when object is removed you can iterate through events array and automatically remove
//all listeners
Another alternative would be to create Class that extends MovieClip - but since your code is huge you probably don't want to do that.
You can also look on Robert Penner's Signals library, which is interesting alternative to AS3 events. (https://github.com/robertpenner/as3-signals)

How can I recover an object who fires an eventListener event in AS3?

How can I access to an object who fires an eventListener event?
Let's say I have a mc:
var element = new MovieClip();
which has an eventlistener:
element.addEventListener(MouseEvent.CLICK, elementEventHandler);
And then, in the event handler, I want to add something to my mc:
function elementEventHandler(event:MouseEvent):void
{
var b1:balloon = new balloon("ballon1"); //this is another class.
event.target.addChild(b1);//this doesn't work.
}
So that is what I want to achieve... Recover the object who fired the event and then do crazy things with it (in this example, add another object in it).
If anybody has any idea, thanks in advance!
pd: yes, I know I can directly use the var element in this snippet, but in the real code I'm generating the mcs in a loop, according to a xml file.
function elementEventHandler(event:MouseEvent):void
{
// use the as-operator to cast the target into the class you need
var element:DisplayObjectContainer = e.target as DisplayObjectContainer;
// if the cast fails, element will be null, then we bail
if(!element) return;
// then, create your child and add it
var b1:balloon = new balloon("ballon1");
element.addChild(b1);
}
The reason you're getting an error is probably that the event is not coming directly from element but instead from one of its descendant objects.
"click" is a bubbling event.
Check out event flow in the DOM Level 3 Events spec to understand how the capture, target, and bubbling phases work:
http://www.w3.org/TR/DOM-Level-3-Events/#dom-event-architecture
So here's what I would do:
function elementEventHandler(event:MouseEvent):void
{
if (event.target != event.currentTarget)
// If event is not from "element", ignore it.
return;
...
}

Is this the correct way to remove a timer?

I'm not sure if the way I did makes the garbage collector remove the timer. Here are my two functions:
public function newWave() {
var callTimer:Timer = new Timer(800);
callTimer.start();
leftToSpawn = 4;
callTimer.addEventListener(TimerEvent.TIMER,waveCall);
}
public function waveCall(e:TimerEvent) {
leftToSpawn--;
if(leftToSpawn <= 0){
e.target.stop();
e.target.removeEventListener(TimerEvent.TIMER_COMPLETE,waveCall);
}
spawnEnemy();
}
Thanks
To remove an event listener you need to remove it with the exact same signature.
If you do:
.addEventListener(TimerEvent.TIMER,waveCall);
Then you need to use the same event type and function to remove it:
.removeEventListener(TimerEvent.TIMER,waveCall);
Using TimerEvent.TIMER_COMPLETE here will try to remove a listener that doesn't exist, which is silently ignored.
Using target here is ok, for other listener types you may need to use currentTarget, which is always the object the listener got added to. For example in a mouse click event, target could be a child of a MovieClip and without any listeners.
Moreover, timer already has ability to repeat specified number of times and the correct code will be like this:
public function newWave() {
var callTimer:Timer = new Timer(800, 4); //repeat 4 times
callTimer.start();
callTimer.addEventListener(TimerEvent.TIMER,waveCall);
}
public function waveCall(e:TimerEvent) {
spawnEnemy();
}
just change:
e.target.removeEventListener(TimerEvent.TIMER_COMPLETE,waveCall);
to
e.target.removeEventListener(TimerEvent.TIMER,waveCall);

checking if child exists

Hello i have a function as following:
private function seatClickHandler(e:MouseEvent):void{
var check:Check = new Check();
if(e.target.contains(check)){
e.target.removeChild(seat);
}else{
e.target.addChild(check);
}
}
basicly i want to check if e.target contains a child called check. If it does i want e.target to remove the child, else i want to add the child. But the method i tried doesnt seem to work although i think this is the way to go. Any suggestions?
When you declare your Check object, Actionscript creates a reference code for that specific object.
So the first time your code is run, your Check object could be given a reference of #c0ecc29. Your if statement checks to see if #c0ecc29 is a child component of target. It won't be, so the Check object with reference #c0ecc29 is added to target.
The second time the clickHandler is called, a new instance of the Check object is created which will have a new reference id. Your target has the original Check object with the #c0ecc29 reference so it won't get removed.
The correct way to get this working depends on what target is (DataGrid, Group, etc.).
EDIT:
Based on your comments, I would try something like this. It checks to see if the Check object is a child of target and adds it if needed. Then when the Check object is clicked, it will toggle its visibility.
public var check:Check = new Check();
private function seatClickHandler(e:MouseEvent):void
{
if(!e.target.contains(check))
{
check.addEventListener(MouseEvent.CLICK, check_handleClick);
e.target.addChild(check);
}
}
protected function check_handleClick(event:MouseEvent):void
{
check.visible = !check.visible;
}
If you need to actually remove the Check object from target instead of just changing its visibility, you could try this:
public var check:Check = new Check();
private function seatClickHandler(e:MouseEvent):void
{
if(!e.target.contains(check))
{
e.target.addChild(check);
}
else
{
e.target.removeChild(check);
}
}
If the child is named 'check' then you should be able to use getChildByName(). See flash.display.DisplayObject.name
If you happen to have the child in memory, you can use getChildIndex()
check is a new object in the scope of that function, so it will not be a child of the event target.
What you want to do is declare check as a global variable (And also cast target as DisplayObjectContainer).
e.g.
private function seatClickHandler(e:MouseEvent):void{
if((e.target as DisplayObjectContainer).contains(check)){
(e.target as DisplayObjectContainer).removeChild(seat);
}else{
(e.target as DisplayObjectContainer).addChild(check);
}
}
However I'm not sure if this is exactly what you want to do (There can only be one check). A better approach would be to have a function (maybe toggleCheck) on the target, and have that display object responsible for rendering the check (And removing it)
This worked perfectly fine for me in my situation:
if(possibleChild.parent == holder)
holder.removeChild(possibleChild)
It may or may not be exactly what you're looking for.

Is there a way for listening for changes in flash.display.DisplayObjectContainer numChildren property?

I want to run some code whenever a DisplayObject is added as a child to a DisplayObjectContainer.
Or to put in other words, to catch the addedToStage event of all DisplayObjects, even ones I don't know about.
Is it possible? and if not, any ideas on how to do something similar?
An 'added' event is dispatched whenever a child display object is added to the display list via addChild() or addChildAt(). In the DisplayObjectContainer class add the listener:
addEventListener(Event.ADDED, onAdded);
and the handler:
private function onAdded(e:Event):void
{
trace('number of children is now ' + numChildren);
}
Using Event.ADDED_TO_STAGE on stage Object and setting useCapture to true.
More info on event here
Example:
function onAdded(e:Event):void{
trace(e.target.toString()); //use target to get the Object added
}
stage.addEventListener(Event.ADDED_TO_STAGE, onAdded, true); // set capture to true
I don't know if there is a built in way to do this.
Alternatives include the obvious,
private var _num_children:Number = 0;
addEventListener(Event.ENTER_FRAME, _checkChildren, false, 0, true);
private function _checkChildren($evt:Event):void {
if (this.numChildren != _num_children) {
_num_children = this.numChildren;
// There was a child (or more) added in the last frame execution
}
}
However, this seems like a more elegant solution...
public function _addChild($do:DisplayObject) {
$do .addEventListener(Event.ADDED_TO_STAGE, _childAdded);
addChild($do );
}
private function _childAdded($evt:Event) {
// do whatever with $evt.target
}
The difference here, is the _childAdded will get fired for each and every child added via _addChild method. This means if you are doing some costly code execution you will be doing it once for each child instance.
If you use the first method, you are only calling the method once per frame, and if 10 images are added on a single frame, then it will only run once.