JS function interfering with wrong timeline


(Markus Bjerre) #1

Hi!
I’m really curious on what I am doing wrong here.
The set up is simple:
• One timeline named touchgesture is showing a hand gesture on the mainscene (a guide for people using the touchscreen)
•The timeline will start after 5 seconds if a user is not active on the scene, e.g any kind of interaction.
•The timeline will go to second 0 and pause if the user interacts in some way on the scene.
•The timeline touchgesture is only being used on the main scene, there is no other scenes with the same name.

My problem:
It all works great on the mainscene, the delay of 5 seconds, the go to time and pause is flawless. However, after the user clicks on a button (which triggers timeline named buttonTL and jumps to a new scene), the new scene somehow use the same touchgesture javascript but on the MAIN TIMELINE. It has nothing to do with touchgesture timeline, but it seems like it has transfered itself.

I’m completely mindblown by this. The only thing that I could guess are the variables that somehow works on any timeline, but wouldn’t it interrupt the buttonTL timeline then?

I am sorry to say that I can’t upload the project file since it’s used within the company but here is the javascript:

function touchGestureTimeOut(hypeDocument, element, event) {

var startOver = function () {
hypeDocument.continueTimelineNamed('touchgesture', hypeDocument.kDirectionForward);
}

window.timerHandle = setTimeout(startOver, 5000);
var resetTimer = function () {
window.clearTimeout(window.timerHandle);
timerHandle = setTimeout(startOver, 5000);
}

var hideGesture = function () {
hypeDocument.goToTimeInTimelineNamed(0, 'touchgesture');
hypeDocument.pauseTimelineNamed('touchgesture');
}

container = document.querySelector("div[id*='_hype_container']");
 
container.addEventListener("mousemove", function() { resetTimer(); hideGesture(); }, false);
container.addEventListener("touchmove", function() { resetTimer(); hideGesture(); }, false);
container.addEventListener("mousedown", function() { resetTimer(); hideGesture(); }, false);
container.addEventListener("touchend", function() { resetTimer(); hideGesture(); }, false);
container.addEventListener("touchenter", function() { resetTimer(); hideGesture(); }, false);
container.addEventListener("touchstart", function() { resetTimer(); hideGesture(); }, false);

}

(Markus Bjerre) #2

I’m somehow starting to understand this problem now.
The thing is that since there is no timeline called touchgesture on scene 2 the javascript will interact with all the other timelines on the scene. I tried different ways to solve it and I found out that by creating a timeline with the same name (touchgesture) on the second scene I got rid of the problem.

This is not a clean solution since the javascript is still triggered by all the eventlisteners. It will not cause any problem when the new timeline is not in use, but I’m sure it’s a big memory leak.

Any idea on how to rewrite the code so it only works for the first scene?


(stephen) #3

If you use a timeline name that isn’t present in the scene we will choose the Main Timeline. We’ve talked about changing this behavior, but are worried about breaking backwards compatibility.

You could add an On Scene Unload action that removes the event listeners.


(Markus Bjerre) #4

Hi again Stephen! Thanks, that explains the problem that I have. Would you take a minute to tell me how that clearing of event listeners would look like? Perhaps an example code would be really awesome.


(Mark Hunte) #5

I tried writing the removeListener() But for the life of me I could not get it working with the similar structure you are using.

What I did get to work was cloning the element with the listeners and then replacing the original with the clone.

This has the benefit of removing all the elements listeners in one go. I found this trick on Stackoverflow. Where in the comments there had concerns about orphaned elements. But it was also pointed out the Modern browser would take care of the garbage.

Others may have a better idea of this than me.

In this demo Scene 1 using onLoad adds a listener on a box (id demo) that when mouse over it shows a random number.

Going to scene 2 and using unload (scene 1) clones the box and replaces it. the mouse over no longer works.

The box is a symbol. So if the listeners was still there it would still have the mouse over working on it.

The Box retains it’s id. So going back to scene 1 will re add the listener.

Clone element and remove Listners.hype.zip (82.2 KB)


(stephen) #6

It should look like this:

var div = document.getElementById('div');
var listener = function (event) {
  /* do something here */
};
div.addEventListener('click', listener, false);
div.removeEventListener('click', listener, false);

(Mark Hunte) #7

@stephen , pretty sure that is what I had tried. Just tried again and It still does not work.

I even tried the call to removeListener in the same function that I had the addlistener in and checked the event.type to detect HypeSceneUnload and HypeSceneload events the to direct the correct part of the function

The function below was on load and unload:

var container =	hypeDocument.getElementById("demo");
 

 if (event.type == "HypeSceneLoad" ){
var resetTimer = function () {
hypeDocument.getElementById("demo").innerHTML = Math.random();
}
 
	
 
	container.addEventListener("mousemove",  resetTimer , false);
	 
	}
	
	if (event.type == "HypeSceneUnload" ){
	
	  console.log("HypeSceneUnload")
	
	container.removeEventListener("mousemove", resetTimer,  false);
	
	
	 
	} 

And this did not work. Maybe I am doing something silly and cannot see the wood for the trees.
I did use the original way markus did the “var listener…” but above I tried it. And again the listener gets added but not removed.


(stephen) #8

The main thing is that you need to call removeEventListener with the exact same handler, so you need to store it in a place you can access it again later. In your case resetTimer is no longer in scope when HypeSceneUnload is called. Here is an example that stores the handler in a global variable:

var container =	hypeDocument.getElementById(hypeDocument.documentId());
if (event.type == "HypeSceneLoad" ){
	window.myFunction = function () {
		console.log('mouseMoved');
	}
	console.log("HypeSceneLoad");
	container.addEventListener("mousemove",  window.myFunction, false);
}

if (event.type == "HypeSceneUnload" ){
	console.log("HypeSceneUnload");
	container.removeEventListener("mousemove", window.myFunction, false);
}

(Markus Bjerre) #9

I tried both of your solutions and the function still tries to invoke the wrong timeline on the next scene.
I’m not sure on how to solve this without adding a clean unused timeline with the exact same name on the nextcoming scenes. Isn’t there any way to make my code only be working on 1 scene? I just need it active on the first scene (main).


(Mark Hunte) #10

Ah,

That would be it then. :smile: Thanks


(Mark Hunte) #11

If both our methods work here then it should for yours. There is something else going on.

Can you duplicate the project and in the duplicate remove the stuff you are not allowed to post. (replace it with something else if need be) and post the project.


(Markus Bjerre) #12

Well it’s most likely that I didn’t understand neither of you, got confused when there were two different projects in the comments :slight_smile: Anyway, here is a REALLY small portion of the project, with different colors and everything. Don’t mind checking the css because nothing is being used in this version.

Anyway, I got two major javascripts. One that resets the timeline on scene 1, and one that is refreshing the page after 10 seconds on scene 2.

As you already know, javascript that resets the timeline on scene 1 is interfering with main timeline on scene 2.
What I want is that this javascript shall not be active on scene 2, instead the other javascript made for this scene will be the only active one (it has the same keybinds, yes).

I hope my project is not confusing you @MarkHunte and I really appreciate you taking your time. You’re always the one who’s helping me and I’m glad that you’ve not been getting tired of me yet :smile:

timelineproblems.hype.zip (34.4 KB)


(Mark Hunte) #13

Ok,

You are right you code is a little confusing. :smile:

If I understand what you are trying to do then I do not think the interference is from the listeners.

I think the problem is you are setting the page location back to the index page in your attempt to reload scene 2.

i.e in your SceneTimeOut function you have.

var startOver2 = function () {
window.location.href = window.location.href;

}

I think this is actually setting the windows location to the index page. With a hype page the location afaik will always be the starting index page. Not …index/scene2

Although (again if I am understanding things right ) I would probably try and control the timeout only using a time line and not Javascript.

if you change that code to

 var startOver2 = function () {
  hypeDocument.showSceneNamed('scene 2', kSceneTransitionInstant, 1.1)

}

Then that will reload the scene.

Here is roughly how i would do it with just timelines and no JS.

controledByTimeLines.hype.zip (43.7 KB)


(Markus Bjerre) #14

Hi again @MarkHunte! I see what you’ve done in your version but the thing is that I need the overlay (touchgesture) to be hidden or removed when you use the mouse or touch in any way. Then it shall start again after 5 seconds (in the real app it will be around 2 minutes).

When you go to another scene, the overlay (touchgesture) is not present. What I have here is a new timeout function which makes the page restart, a simple refresh of the page after the user have not been moving or interacting for around 2 minutes.

The problem is that the javascript for the first scene (touchgesture) is interfering with the other scenes, making them think that the javascript shall be running on this scene as well. If I don’t add a timeline called touchgesture on all the other scenes, the javascript will automatically think it shall be used on the main timeline instead.

So, the only thing I need is to find a way to use the touchgesture javascript to ONLY work on scene 1. The rest is easy to fix for me.

Thanks for helping out mate