Calling function on custom event in javascript - javascript

i want to bind an event handler to my (dynamic) javascript object, but it doesnt work how i think.
Do you have an idea how i could solve it?
What i want is to call an object function on a custom event. Or in other words i want to bind an event listener to an update function of my object.
For example i have my demo object:
var Demo = function (name) {
this.name = name;
var eventCounter = 0;
this.updateCounter = function () {
eventCounter++;
}
//...
};
Then i create multiple dynamic instances from it, like for example
var names = ["Jon", "Doe", "Max", "Mustermann"]
for(var i in names) {
new Demo(names[i])
}
and sometimes i want to update this Demo instance with a custom event.
document.dispatchEvent(new CustomEvent("CUSTOMEVENT"));
so my question is, how can i achieve something like the following
document.addEventListener("CUSTOMEVENT", function(){
// how to get the dynamic Demo instance?
// i want something like Demo.updateCounter()
});
My idea was to push the demo object into an array like
var names = ["Jon", "Doe", "Max", "Mustermann"]
var elementsToUpdate = [];
for(var i in names) {
elementsToUpdate[i] = new Demo(names[i])
}
and then in the event listener i can access them by
document.addEventListener("CUSTOMEVENT", function(){
elementsToUpdate[i].updateCounter()
});
But it feels a little ugly to me. So maybe there is a better solution for updating a dynamic instance of an object.
Thanks in advance

Related

Module pattern in JavaScript

I'm practicing my JS skills (I'm new at it). I'm trying to get the specific element that triggered the event and display it in a span element. But I don't know what I'm doing wrong, when I click the button nothing happens.
This is for a calculator program that I'm doing but using module pattern I think it's called.
var Calculator = {
init: function(){
var button = document.querySelectorAll("[class^='button']");
button.onclick = this.writeEvent;
},
write: function (element){
document.getElementById("display").innerHTML = element;
},
writeEvent: function(event){
write(target.event)
}
}
Calculator.init();
There are several problems with the posted code.
var button = document.querySelectorAll("[class^='button']");
button.onclick = this.writeEvent;
The result of querySelectorAll is a NodeList.
Assigning to its onclick property will not achieve what you want.
You want to assign to the onclick property of each individual node.
But actually that's not so simple, we'll need to come back to this.
writeEvent: function(event){
write(target.event)
}
One problem here is that target is undefined.
Surely you meant event.target.
Another problem is that write is also undefined.
Perhaps you meant this.write,
but that won't actually work well.
The problem is that when writeEvent is called from a click event,
it won't be called on the object,
so this will not be bound to the calculator object,
and the this.write call will raise an exception.
There's a way to overcome this,
by binding the onclick handler function to the object when setting it.
Putting the above together:
var Calculator = {
init: function() {
var nodeList = document.querySelectorAll("[class^='button']");
var callback = this.writeEvent.bind(this);
nodeList.forEach(item => item.onclick = callback);
},
write: function(element) {
document.getElementById("display").innerHTML = element;
},
writeEvent: function(event) {
this.write(event.target);
}
}
Calculator.init();

Storing Jquery reference

Is it possible to store the reference to an element in an array or object without having a unique ID on the element?
I am having trouble with storing a subtable in another table so I can reference it later. I get the table by class with this code:
$(this).parent('tr').parent().find('.tableSomeTable');
Is the only solution to have unique id's on each element and use the .selector method?
More of my code. Abit simplified.
var rows = [];
var historyLoad;
$(document).on("click", '.details-control', function (e) {
var t = $(this);
var tr = t.closest('tr');
var row = t.parent().parent().parent().DataTable().row(tr);
var id = t.closest('tr').attr('id');
var object = {
id: id,
btnHistory: t.parent('tr').next().find('#btnHistory'),
tblHistory: t.parent('tr').parent().find('.tableHistory'),
historyLoad: historyLoad
};
if ($.inArray(id, rows) > -1) {
loadData = false;
}
else {
loadData = true;
loadHistory(object);
rows.push(object);
}
};
Here is where I am having trouble retrieving the correct elements. And I also want to save the ajaxHistory element to my object (which is working fine).
This code is not working, but if I change it to $(result.btnHistory.btnHistory.selector) I get the object. But this doesn't seem to work very good with multiple rows.
function loadHistory(result) {
result.ajaxHistory = $.ajax({
...
beforeSend: function () {
$(result.btnHistory).html(<loading txt>);
$(result.tblHistory).find('tbody').html(<loading txt>);
},
....
success: function (data) {
if (data.TotalRecordCount > 0) {
$(result.tblHistory).find('tbody').html('');
$.each(data.Records, function (e, o) {
$(result.tblHistory).find('tbody').append(<data>)
});
}
else {
$(result.tblHistory).find('tbody').html(<txt no records>);
}
$(result.btnHistory).html(<txt loading done>));
},
First off, if you are trying to find the parent table, try
var $myObj = $(this).closest('table.tableSomeTable');
instead of navigating parents.
As far as storing the jQuery reference, define a variable and then store it. The above will store the object in $myObj but that is locally scoped. If you need a global variable then define it globally and just assign it here. If you want to define it as a property within an object then define it that way. It really comes down to a scope question at this point.
EDIT: Just saw your added content.
First off, don't name it 'object'. This may run into key word issues. Use var myObj or similar instead. Next, object.btnHistory is a reference to a jQuery object. So, when you pass it to loadHistory, you do not need to apply the $(...) again. Just use the reference directly: result.btnHistory.html(...). A good habit to get into is prepending you jQuery variables with $ so you remember it is already a jQuery variable.
The .find() method returns a jQuery object. So the answer is, yes, you can store this return object in a variable:
var $yourObject = $(this).parent('tr').parent().find('.tableSomeTable');

Javascript - Using a string to prototype functions

So I am a super JS noob, I am not even sure I am asking this question properly, but...
I am trying to dynamically create buttons, then set their onclick event listeners to one of a set of predetermined functions. I want to be able to do this one of two ways, as follows:
var newInput = document.createElement('input');
newInput.parameters = "xyz";
document.getElementById("element").appendChild(newInput);
newInput.onclick = "funcsnip" + array[variable] + "()";
Or, alternatively,
var newInput = document.createElement('input');
newInput.parameters = "xyz";
document.getElementById("element").appendChild(newInput);
newInput.onclick = array[variable];
Where the array is stocked with "myFunction()" primitives.
I have no doubt there are a million things I "should" be doing, but I am happy with the majority of my code, I just want to know basically how to pass a string as a parameter for onclick, if that is possible.
Or, if it is not possible, another way to attach an onclick event to a dynamic input element.
Simpler and more elegant, the better. Thank you!
You can't pass a string to onclick listener. It should be a function. What you can do alternatively is to create a map of functions.
Let's say you have these functions available:
function oneFunc() { console.log('one');
function twoFunc() { console.log('two');
Then you can create a mapping:
var map = {
one : oneFunc,
two : twoFunc
};
So you can access oneFunc by calling map.one or map["one"].
Now solving your problem is straightforward
var newInput = document.createElement('input');
var variable = "one";
// var variable = "two";
newInput.parameters = "xyz";
document.getElementById("element").appendChild(newInput);
// You pass an actual function, but using a string parameter.
newInput.onclick = map[variable];
When you click a button, you will see "one" in the console.
If you have a string for every button that represents a function, you can write
var functionCollection = {
'stringOne': function() {
...
}
}
...
newInput.onclick = functionCollection[myStringValue];
or you could create one handler function and invoke it with the string type through binding, e.g.
function clickHandler(stringValue) {
switch (stringValue) {
case 'stringOne':
return someSpecialFunction();
...
}
}
...
newInput.onclick = clickHandler.bind(this, myStringValue);
Both newInput.setAttribute("onclick", "alert(1)") and newInput.setAttribute("onclick", "function() {alert(1) }" will work. Your code doesn't work because you're trying to set a function string on the onclick property, which expects a true function object.
Having said that, you really should use addEventListener, or an abstraction like jquery's on.

JS method binding with 2 different this

I have this small code:
ScenesController.prototype.viewAction = function() {
this.flash = this.di.HelperFlash.hasSupport();
this.$playerElem = !this.flash? $('#html_player') : $('#flash_player');
// the first click is just a sample, I need the same object in the Quailty Change method
$('.scenes_view_video_quailty').on('click', function() { echo($(this));});
$('.scenes_view_video_quailty').on('click', this.viewVideoQuailtyChange.bind(this));
};
ScenesController.prototype.viewVideoQuailtyChange = function(e) {
e.preventDefault();
if (!this.flash) {
echo(this);
echo($(this));
}
};
When I'm clicking the link, I would need pass 2 this variable to the QualityChange method. One with the object (in the bind) and the other is the click event, because I need the clicked element too.
I was trying with the .on('click', {$this: $(this)}, this.method) solution, but dosen't work, the evend.data.$this looks a different object.
I need the same object as I have in the first click method.
(echo = console.log)
Alias the this that refers to the current instance as something else (traditionally, self) and use this to refer to the clicked element
ScenesController.prototype.viewAction = function() {
var self = this;
this.flash = this.di.HelperFlash.hasSupport();
this.$playerElem = !this.flash? $('#html_player') : $('#flash_player');
$('.scenes_view_video_quailty').on('click', function() { echo(self, $(this));});
};
To call a method setting it's this reference you would use Function.apply, for example:
$('.scenes_view_video_quailty').on('click', function(){
self.viewVideoQuailtyChange.apply(self, [$(this)])
});
You can attach any number of variables with bind
You could have a proper method like:
ScenesController.prototype.viewVideoQuailtyChange = function(secondThis) {
}
then use the bind as:
this.viewVideoQuailtyChange.bind(this, $(this));
With this solution you do lose the event though, so it might need some more thought. I'll look into it and update the answer :)

Event/Class Design Pattern (Prototype)

I've bumped up against this before and I need the help of someone smarter than me!
How can I attach an event of an element to a method of a class? More specifically, the same class? Is there a better way?
Using a Qt widget style approach, I've been trying to create self-contained "widget" objects in javascript. I'm using the Prototype javascript framework and building my widget as a class. Then, using Event.observe, I've tried to attach the event to a method of the class. But the Event assignment unbinds the assigned method. Here's an example of a simple table I'm trying to build that has clickable column headers:
Objectify.Grid.Table = Class.create({
initialize: function(headers) {
this.columns = headers;
this.rows = [];
},
addRow: function(GridData) {
var len = this.rows.push(GridData);
return len-1;
},
getRow: function(rowIndex) {
return this.rows[rowIndex];
},
build: function(parent) {
this.mainTable = new Element('table',{'class':'Objectify-Grid'});
$(parent).update(this.mainTable);
var tableBody = new Element('tbody',{});
this.mainTable.update(tableBody);
var headerRow = new Element('tr',{'class':'Objectify-Grid-header-row'});
tableBody.update(headerRow);
this.columns.each(function(val,id) {
var hcell = new Element('td',{'class':'Objectify-Grid-header-cell'}).update(val);
headerRow.insert(hcell);
// EVENT ASSIGNMENT //
hcell.observe('click',this.respondToClick);
/////////////////////
}.bind(this));
this.rows.each(function(GridData,id) {
var row = new Element('tr',{'class':'Objectify-Grid-row','id':'Objectify-Grid-row'+id});
tableBody.insert(row);
this.columns.each(function(columnName,index) {
var cell = new Element('td',{'class':'Objectify-Grid-cell'}).update(GridData.getValue(columnName));
row.insert(cell);
});
}.bind(this));
},
// RECEIVING METHOD //
respondToClick: function(event) {
var columnName = event.element().innerHTML;
// "this" is no longer bound in this method
this.sortColumnAsc(columnName); // [ERROR]
}
});
I think that you should do this :
hcell.observe('click',this.respondToClick.bindAsEventListener(this));
and the second argument of the "each" method is the object to bind the function to, so you can do this:
array.each(someFunction, this);
instead of
array.each(someFunction.bind(this));
I'm not too familiar with Prototype, but what does this refer to in the respondToClick event handler? If I had to guess, I'd say it referred to the clicked DOM element, and not the object instance you wanted.
Perhaps you can assign the grid to a property the DOM element and reuse that property during the respondToClick function, as in:
myDOMElement.someProperty = grid;
//and later
this.somePropery.sortColumnAsc(columnName);

Categories

Resources