I am working on an ASP.NET web application that is required to bring up a popup on a roolover. I am using the "OnMouseOver" event and it works as expected. The problem is that the event is on a "hair trigger"; even a casual passage of the mouse over the control brings up the popup (which then must be manually dismissed). I want to add a delay so that a rapid pass over the control in question does not trigger the event. Is there a way to set such a delay or is there a different event that I could use to get the same "trigger event on a slow rollover"?
One solution that comes to mind, there may be better ways though:
Make the onmouseover call the function via a setTimeout delay
Inside the function, check the mouse is actually over that element.
You could also use an onmouseout to clear the setTimeout, but then you'd have to store a reference to the timer in a global variable to get at it again.
What I ended up doing is as follows (oRow is a table row but it could be any control):
function ItemMouseOver(oRow, "parameters for the popup")
{
oRow.showTimer = window.setTimeout(function()
{
alert('popup');
}, 1000);
}
function ItemMouseOut(oRow)
{
if (oRow.showTimer)
window.clearTimeout(oRow.showTimer);
In the ASP.NET grid view RowDataBound event: I added the following code:
protected void ReportGridView_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow && (
e.Row.RowState == DataControlRowState.Normal
|| e.Row.RowState == DataControlRowState.Alternate))
{
// get the input values for the popup for the row (stuff deleted)
e.Row.Attributes["onmouseover"] = "javascript:ItemMouseOver(this,
"parameters for the popup");";
e.Row.Attributes["onmouseout"] = "javascript:ItemMouseOut(this);";
}
}
It works just fine. Thanks.
Related
I have been dealing with this problem for days already. I am at my wits' end!
I can't seem to find a definitive answer anywhere on any of the forums, documentation, etc.
Everything looks fine at first run, or when I load a next level for the user to play. But if the user hits the ESC key to load a different level, the ENTER FRAME listener does not get removed and it duplicates all the triggers in it, showing the player going really fast, and all funky, because it builds on top of the previously instantiated ENTER FRAME listener.
I don't know if I have a problem of an anonymous function, or an unknown instance being referenced in my removeEvent... command... Bottom line, I give up and I need this working HELP!!!
Here's the code:
function initPlay():void
{
//code here determining what display object to add to the list and assign it to the currentLevel variable (a movieclip)
if(userIsLoadingOtherLevel){
removeEnterFrameListener();
addChild(currentLevel);
}
if(userIsGointToNextLevel)
addChild(currentLevel);
currentLevel.addEventListener(Event.ENTER_FRAME, onEnterFrame);
function onEnterFrame(event:Event):void
{
//collision detection, parallax scrolling, etc, etc is done here.
if(allCoinsCollected)
loadNextLevel();
if(ESCKeyPressed)
ESCKeyPressHandler();
}
function loadNextLevel():void
{
removeChild(currentLevel);
newLevelToLoad++
removeEnterFrameListener();
initPlay();
}
function ESCKeyPressHandler():void
{
removeChild(currentLevel);
initPlay();
}
function removeEnterFrameListener();
{
currentLevel.removeEventListener(Event.ENTER_FRAME,onEnterFrame)
trace("currentLevel.hasEventListener(Event.ENTER_FRAME) = "+currentLevel.hasEventListener(Event.ENTER_FRAME)); //outputs TRUE if called from loadNextLevel but FALSE if called from initPlay() !!!
}
}
I also tried to add and remove the eventListener to stage, MovieClip(Root), or nothing at all and the result is always the same.
I know that there may be other ways to design such a process, but please note I am not really flexible at the moment on doing this because the project is very long (about 4000 lines of code) and removing the ENTER FRAME this way, crazy or not should still work!!
THANK YOU in advance for anyone willing to help.
The problem appears to be the nested functions inside the initPlay() method.
Each time you call initPlay() you are defining new functions. Some of these nested functions call initPlay() themselves.
Functions are objects (memory references). So each time you call initPlay() you are making new references to new functions. So when you try to remove an event listener, you're only able to remove one of these event handlers (the one in the current scope of execution).
I'm not sure if I'm explaining this clearly, perhaps this example will help. I'll use numbers to represent the references to each function, and a simple scenario that is similar to yours:
function example():void
{
addEventListener(MouseEvent.CLICK, mouseClickHandler);
function mouseClickHandler(event:Event):void
{
if (someCondition)
{
example();
}
else
{
removeEventListener(MouseEvent.CLICK, mouseClickHandler);
}
}
}
When we run this function the first time, a new function is defined within the scope of the example() function. Lets use the number 1 to represent the reference to this nested function. someCondition is true on the first time around, and so the example() function is called again.
On the second execution of the example() function, a new reference to the mouse event handler is created (#2). We also add the event listener again. At this point, there are two event handling functions in memory, and both will be executed when the event is dispatched.
Let's say that in the second invocation of example() that someCondition is false and now we want to remove the listener. When we call:
removeEventListener(MouseEvent.CLICK, mouseClickHandler);
It's referring to event handler #2. Event handler #1 still exists, and because it's hidden in the scope of the first invocation of example() it can't be removed here.
My simple example breaks down after this... but I hope it makes it clear why your event handlers shouldn't be nested inside a function. Admittedly, this is difficult to describe and even more so in a real world example like yours. But I'm pretty confident that this is the source of most, if not all, of the issues you describe.
Here's how I was able to get around this without changing the scope of the nested functions (although I agree that would be the preferred solution) by creating a boolean variable called "loadingNewGame" and changing it to true from outside the onEnterFrame (in fact, this assignment was done from initPlay() and then from onEnterframe I called removeEnterFrameListener() function. This did the trick.
here's the code in case anybody is interested:
// package, and other code here.
var loadingNewGame:Boolean = new Boolean(false);
function initPlay():void
{
//code here determining what display object to add to the list and assign
//it to the currentLevel variable (a movieclip)
if(userIsLoadingOtherLevel)
{
loadingNewGame = true;
removeEnterFrameListener();
addChild(currentLevel);
}
if(userIsGointToNextLevel)
addChild(currentLevel);
loadingNewGame:Boolean = false;
currentLevel.addEventListener(Event.ENTER_FRAME, onEnterFrame);
function onEnterFrame(event:Event):void
{
if(loadingNewGame)
removeChild(currentLevel);
//collision detection, parallax scrolling, etc, etc is done here.
if(allCoinsCollected)
loadNextLevel();
if(ESCKeyPressed)
ESCKeyPressHandler();
}
function loadNextLevel():void
{
removeChild(currentLevel);
newLevelToLoad++
removeEnterFrameListener();
initPlay();
}
function ESCKeyPressHandler():void
{
initPlay();
}
function removeEnterFrameListener();
{
currentLevel.removeEventListener(Event.ENTER_FRAME,onEnterFrame)
trace("currentLevel.hasEventListener(Event.ENTER_FRAME) = "+currentLevel.hasEventListener(Event.ENTER_FRAME));
//outputs true
}
I have two function on my AS3 program, one fires when the width and height changes:
stage.addEventListener(Event.RESIZE, resizeListener);
function resizeListener (e:Event):void {
//some commands
}
And the second one fires one a number of milliseconds pass:
var myTimer:Timer = new Timer(clockUpdate, 0);
myTimer.addEventListener(TimerEvent.TIMER, updateData);
myTimer.start();
function updateData(e:TimerEvent):void {
trace("AUTOUPDATE");
trace(e);
}
I need to fires those function also manually, lets say when the user press a button, but i don't know what parameters i have to send them when they are called manually.
I tried just resizeListener() and updateData() but of course it fails asking me for the parameter.
You can make parameters in a function optional by providing a default value. This is an example by taking your two functions above and making the event parameters optional:
function resizeListener(e:Event = null):void {
//some commands
}
and
function updateData(e:TimerEvent = null):void {
trace("AUTOUPDATE");
trace(e);
}
Calling, for example, resizeListener() will now execute the function and the value of e will default to null.
Making the Event parameter optional, resizeListener(e:Event=null), as in walkietokyo's answer, is a perfectly valid and often convenient solution. Another alternative is to put the stuff you want to be able to do without the event being triggered in a separate function, that can be called by the event handler and from anywhere else.
So assuming for example that what you want to do on resize is to rearrange the layout, and you also want to do that same layout setup at initialization, or at the click of a button, or anytime really, you could do something like this:
stage.addEventListener(Event.RESIZE, resizeListener);
function resizeListener(e:Event):void {
rearrangeLayout();
}
function rearrangeLayout():void {
// The actual rearrangement goes here, instead of in resizeListener. This can be called from anywhere.
}
Which way to do it is probably a matter of taste or can vary from case to case, really, both works fine.
A benefit of separating things in an event handler and another function is that there will not arise a situation where you would have to check if the e:Event parameter is null or not. In other words, you would have code that is dependent on the Event, if any, in the event handler, and code that is independent of the Event in a more general function (not an event handler).
So in a more general and schematic case, the structure would be something like this:
addEventListener(Event.SOME_EVENT, eventListener);
function eventListener(e:Event):void {
// Code that needs the Event parameter goes here (if any).
// Call other function(s), for the stuff that needs to be done when the event happens.
otherFunction();
}
function otherFunction():void {
// Stuff that is not dependent on the Event object goes here, an can be called from anywhere.
}
I have a numeric stepper and I want to add an event listener to its text box:
use namespace mx_internal;
durationStepper.inputField.addEventListener(Event.CHANGE,durationStepperTextInputChanged);
private function durationStepperTextInputChanged(event:Event):void
{
use namespace mx_internal;
trace(durationStepper.inputField.text);
}
However, the event function does not execute! I put a break point there and it does not reach it! What am I missing here? Thanks.
The problem is that the developer has stopped Change event from bubbling up. You can find it if you go to the source file of the NumericStepper. Here are two functions, which prevent you from getting the event.
override protected function createChildren():void
{
super.createChildren();
if (!inputField)
{
inputField = new TextInput();
//some code
//some code
inputField.addEventListener(Event.CHANGE, inputField_changeHandler);
addChild(inputField);
}
}
private function inputField_changeHandler(event:Event):void
{
// Stop the event from bubbling up.
event.stopImmediatePropagation();
var inputValue:Number = Number(inputField.text);
if ((inputValue != value &&
(Math.abs(inputValue - value) >= 0.000001 || isNaN(inputValue))) ||
inputField.text == "")
{
_value = checkValidValue(inputValue);
}
}
As you can see the second function has
event.stopImmediatePropagation();
In this case you have two options: either you should find another way of implementing your logic, or you can copy the source code of the component and eliminate this code line.
It would be fine to override the function, but it is private.
You can read about this common problem here
I have tried to choose the second way. It works perfectly! It is not only the *.as file, but some others, which are used in it.
You can download the component here.
I have a webpage with a flash header. In the flash header (block), different dropdownmenus are present together with a 'submit' button.
When the user presses 'enter', i want to submit the form.
However, I can't seem to be able to catch any key :
stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDownFunc);
root.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDownFunc);
optiesPanelNew.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDownFunc);
optiesPanelNew.ddOptiesMerk.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDownFunc);
function onKeyDownFunc(evt)
{
if (evt.keyCode == Keyboard.ENTER)
{
submitForm();
}
}
This javascript works if the html part of the page (non flash) has focus :
document.body.onkeydown = function theFunction()
{
alert("keydown");
}
Edit: Oh no, the flash file was written in AS2 ... sorry :-/
FYI : the code I ended up using was : (AS2) :
var keyListener:Object = new Object();
keyListener.onKeyDown = function():Void
{
if(Key.getCode()==13)
submitForm();
}
Key.addListener(keyListener);
I think you need to specify the event's type in the handler (i.e. KeyboardEvent) because this way the dispatched object will be cast to a Event object when its instance is passed to the handler. The Event class doesn't have the keyCode property, and subsequently the condition to submit the form is not met. That's why the form doesn't get submitted.
So, try replacing the
function onKeyDownFunc(evt)
line, with
function onKeyDownFunc(evt:KeyboardEvent)
and it should do the trick.
Have a great day.
I was trying to filter a combo box dataprovider based on the values in the text boxes . When the contents of the dataprovider changes Combo box automatically calls change event method . Please find the sample code below.
Filter Utility Function:
private function filterLocations(event:FocusEvent):void {
locationsList1.filterFunction = filterUtility;
locationsList1.refresh();
}
public function filterUtility(item:Object):Boolean {
// pass back whether the location square foot is with in the range specified
if((item.SQUARE_FOOTAGE >= rangeText1.text) && (item.SQUARE_FOOTAGE rangeText2.text))
return item.SQUARE_FOOTAGE;
}
// THIS WOULD BE CALLED WHEN COMBO BOX SELECTION IS DONE
private function selectLocationsReports(event:ListEvent):void {
selectedItem =(event.currentTarget as ComboBox).selectedItem.LOCATION_ID;
}
When the DataProvider gets refreshed its automatically calls change method and was throwing Null Pointer function because its prematurely calling the above selectLocationsReports method and its throwing error.
Can somebody let me know how to stop the CHANGE event from propogation when the dataprovider is refreshed.
You can't stop a CHANGE event, just don't add an event listener unless you are prepared to get the event. I don't see where your event listener for Event.CHANGE is in the code above.
Just be sure that you don't addEventListener(Event.CHANGE, selectLocationsReports) until your ComboBox is ready for it.
The other thing to do (on top of Kekoa's response) is put an if statement in the event handler, and check to make sure the data is there before you begin working with it.
A handy syntax I use frequently for this is
if(dataprovidername) {
}