Return value of event triggered function - javascript

I'm simply implementing a function that is called after a button is clicked. The function works fine, but i'm not capable of making it return a value and assign it to a variable.
I would like to call the function play once the button is clicked, and then return the values (as shown in the code) to a variable, in order to make them accessible for future uses.
I've already implemented all I need (i'm messing around with the web audio api) and it works fine, the test1 variable "embeddeds" the osc and lfo variables and i'm able to access them outside the function.
var ac = new AudioContext();
function play(){
var osc = ac.createOscillator();
var lfo = ac.createOscillator();
osc.frequency.value=1000;
console.log("Clicked!!");
returnedObject={};
returnedObject["value1"] = osc;
returnedObject["value2"] = lfo;
return returnedObject;
};
var test1= play();
var test= document.getElementById("mybtn").addEventListener("click", play);
The test1 variable contains what i need, but the test variable doesn't.
How can i assign to a variable the returned object of a function after it's been called by an event?
What i'm getting right now if i use test.value1 is an undefined error, this makes me think that the assignment of the variable failed.

Answer:
You can do this, but you have to consider that the value is undefined until the button is clicked.
var ac = new AudioContext();
function play(){
var osc = ac.createOscillator();
var lfo = ac.createOscillator();
console.log("Clicked!!");
returnedObject={};
returnedObject["value1"] = osc;
returnedObject["value2"] = lfo;
return returnedObject;
};
var test;
document.getElementById("mybtn").addEventListener("click", () => (test = play()));
<button id="mybtn">click me</button>
In order to perform functions with the data you probably want to create a middle function that takes care of whatever work you want done:
var ac = new AudioContext();
function play(){
var osc = ac.createOscillator();
var lfo = ac.createOscillator();
console.log("Clicked!!");
returnedObject={};
returnedObject["value1"] = osc;
returnedObject["value2"] = lfo;
return returnedObject;
};
function middle(fn) {
return function() {
let data = fn();
//do something
console.dir(data);
}
}
document.getElementById("mybtn").addEventListener("click", middle(play));
<button id="mybtn">click me</button>
Alternatively you can create a Constructor that holds onto the data, thus allowing you to add methods to the Constructor for manipulation at a later time:
function Player() {
let proto = {};
proto.ac = new AudioContext();
proto.data = [];
proto.play = function() {
var osc = proto.ac.createOscillator();
var lfo = proto.ac.createOscillator();
proto.data.push({
value1: osc,
value2: lfo
});
proto.latest = proto.data[proto.data.length-1];
console.log("Clicked!!", proto.latest);
};
return proto;
}
const myPlayer = new Player();
document.getElementById("mybtn").addEventListener("click", myPlayer.play);
<button id="mybtn">click me</button>

I will suggest you to use closure instead.
Try something like this.
// closure
function audioControl() {
var ac = new AudioContext();
var props = {
value1: ac.createOscillator(),
value2: ac.createOscillator()
};
// function play
function play() {
osc.frequency.value = 1000;
console.log("Clicked!!");
props.value1 = "_modified_value";
props.value2 = "_modified_value";
}
return {
props: props,
play: play
}
};
var test1 = audioControl();
var test = document.getElementById("mybtn").addEventListener("click", test1.play);
// now you can get values anywhere anytime using
// -- test1.props.value1;

addEventListener, by design, is meant to don't return anything at all (thanks #AmitJoki for mentioning that) as you can read in the MDN docs.
The relevant part is (excerpt of this section)
Event listeners only take one argument, the Event Object, which is automatically passed to the listener, and the return value is ignored
The reason behind that is that addEventListener is just meant to register an event, not to handle the result of it.
If you want to assign the result to the global variable test, you should just do that inside your play callback.

Related

Javascript Module Pattern accessing updated parameter value

I've tried to search for an answer to my question, but I'm starting to think that, given the lack of results, I'm obviously not expressing the question properly. With that in mind, apologies if the title of this post is misleading, I am still very much learning.
I have a simplified version of my code below
var testData = ['a', 'b']
var addReceiver = (function () {
dataReceiver = function (dataInput) {
t = this
t.data = dataInput;
console.log(t.data);
t.interval = setInterval(function () {
t.update();
}, 1000);
t.stopUpdate = function () { clearInterval(t.interval); };
t.update = function () {
//t.data = dataInput;
console.log(t.data);
};
};
})()
var testLogger = new dataReceiver(testData);
</script>
The behaviour that I wish to emulate is when I type into the console
testData = ['c','d','e']
for testLogger to log the array ['c','d','e'] every second rather than ['a','b'].
I could change
t.data = dataInput to t.data = testData
to achieve this, but that would obviously be pointless, or else every new instance I created of dataReceiver would have a reference to the same data input.
So, my question is, how would I need to change the code to provide the testLogger vairable (or any instance of dataReceiver) access to a variable outside of its local scope?
Use the object that you have created instead of accessing the global variable,
var testLogger = new dataReceiver(testData);
testLogger.data = [1,2,3,4];
Now you will be able to print the newly injected data. I mean the setInterval will print the updated value.

How to declare functions after event listener occcurs

I've been trying to make this Music Playing script with the Audio element.
So what my problem is that I don't want these functions to be available before the Metadata is loaded.
I'm using this object here.
var MusicPlayer = new Object();
MusicPlayer.source;
MusicPlayer.duration = 0;
MusicPlayer.Player = new Audio();
MusicPlayer.metadataloaded = false;
What I attempted was to firstly, create a function which defines these functions and this function itself is loaded by the event listener.
function Playerinitialize()
{
MusicPlayer.metadataloaded = true;
}
Then
MusicPlayer.Player.addEventListener('loadedmetadata', function()
{
MusicPlayer.duration = this.duration;
console.log(this.duration);
console.log(MusicPlayer.duration / 60);
console.log(this.currentTime);
Playerinitialize();
});
But it doesn't work saying currentTime is undefined. So how do i achieve this moreover, I'm doing this to prevent this functions being called while Audio objects duration is NaN etc. Is this the most efficient way?
And why can't i use Prototype to add functions to MusicPlayer Object?
Please, take look at this. You will find all eventhandlers and more about Audio object you need here: Audio
I think Something like this should suit you (Edited):
function MusicPlayer(src, ready){
var player = new Audio();
var controller = { };
controller.play = function(){
player.play();
};
//add to controller all parameters you need here
player.onloadedmetadata = function() {
//do your things here: after metadata is loaded
//add to controller some more parameters
//retrieve your object:
ready(controller);
};
player.src = src;
}
var ready = function(controller){
//do with your player what you think is necessary
//via controller like:
controller.play();
}

Cannot overwrite object?

[RESOLVED]
Was calling the newGame function from an event listener so this would then refer to the event caller
I'm having this annoying issue with JS at the moment, I'm creating a game where all the variables for a particular game are stored in the Game() class/function/object and have a GameManager which creates the initial game and has a function to create a new one.
The problem is, I cannot overwrite the initial game in the newGame function, here it is:
var GameManager = {};
GameManager.currentGame = null;
GameManager.init = function() {
this.newGame();
}
GameManager.newGame = function() {
this.currentGame = new Game();
}
GameManager.init();
After running the newGame function, the result:
GameManager.currentGame == the original game, NOT the new one.
To put things in a simpler form, I've used int's here:
var GameManager = {};
GameManager.currentGame = null;
GameManager.init = function() {
this.currentGame = 12;
}
GameManager.newGame = function() {
this.currentGame = 0;
}
GameManager.init();
After running the newGame function, the result:
GameManager.currentGame == 12 (NOT 0, as expected)
I have a feeling this is an obvious issue with object referencing or something similar, I'd appreciate all responses!
Thanks

Create a function on the fly injecting local var values inside

I need to write functions on the fly while avoiding the use of global variables. They'll be 'connected' to event listener.
I currently have a function to create a connection. They receive:
The object I want to connect
Two variables: IDX and id I have to identify some element that I want to use.
This is my code so far:
create_fun = function (obj, IDX,id) {
var f = function () {document.getElementById(IDX+"_"+id).display = "block";}
obj.addEventListener('click', f,false);
}
This code runs OK. I see in the Chrome Event Listeners Panel the click event connected to the objected that I wanted.
The Problem: IDX and id are variables, and as they are not globals, they are undefined.
At chrome Eventlistener inspection I see:
handler = function ()
{document.getElementById(IDX+"_"+id).display = "block";}
But, being IDX = "sec1" and id="3", I'd like to see :
handler = function ()
{document.getElementById("sec1+"_"+"3").display = "block";}
Is this possible? How would I do this?
The fix is given by Function constructor
var f = new Function ( "document.getElementById('"+IDX+"_"+id+"').style.display = 'block';" );
obj.addEventListener('click', f,false);
}

javascript time event issue (setTimeout/clearTimeout)

I have always had trouble working with time events. Could someone please explain why A doesn't work and B does? The only difference is in A I put the event binding in a function. Don't worry about the function close, it has nothing to do with the question. When I test A, there is no js errors but timer is not cleared.
A ->
Test.Navigation = (function() {
var openTimer = null;
var closeTimer = null;
var addListeners = function() {
$('.hover_container').on('mousemove', function(e) {
clearTimeout(closeTimer);
});
$('.hover_container').on('mouseleave', function(e) {
// set the close timer
var container = this;
closeTimer = setTimeout(function() {
//has the mouse paused
close(container);
}, 750);
});
};
return {
init : function() {
addListeners();
}
};
})();
B ->
Test.Navigation = (function() {
var openTimer = null;
var closeTimer = null;
$('.hover_container').on('mousemove', function(e) {
clearTimeout(closeTimer);
});
$('.hover_container').on('mouseleave', function(e) {
// set the close timer
var container = this;
closeTimer = setTimeout(function() {
//has the mouse paused
close(container);
}, 750);
});
var addListeners = function() {
// nothing here
};
return {
init : function() {
addListeners();
}
};
})();
Edit: Please ignore the container part, it has nothing to dow ith the question it is simply part of the full code that I did not take out
A is binded before the object exists where the init is called. Because your return a new object. If you are using, 2 objects are created. 1 with the vars en binds. and 1 with the returns.
B is working because you create a function where the elements are initialized and use the right scope. A is not working because the bindings are on the wrong scope because your create 2 objects:
new Test.Navigation(); // Create 1 object
// Create second object.
return {
init : function() {
addListeners();
}
};
Youd better get a structure like this, then it should work aswell:
Test.Navigation = (function() {
// Private vars. Use underscore to make it easy for yourself so they are private.
var _openTimer = null,
_closeTimer = null;
$('.hover_container').on('mousemove', function(e) {
clearTimeout(_closeTimer );
});
$('.hover_container').on('mouseleave', function(e) {
// set the close timer,
// use $.proxy so you don't need to create a exta var for the container.
_closeTimer = setTimeout(
$.proxy(function() {
//has the mouse paused
close(this);
}, this)
, 750);
});
this.addListeners = function() {
// nothing here
};
this.init = function() {
this.addListeners();
}
// Always call the init?
this.init();
return this; // Return the new object Test.Navigation
})();
And use it like
var nav = new Test.Navigation();
nav.init();
Also as you can see I upgraded your code a bit. Using $.proxy, _ for private vars.
Your use of this is in the wrong scope for the first approach.
Try
var openTimer = null;
var closeTimer = null;
var self = this;
and then later
var container = self;
In your code for example A,
$('.hover_container').on('mouseleave', function(e) {
// set the close timer
var container = this;
this is actually referring to the current $('.hover_container') element.
Also, since setTimeout will wait before the previous setTimeout finishes to start again, you can get discrepancies. You may want to switch to setInterval because it will issue its callback at every interval set regardless of if the previous callback has completed.
My guess is that in the calling code, you have a statement new Test.Navigation() which, for B, addListeners is called at the time of new Test.Navigation(). In A, you return an object ref that calls an init function. Can you verify that init() is called?
I.e. in A, init() has to be called before the handlers are added. In B, the handlers are added everytime you instantiate Test.Navigation --- which, depending on the calling code, could be bad if you intend to instantiate more than one Test.Navigation() at a time.

Categories

Resources