How do I run an object's method onEvent in javascript? - javascript

I just started using javascript and I'm missing something important in my knowledge. I was hoping you could help me fill in the gap.
So the script I'm trying to run is suppose to count the characters in a text field, and update a paragraph to tell the user how many characters they have typed. I have an object called charCounter. sourceId is the id of the text area to count characters in. statusId is the id of the paragraph to update everytime a key is pressed.
function charCounter(sourceId, statusId) {
this.sourceId = sourceId;
this.statusId = statusId;
this.count = 0;
}
There is one method called updateAll. It updates the count of characters and updates the paragraph.
charCounter.prototype.updateAll = function() {
//get the character count;
//change the paragraph;
}
I have a start function that is called when the window loads.
function start() {
//This is the problem
document.getElementbyId('mytextfield').onkeydown = myCounter.updateAll;
document.getElementbyId('mytextfield').onkeyup = myCounter.updateAll;
}
myCounter = new charCounter("mytextfield","charcount");
window.onload = start;
The above code is the problem. Why in the world can't I call the myCounter.updateAll method when the event is fired? This is really confusing to me. I understand that if you call a method likeThis() you'll get a value from the function. If you call it likeThis you are getting a pointer to a function. I'm pointing my event to a function. I've also tried calling the function straight up and it works just fine, but it will not work when the event is fired.
What am I missing?
Thanks for all the answers. Here's three different implementations.
Implementation 1
function CharCounter(sourceId, statusId) {
this.sourceId = sourceId;
this.statusId = statusId;
this.count = 0;
};
CharCounter.prototype.updateAll = function() {
this.count = document.getElementById(this.sourceId).value.length;
document.getElementById(this.statusId).innerHTML = "There are "+this.count+" charactors";
};
function start() {
myCharCounter.updateAll();
document.getElementById('mytextfield').onkeyup = function() { myCharCounter.updateAll(); };
document.getElementById('mytextfield').onkeydown = function() { myCharCounter.updateAll(); };
};
myCharCounter = new CharCounter('mytextfield','charcount');
window.onload = start;
Implementation 2
function CharCounter(sourceId, statusId) {
this.sourceId = sourceId;
this.statusId = statusId;
this.count = 0;
};
CharCounter.prototype.updateAll = function() {
this.count = document.getElementById(this.sourceId).value.length;
document.getElementById(this.statusId).innerHTML = "There are "+ this.count+" charactors";
};
CharCounter.prototype.start = function() {
var instance = this;
instance.updateAll();
document.getElementById(this.sourceId).onkeyup = function() {
instance.updateAll();
};
document.getElementById(this.sourceId).onkeydown = function() {
instance.updateAll();
};
};
window.onload = function() {
var myCounter = new CharCounter("mytextfield","charcount");
myCounter.start();
};
Implementation 3
function CharCounter(sourceId, statusId) {
this.sourceId = sourceId;
this.statusId = statusId;
this.count = 0;
};
CharCounter.prototype.updateAll = function() {
this.count = document.getElementById(this.sourceId).value.length;
document.getElementById(this.statusId).innerHTML = "There are "+this.count+" charactors";
};
function bind(funcToCall, desiredThisValue) {
return function() { funcToCall.apply(desiredThisValue); };
};
function start() {
myCharCounter.updateAll();
document.getElementById('mytextfield').onkeyup = bind(myCharCounter.updateAll, myCharCounter);
document.getElementById('mytextfield').onkeydown = bind(myCharCounter.updateAll, myCharCounter);
};
myCharCounter = new CharCounter('mytextfield','charcount');
window.onload = start;

I think you are having problems accessing your instance members on the updateAll function, since you are using it as an event handler, the context (the this keyword) is the DOM element that triggered the event, not your CharCounter object instance.
You could do something like this:
function CharCounter(sourceId, statusId) {
this.sourceId = sourceId;
this.statusId = statusId;
this.count = 0;
}
CharCounter.prototype.updateAll = function() {
var text = document.getElementById(this.sourceId).value;
document.getElementById(this.statusId).innerHTML = text.length;
};
CharCounter.prototype.start = function() {
// event binding
var instance = this; // store the current context
document.getElementById(this.sourceId).onkeyup = function () {
instance.updateAll(); // use 'instance' because in event handlers
// the 'this' keyword refers to the DOM element.
};
}
window.onload = function () {
var myCharCounter = new CharCounter('textarea1', 'status');
myCharCounter.start();
};
Check the above example running here.

The expression "myCounter.updateAll" merely returns a reference to the function object bound to "updateAll". There's nothing special about that reference - specifically, nothing "remembers" that the reference came from a property of your "myCounter" object.
You can write a function that takes a function as an argument and returns a new function that's built specifically to run your function with a specific object as the "this" pointer. Lots of libraries have a routine like this; see for example the "functional.js" library and its "bind" function. Here's a real simple version:
function bind(funcToCall, desiredThisValue) {
return function() { funcToCall.apply(desiredThisValue); };
}
Now you can write:
document.getElementById('myTextField').onkeydown = bind(myCounter.updateAll, myCounter);

You can:
function start() {
//This is the problem
document.getElementbyId('mytextfield').onkeydown = function() { myCounter.updateAll(); };
document.getElementbyId('mytextfield').onkeyup = function() { myCounter.updateAll(); };
}

In ASP.Net Ajax you can use
Function.createDelegate(myObject, myFunction);

I want to do something like this but simpler.
The idea is to have the user click on bolded text and have a text field appear where they can change all the values of a role-playing character. Then when the value is changed, have the text field disappear again replaced by the updated bolded text value.
I can do this already using an annoying text box alert. But I would rather have something similar to this below to replace all that.
I have searched for months and CMS is the closest to answering my question in the simplest way with a full html example. Nobody else on the net could.
So my question is, how do I do this?
I have multiple objects(characters) and need this.elementId to make this work.
I've modified this example but it breaks if I try to add to it.
html>
head>
title>Sandbox
/head>
body>
input id="textarea1" size=10 type=text>
script>
function CharCounter(sourceId, statusId)
{this.sourceId=sourceId;
this.statusId=statusId;
this.count=0;
}
CharCounter.prototype.updateAll=function()
{text=document.getElementById(this.sourceId).value
document.getElementById(this.statusId).innerHTML=text
}
CharCounter.prototype.start=function()
{instance=this
document.getElementById(this.sourceId).onkeyup=function ()
{instance.updateAll()}
}
window.onload=function ()
{myCharCounter=new CharCounter('textarea1', 'status')
myCharCounter.start()
}
/script>
/body>
/html>

Related

Passing values from an object inside a function

I have been working all day trying to pass the value of "returnData.salary" inside the "readData" function to
the object inside the "calculateTax" function which is suppose to take the salary value and calculate state and federal taxes. I am stumped, I can't find anything on the internet which provides a good example for me to work with. The examples are either way to simple or super complex. Any help would be appreciated.
I apologize in advance if I did not submit this question in the correct format. This is my first time asking for help on stackoverflow.
function readForm() {
var returnData = {};
returnData.name = $("#name").val();
returnData.lastName = $("#lastName").val();
returnData.age = $("#age").val();
returnData.gender = $("[name=gender]:checked").val();
returnData.salary = $("#salary").val();
returnData.isManager = $("#isManager").val();
returnData.myTextArea = $("#myTextArea").val();
$("#name2").text(returnData.name);
$("#lastName2").text(returnData.lastName);
$("#age2").text(returnData.age);
$("#gender2").text(returnData.gender);
$("#salary2").text(returnData.salary);
$("#myTextArea2").text(returnData.myTextArea);
if ($(isManager).is(':checked')) {
$("#isManager2").text("Yes");
}
else {
$("#isManager2").text("No");
}
//$("#employeeForm")[0].reset();
} //end of readForm function
function calculateTax() {
console.log("Button Works");
var calculateTax = {
state: function(num) {
num *= 0.09;
return num;
}
, federal: function(num) {
if (num > 10000) {
num *= 0.2;
return num;
}
else {
num * 0.1;
return num;
}
}
, exempt: true
};
}
//Invoke readForm function when the submit button is clicked.
$(document).ready(function () {
$("#btnSubmit").on("click", readForm);
$("#btnCalculate").on("click", calculateTax);
})
</script>
Well, simply put; you can't. Not like this anyway. Or, at least not pass the value to the function directly.
You are using global functions right now, which are not inside a class. If it was inside a class, you could instantiate the class and save it to this (which would be the class' instance). However, I'm assuming classes are a bit over complicated in this case. What you could do, is set variables globally so all functions can use them, like this;
//declare the global variable so it exists for every function
var returnData = {};
function readForm() {
//We do NOT redeclare the "var" again. It's global now.
returnData = {}; //Reset the global variable when this function is called
returnData.name = $("#name").val();
returnData.lastName = $("#lastName").val();
returnData.age = $("#age").val();
returnData.gender = $("[name=gender]:checked").val();
returnData.salary = $("#salary").val();
returnData.isManager = $("#isManager").val();
returnData.myTextArea = $("#myTextArea").val();
//Rest of your function
}
function calculateTax(){
console.log(returnData) //works here
}
Note that you do overwrite global variables, so it's best to reset them on every function call. You might get old data stuck in there, otherwise.

Multiple instances of constructor not working

When I run this program with only one instance of Cat (var cat1), it works perfectly. When I run a second instance (var cat2), things stop working.
function main() {
var catPic = document.getElementById('catPic');
var numClicks = 0;
var numCats = -1;
var scope;
var Cat = function Cat(name, url) {
numCats++;
scope = this;
this.name = name;
this.url = url;
this.numClicks = 0;
this.clickId = 'clicks' + numCats;
window.onload = function() {
//Puts a new Cat on the screen. The name, image, and # of clicks will be shown.
appendCat(scope.name, scope.url, scope.clickId, scope.numClicks, numCats);
}
};
var cat1 = new Cat('Bob', 'https://lh3.ggpht.com/nlI91wYNCrjjNy5f-S3CmVehIBM4cprx-JFWOztLk7vFlhYuFR6YnxcT446AvxYg4Ab7M1Fy0twaOCWYcUk=s0#w=640&h=426');
var cat2 = new Cat('Samantha', 'https://lh3.ggpht.com/nlI91wYNCrjjNy5f-S3CmVehIBM4cprx-JFWOztLk7vFlhYuFR6YnxcT446AvxYg4Ab7M1Fy0twaOCWYcUk=s0#w=640&h=426');
}
main();
The problem has something to with the appendCat(); function.
//Puts a new Cat on the screen. The name, image, and # of clicks will be shown.
function appendCat(name, url, clickId, numClicks, numCats) {
document.body.appendChild(document.createElement('div'));
document.getElementsByTagName('div')[numCats].setAttribute('id', 'cat' + numCats);
document.body.getElementById().appendChild(document.createElement('h2'));
document.getElementsByTagName('h2')[numCats].textContent = name;
document.body.appendChild(document.createElement('img'));
document.getElementsByTagName('img')[numCats].setAttribute('id', name);
document.getElementsByTagName('img')[numCats].setAttribute('src', url);
document.body.appendChild(document.createElement('p'));
document.getElementsByTagName('p')[numCats].setAttribute('id', clickId);
document.getElementsByTagName('p')[numCats].textContent = numClicks;
document.body.appendChild(document.createElement('br'));
document.body.appendChild(document.createElement('br'));
}
When these first two lines of this function are run, document.getElementsByTagName('div')[numCats] evaluates to undefined. I'm not sure why this happens. Console.log() isn't helping me much either. Thanks!
First your scope variable needs to be declared within the constructor so that window.onload is referring to the correct instance. Otherwise, both window.onload handlers will refer to whatever value of scope was set to at the time window.onload triggers — which will always be the second instance. Second, instead of setting window.onload, you need to use window.addEventListener("load", function () { ... }") so that each instance has it's own listener. The window object only has one onload property so setting it multiple times doesn't create multiple listeners it just overwrites the previous ones.
As an additional note, I would suggest removing the event handling from the constructor and placing it outside something like this:
var cats = [
new Cat(...),
new Cat(...)
];
window.addEventListener('load', function () {
cats.forEach(function (cat) {
appendCat(cat.name, cat.url, cat.clickId, cat.numClicks, cats.length);
});
});
This would remove the scope and numCats variables and move all the window.onload funtionality to one handler instead of having one handler for each instance.

Custom object 'game' doesn't have specified method

I'm trying to call a method cardSelection() from a function game() but instead I'm getting an error report which throws back to me the whole function with a "has no method cardSelection()" The idea is to access the method through the click of a button, which HTML tag is as follows:
<img id="PlayerCard0" class="card" src="images/Cards/Mario.png" alt="Mario" title="Mario" onclick="game.cardSelection('PlayerCard0')">
I'm not posting the whole Javascript as I believe this to be the case of a mere declaration error, anyhow, game() and cardSelection() were declared as follows:
function game()
{
...
this.cardSelection = function(card)
{
var cardElem = document.getElementById(card);
var id = cardElem.getAttribute("id");
var call = document.getElementById("call");
var select = function(card)
{
var found = 0;
for (var card = 0, totalCards = 5; card < totalCards; card++)
{
if (document.getElementById("PlayerCard" + card + "selected"))
{found++}
}
if (found == 0)
{
call.setAttribute("onclick", "changeHand()");
call.childNodes[0].nodeValue = "Change";
}
if (found < 3)
{
id += "selected"
setAttributes(cardElem,
{
"id" : id,
"style": "position: relative; top: 1em;",
"onclick" : "cardSelection('" + id + "')"
});
}
else { return; }
}
var unselect = function (card)
{
cardElem.removeAttribute("style");
id = id.replace("selected","");
setAttributes(cardElem,
{
"id" : id,
"onclick" : "cardSelection('" + id + "')"
});
var cardNumber = 0;
var found = false;
while (cardNumber < 5 && !found)
{
if (document.getElementById("playerCard" + cardNumber + "selected"))
{found = true;}
cardNumber++;
}
if (!found)
{
call.setAttribute("onclick", "compareHands()");
call.childNodes[0].nodeValue = "Hold";
}
}
if (id.indexOf("selected") >= 0){unselect(card);}
else {select(card);}
}
...
}
How game() is called:
window.onload = function openingScreen()
{
var startGame = document.createElement("a");
startGame.setAttribute("onclick", "game()");
startGame.appendChild(document.createTextNode("Play"));
window.table = document.getElementById("table");
table.appendChild(startGame);
}
The problem you are experiencing is the result of confusion about Objects/Classes/Instances in javascript.
The critical point for you on this issue is the difference between new game() and game();
var foo = new game()
tells the JS engine to create a new object
point that object's Prototype (not prototype) at game's prototype
and then invoke the function game, but for the sake of the body of that function this will refer to the created object.
If the function doesn't return an object, assign our created object to foo (otherwise assign the function's return value to foo
Inside the body of your game function, you have this.cardSelection = function (....
If you simply invoke game as a function, so just game(), without the new keyword, this inside the body of the function will be the window object! So you'll add cardSelection to the window object.
Also importantly: game.cardSelection() is looking for a function named cardSelection as a property on the function game.
Here's an example of using that style that would work:
var foo = function () {
//do interesting stuff
}
foo.bar = function () {
//do interesting stuff related to foo
}
foo.bar();
What you seem to be expecting would need to be written this way:
var game = function () {
this.cardSelection = function () {
//perform card selection!
}
}
var aGame = new game();
aGame.cardSelection();
Or, if cardSelection does not need access to any private properties of the game, it could be written more efficiently as
var game = function () {
//setup the game
};
game.prototype.cardSelection = function () {
//perform card selection
};
var aGame = new game();
aGame.cardSelection();

Javascript onchange select binding issue

I cannot seems to select the correct keywords in google to find this answer so...
I am creating a series of functions within a class to handle select box changes. Here is a very simple example of how the javascript is laid out
function example(n) {
this.name = n;
}
example.prototype.setChange(i) {
var c = document.getElementById('test' + i );
for ( var x = 0; x < 10; x++
c.options[0] = new Option(x, x);
c.change = this.doChange;
}
example.prototype.doChange() {
alert(this.name);
}
Everything works find until I try to access 'this'. When doChange is called, this is the actual select html object instead of the class. Because this is an event, I cannot use .call(this) to enforce the proper bindings.
You loose your this by doing this.doChange. Use something like
function example(n) {
this.name = n;
var self = this;
this.doChange = function () {
alert(self.name);
}
}

In JQGrid add onclick event on img

I'm very new to JQuery, and I'm having some trouble
function Clients(guid)
{
var that = this;
this.guid = guid;
this.container = $("#Clients_" + that.guid);
this.LoadClients = function () {
var ids = that.container.find("#clients-tbl").getDataIDs();
for (var i = 0; i < ids.length; i++) {
var row = that.container.find("#clients-tbl").getRowData(ids[i]);
var imgView = "<img src='../../Content/Images/vcard.png' style='cursor:pointer;' alt='Open case' onclick=OnClickImage(" + ids[i] + "); />";
that.container.find("#clients-tbl").setRowData(ids[i], { CasesButtons: imgView });
}
}
this.CreateClientsGrid = function () {
var clientsGrid = that.container.find("#widget-clients-tbl").jqGrid({
.....
ondblClickRow:function(rowid)
{
---
}
loadComplete: function () {
that.LoadClients();
}
}
this.OnClickImage=function(idClient){
....
}
this.Init = function () {
that.CreateClientsGrid();
};
this.Init();
}
The problem is with onclick, because OnClickImage is not global function.
How can I use OnClickImage function?
You can bind to the click event in different ways. For example you can follow the way from the answer. By the way, it works much more quickly as getRowData and setRowData. Moreover you should save the result of that.container.find("#clients-tbl") operation in a variable outside of the loop and use use the variable inside the loop. JavaScript is dynamic language and every operation even ids.length will be done every time.
One more way would to use onCellSelect event without click event binding. See the answer which describe the approach and gives the corresponding demo.

Categories