How to access a variable declared inside of an eventhandler - javascript

I'm trying to edit an old object (workout) that was previously created. In the eventHandler I'm accessing the values of the old object and I want to now replace these old values with newly inputted values, but I can't access the old values from the old object outside of the event handler.
const fields = document.querySelector('.workout--running');
fields.addEventListener('click', function (e) {
e.preventDefault();
let workOut = e.target.closest('.workout');
if (!workOut) return;
let workout = app.#workouts.find(work => work.id === workOut.dataset.id);
console.log(workout);
(I'm trying to access workout outside of the eventhandler)
I've tried refactoring the code and giving the variables a common parent scope, but I keep getting errors. I've also tried export{} which I could have implemented incorrectly as I don't fully understand it.
app.#workouts is an array of old workouts. Here is an example with both variables declared in the parent method:
const fields = document.querySelector('.workout--running');
let workOut;
let workout;
fields.addEventListener('click', function (e) {
e.preventDefault();
workOut = e.target.closest('.workout');
if (!workOut) return;
workout = app.#workouts.find(work => work.id === workOut.dataset.id);

Related

innerHTML pass in variable to function

I have a table which is pulling data from firebase:
r.insertCell(23).innerHTML = doc.data().username;
r.insertCell(24).innerHTML = doc.data().uid;
r.insertCell(25).innerHTML ='<button onclick="activateUserInTable('+doc.data().uid +')">Activate User</button>';
I have a function:
function activateUserInTable(userId) {
db.collection("users").doc(userId).update({
activated: 1,
});
}
I have a button in my table:
r.insertCell(25).innerHTML ='<button onclick="activateUserInTable('+doc.data().uid +')">Activate User</button>';
Im trying to pass doc.data().uid into the button above in the table.
But this doesn't seem to work. This function does work if its outside the table and i manually pass in a uid. I want to be able to pass in the data from firebase into the function while the button is in the table.
It's best to avoid setting event handlers via text assigned to onxyz attributes. It has several issues, not least that you have to create the text correctly (quotes are easily confused or mismatched), the browser has to parse it as code, and any functions you use have to be globals.
Instead, just use a function you create right then and there and attach it via addEventListener:
r.insertCell(23).innerHTML = doc.data().username;
r.insertCell(24).innerHTML = doc.data().uid;
const button = document.createElement("button");
button.type = "button";
button.addEventListener(
"click",
activateUserInTable.bind(null, doc.data().uid)
);
button.textContent = "Activate User";
r.insertCell(25).appendChild(button);
(I've used bind there so we're capturing the value of doc.data().uid as of when we create the function, not later when the function is called.)
That said, I think I'd avoid repeatedly calling doc.data(). Also, I'd avoid the XSS vulnerability that treating username as HTML might provide (and the same for uid, though I'm guessing it's a less likely vector) by treating them purely as text via textContent rather than innerHTML. Something like this:
const { username, uid } = doc.data();
r.insertCell(23).textContent = username;
r.insertCell(24).textContent = uid;
const button = document.createElement("button");
button.type = "button"; // I added this, it wasn't in your HTML, but note that
// the default `type` of `button` elements is
// `"submit"`. Only matters if this is in a `form`...
button.addEventListener("click", () => {
activateUserInTable(uid);
});
button.textContent = "Activate User";
r.insertCell(25).appendChild(button);
(I don't need bind there and can use the easier to read [IMHO] arrow function because we've already captured the value of doc.data().uid to the uid constant.)

Javascript Syntax Confussion

// The keys and notes variables store the piano keys
const keys = ['c-key', 'd-key', 'e-key', 'f-key', 'g-key', 'a-key', 'b-key',
'high-c-key', 'c-sharp-key', 'd-sharp-key', 'f-sharp-key', 'g-sharp-key', 'a-
sharp-key'];
const notes = [];
keys.forEach(function(key){
notes.push(document.getElementById(key));
})
// Write named functions that change the color of the keys below
const keyPlay = function(event){
event.target.style.backgroundColor = "#ababab";
}
const keyReturn = function(event){
event.target.style.backgroundColor = "";
}
// Write a named function with event handler properties
function eventAssignment(note){
note.onmousedown = keyPlay;
note.onmouseup = function(){
keyReturn(event);
}
}
// Write a loop that runs the array elements through the function
notes.forEach(eventAssignment);
LINE-17 and LINE-18 serve similar purposes by triggering event handlers well the instructor tells me not to use this syntax at LINE-17 even though it works fine. he sort of mentions something which completely hops over my mind "we can't define note.onmousedown to the keyPlay function as it would just redefine the function (i have no idea which function is he referring to as being redefined)"
Any help would be appreciated.
First line will call keyPlay directly on mouse down, meanwhile the second one will create a function that then will call keyReturn. The second line is actually wrong as event is undefined (you have to declare it in function's input). I prefer the first line as it allows you to keep code cleaner.

find class instance by property in javascript

I'd like to retrieve an instance of some js Class with only the value of a parameter
lets say :
function myClass(id)
{
this.id = id
}
myClass.prototype.sayHello = function()
{
alert('hello');
}
myClass.instanceFromID = function()
{
...
}
var instance1 = new myClass(22);
var instance2 = new myClass(33);
var instance3 = new myClass(232);
var instance4 = new myClass(1533);
I would love to be able to access the instance by some method like
myClass.instanceFromID(33).sayHello();
I've been thinking of 2 solutions but they seam overkill :
First idea:
storing all the instances in an array, (global scope or static to the class) and iterating over all of them to find and return the instance, but this implies to keep track, add and remove the instances from the array.
Second idea:
make each instance listen to a custom event triggered from the document and compare the data emitted with inner parameter to check if it is concerned and emit a signal back to the document.
How can I achieve this in a simple way or is there no other way than these 2?
Based on what you've written, having the class itself keep track of instances with an instance variable seems to be the approach you're looking for. Of course, this means that instances will never be garbage collected unless you explicitly release them. But perhaps that isn't an issue for you. Here's how I would tackle this:
function MyClass(id) {
this.id = id;
MyClass.instances[id] = this;
}
MyClass.instances = {};
MyClass.instanceFromId = function(id) {
return MyClass.instances[id];
}

Can you access an object-defined property in a private method in JavaScript?

I am building out a library that is roughly laid out as follows. There are a couple of variables, an event handler and a method which results in the related event firing. Finally I extend the deal with a property. It goes like this (I've added some ///... where I've clipped out other code):
$.collidR = function (options) {
/// ...
var hubName = 'CollidRHub';
var hubProxy = connection.createHubProxy(hubName);
/// ...
hubProxy.on('registrationComplete', function (username, hasChanges) {
$(window).triggerHandler(events.onRegistrationComplete, { username: username, hasChanges: hasChanges });
log(username + " has successfully registered for this entity.");
// capture current user
this._currentUser = username;
// hook for catching up when user joins after edits
if (hasChanges) {
log("There are outstanding changes for this entity...");
}
});
/// ...
this.registerClient = function () {
/// does some stuff that makes registrationComplete fire
/// ...
};
};
Object.defineProperty($.collidR.prototype, "currentUser", {
get: function () {
return this._currentUser ? this._currentUser : "";
}
});
Note that above the this._currentUser = username bit does not seem to work. I think it's a problem with encapsulation, which is what this question is building towards.
In a separate but related library I create an instance of collidR, and I need to respond to an event. I've got the handler for that set up as follows:
$(window).on(collidR.events.onEditorsUpdated, function (e, data) {
/// ...
users.forEach(function (user) {
var currentUser = collidR.currentUser;
// here, currentUser is always default of ""
if (user != currentUser) {
/// ...
}
});
});
Here is what I am seeing:
My registrationComplete event fires and the handler is called successfully
From a debugger, this._currentUser is undefined before the value is set
The line this._currentUser = username is executed and the value is set
When the onEditorsUpdated event fires, collidR.currentUser is always the default value (an empty string) in my handler
What feels out-of-order is where I define the property -- after the rest of the object. It's as though I'm altering the prototype of the object after I define a method which tries to reference the property...this can't be right.
I've also tried this.currentUser (in the internal method), but had the same result.
I had assumed that if the prototype was extended before the internal method was invoked that when I did var currentUser = collidR.currentUser; that I would get the value from the property, but it's always an empty string.
Is there a way to register the property earlier?
Is there a correct way to set the value so that I will be able to access it later via the exposed property?
Because this in this._currentUser = username; isn't what you think it is. The value of this in JavaScript depends on how the function was called. I assume that inside the handler, it's now referring to hubProxy or some other object other than collidR.
Assuming that your entire plugin's this refers to collidR (which I highly suspect isn't, explained in the section after this), what you can do is save the context of that scope into another variable outside the handler. That way, you can refer to the outer scope's context via that variable:
// Saving this scope's context
var that = this;
hubProxy.on('registrationComplete', function (username, hasChanges) {
// access _currentUser via the saved context
that._currentUser = username;
});
However, I should warn you about using this. Assuming you are creating a plugin, you'd be calling it like $.collidR({..}). In this case, the this inside the function will refer to $ (which I assume is jQuery) and you are attaching some property to the library. It's risky to attach plugin-specific values to a global library as collisions might occur.
I suggest you store it inside a local variable/object instead.

Object Reference Persisting Beyond Variable Declaration

I'm building up a client-side collection of data to post to a server. Using the onClick event of an Add button, I'm collecting the form data and storing it in an internal array. This is some faux/sample code to try to demonstrate the issue (I can't share the source, unfortunately).
var getDataFromForm = (function() {
var _internal = {}; /* Based on feedback, I think this is the reason for the behavior I'm experiencing */
return function() {
var form = {};
/* Get the form data */
_internal.name = form.name;
_internal.id = form.id;
return _internal;
};
}());
var internal = { vals: [] };
function onClick() {
var newData = getDataFromForm();
doAjaxValidation({
success: function() {
internal.vals.push(newData); /* All values in array have been updated to this newData reference on click */
internal.vals.push( JSON.parse(JSON.stringify(newData)) ); /* ugly, but the reference is broken and future clicks work as expected */
internal.vals.push( $.extend({}, newData) ); /* reference to the object is still persisted through jQuery's extend */
}
});
}
I'm confused as to why the newData reference is handed to the new assignment of newData each button click. Each click event is a new function call and newData (theoretically) should be a new object which is not related to the newData from any other button click.
What am I missing?
Edit: this sample is from a much larger block of code, and I tried to reduce it to the simplest possible expression. Obviously, as-is, this is not a functional piece of code.
It sounds as though you're passing an object,and expecting a copy.
In JavaScript, objects themselves are never copied in an assignment. It's a by value language, but in the case of Objects, the value that is copied is the reference to the object. So returning newData from getDataFromForm will result in a copy of the reference to the same object in each onClick.

Categories