How may I call a method of the same object inside .append()? - javascript

generarGrilla() creates an output that has some html buttons.
Each button calls a method that exist in the same object guardarReserva()
When I load the page guardarReserva() is called without clicking any button, because this line console.log("whatever "+this.i); gets printed into the console.
If I do click one of the buttons that have the listeners, nothing happens.
var reservasAPP = {
i:0,
nombre: function () {
return $('#h09').val();
},
guardarReserva:function(){
var reservaConfirmada = $('#horario'+this.i).html('--> '+this.nombre());
console.log("whatever "+this.i);
return false;
},
generarGrilla: function(){
//it loads from html using jQuery(document).ready();
for(this.i=1; this.i<13; this.i++){
var impresionGrilla = $('#grilla').append("<button class=\"btn btn-primary btn-xs\" onclick=\"return "+this.guardarReserva()+"\">Reservar</button>");
};
},
}

You're actually running guardarReserva() within your .append() function. That is why you see the immediate console.log("whatever "+this.i); when you first open the browser.
I think you want the onclick of that button to call that function. For that (assuming reservasAPP is in the window namespace) you can do something like this:
var reservasAPP = {
i:0,
nombre: function () {
return $('#h09').val();
},
guardarReserva:function(){
var reservaConfirmada = $('#horario'+this.i).html('--> '+this.nombre());
console.log("whatever "+this.i);
return false;
},
generarGrilla: function(){
//it loads from html using jQuery(document).ready();
for(this.i=1; this.i<13; this.i++){
// ---Change is here ------------------------------------------------------------------------|
// V
var impresionGrilla = $('#grilla').append("<button class=\"btn btn-primary btn-xs\" onclick=\"return reservasAPP.guardarReserva();\">Reservar</button>");
};
},
}
Why I couldn't use "this" and I need to use "reservasAPP"
So, let's look at some of your original code (I'll slightly modify it so it is a bit shorter) and see why it wasn't working.
The line where the good stuff happens is within generarGrilla, within the for loop where you call the .append() function.
var i_g = $('#grilla').append('<button onclick="return ' + this.guardarReserva() + '">Reservar</button>');
Now you are correct in the idea that when that line of code executes, the this keyword is pointing at the reservasAPP object. However, what you are doing is not setting the onclick event for the button to run this.guardarReserva(), you are immediately running the this.guardarReserva() function.
Let's look at a small, semi-related example:
var n = function() {
return 'John';
};
var str = 'Hello, ' + n();
Now, what is the value of our variable str? Well, we are doing two things:
First, we have a string literal Hello, .
We are then concatenating it using the + operator with what the function n() returns.
Notice how we are going to use what n() returns rather than the literal 'n()' (string or function). This is because we are actually calling the n() function.
So, at the end of the day:
// str = 'Hello, John';
So, let's go back and look at your original code. What is actually going on there? Well, you are selecting all elements with id set to grilla (I'm assuming there is only one of those) then calling the jQuery append() function. .append() can accept a string as its argument, and it'll take that string, and insert it into the DOM as HTML.
Your string is concatenating three different things.
'<button onclick="return '
The return value of this.guardarReserva()
'">Reservar</button>'
Your guardarReserva function at the end returns false, which when concatenated with a string, uses its .toString() method, which in this case returns the actual word 'false'.
So, if you'd look at your HTML from before, you'd see your HTML code looked like:
<div id="grilla">
<button onclick="return false">Reservar</button>
</div>
Which is not at all what you wanted.
Instead, to fix that issue, I had you pass in one long string (which includes the function name you do want to call). So, your HTML ended up looking like:
<div id="grilla">
<button onclick="return reservasAPP.guardarReserva()">Reservar</button>
</div>
OK, so that's why the function was running right away, and why your button wasn't working. So we need to pass in the function as a string for the HTML button to know to run that function when it is clicked.
So, why can't you pass in the string:
'<button onclick="return this.guardarReserva()">Reservar</button>'
It has to do with how the browser evaluates the this keyword within that button.
In fact, let's do an experiment
<button onclick="console.log(this);">CLICK ME</button>
What happens when I click the button? You can do this experiment yourself. You'll see that it actually logs the literal button. Because within the buttons inline onclick, this refers to the button itself!
You can doubly verify this with this code:
<button id="button-1" onclick="console.log(this.id);">CLICK ME</button>
And see that it logs the string "button-1" (aka, the button's id).
So, if this refers to the button, we can't leave that context to get at our reservasAPP object! By referencing the reservasAPP object directly (assuming it was declared in your main <script></script> tag, thus placing it in the accessible window object), we can access that object, and thus its inner properties.
SIDE NOTE:
I would use a different method altogether for attaching our onclick handler.
I'd use jQuery's .on() function. Here is an example of how you could define generarGrilla:
generarGrilla: function() {
for (this.i = 1; this.i < 13; this.i++) {
// Save our `this` object for later
var self = this;
var button = $('<button class="btn btn-primary btn-xs">Reservar</button>');
// Set the `onclick` handler for our button (that isn't in the DOM yet)
button.on('click', function(event) {
// Use our saved `self` variable, which points to `reservasAPP`
return self.guardarReserva();
});
// `onclick` is set, insert our button into the DOM
var impresionGrilla = $('#grilla').append(button);
};
}
Now, when you run reservasAPP.generarGrilla();, it'll insert your buttons, and they'll all have onclick event handlers.
You can read this post on why'd some people think it's better to use attached event handlers vs inline JS on HTML attributes.

Have you tried reservasAPP.guardarReserva() instead of this.guardarReserva() ?

Related

How to click a button to add a value to an array and display it

I am currently stuck on trying to use an onclick button to push a value into an array.
let display = document.getElementById('screen');
let results = [];
display.innerHTML = results[0];
$(document).ready(() => {
$('.one').click(function() {
results.push(1);
});
})
I am trying to push 1 into the array when the button is pushed, then display it. However, my current code does not push the function.
It does work, but the line that shows the results must be inside of the click callback. As it is now, the display gets updated just once, before the click happens.
Also, JQuery deprecated "shortcut" event methods (.click()) a while back and recommends the use of .on().
Lastly, innerHTML has performance and security implications, so don't use innerHTML when the string in question doesn't contain any HTML. Instead, use .textContent. But, because you are already using JQuery, you can use .text().
// If you are going to use JQuery, then use it.
// Here, we can get the element with an id of "screen"
// into a JQuery wrapped set object very easily.
// Naming the variable that will hold that JQuery object
// with a $ is a standard convention to remind you that
// the variable holds a JQuery object and not a standard
// DOM object.
let $display = $('#screen');
let results = [];
// And, with JQuery, if you just pass a function directly
// to JQuery, that function is automatically understood to
// be a document.ready callback
$(() => {
$('.one').on("click" ,function() {
results.push(1);
$display.text(results[0]); // This must be in the callback to show the most up to date information
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input type="button" class="one" value="push">
<div id="screen"></div>

Need a javascript onclick decorator

I have legacy code that includes links with hard-coded onclick handlers (bad, I know).
link
I need to decorate that onclick function. In other words, I need to to replace the hard-coded onclick function with another function of my own that will first call the original function and then allow me to call my own code.
This is what I've tried, to no avail:
var oldFunc = $('a.linkClass').attr('onclick');
function newFunc(oldFunc) {
return function(oldFunc) {
oldFunc();
// my add'l code
}
}
$('a.linkClass').attr('onclick', '').unbind('click');
$('a.linkClass').click(newFunc(oldFunc));
First issue is need proper selector to find the onclick elements. $('[onclick]') will find all elements in page with that attribute. Also you need to remove the onclick attribute, trying to unbind with jQUery won't work
Assuming each onclick only has one function you could try:
$('[onclick]').each(function(){
var $el=$(this)
var funcName=$el.attr('onclick').replace('()','');
$el.click(window[funcName]).removeAttr('onclick');
});
Just realizing this won't work if any params in function
You are trying to wrap multiple events, but to do that you need to loop over the elements and wrap each one in turn (javascript can't store multiple functions in a single variable like that!) Also you get a string from onclick, so you'll need to eval it (which is generally a BadThing (tm). But the real bad thing here is that you're using onclick in the first place.
var elementsToFix = $('a.linkClass');
for( var i = 0; i < elementsToFix.length; ++ i ) {
var obj = elementsToFix.eq( i );
var oldFnc = obj.attr( 'onclick' );
obj.removeAttr( 'onclick' ).on( 'click', (function( curOldFnc ) {
return function( ) {
// pre-wrapper here
eval( curOldFnc );
// post-wrapper here
}
}( oldFnc )) );
}
This uses the super-evil eval. That's bad, and the only reason I'm using it is because you get a string from attr('onclick') instead of a function. The correct solution is to never use onclick in the first place.
jsfiddle example: http://jsfiddle.net/Jdn6V/1
As the function called from the onclick handler is in the global scope, the easy solution would be to just remove the onclick handler, store the string, and eval it later.
Of course eval is bad, and there are ways to parse the string, remove the parenthesis and keep any arguments, and call it as window[oldFunc] etc. but as a quick example, here it is with eval (which is evil)
$('a.linkClass').each(function() {
var old = $(this).attr('onclick');
$(this).attr('onclick', '').on('click', function(e) {
e.preventDefault();
eval.apply(window, [old]);
alert('new');
});
});
FIDDLE
without eval
$('a.linkClass').each(function() {
var old = $(this).attr('onclick'),
func = old.split('(')[0],
param = old.split('(')[1].replace(')','').split(',');
$(this).attr('onclick', '').on('click', function(e) {
e.preventDefault();
window[func].apply(window, param);
alert('new')
});
});
FIDDLE
.attr() always returns a string, so you'd have to eval it to execute the function.
You can get the original function this way, and not need to eval it later:
var oldFunc = $('a.linkClass')[0].onclick;
([0] picks the "pure" DOM element that has the onclick function.)
Now you can bind the function to the element:
$('a.linkClass').click( function(e) {
// new code here
// ...
oldFunc(e);
});
(e given as the parameter, just in case the old function uses it.)
The additional benefit of this is that it works regardless of what's in the old onclick attribute because it's wrapped in a function, so e.g. onclick="foo(); bar(); return false;" works correctly.

Array of HTML buttons with on click event to return an attribute

I am trying to create an array of buttons with an onclick event alerting an attribute.
the buttons are created with no problem and every time I create a problem I set up an alert of the attribute. this works perfect.
Every time you press a button, the right function is called, and it works great, but for some reason I get the second alert to say "undefined"
Just look at the code, it explains it self better then I can.
var i = 0
var insertframe = function () {
savebutton[i].index = i
//this works!
alert(savebutton[i].index)
savebutton[i].click(function (event) {
//this returns "undefined"
alert(savebutton[i].index)
})
i++
}
Solution:
instead of naming the object, I had to use "this" instead so, instead of:
savebutton[i].index
use:
this.index

javascript change name of called function

Hi all im very new to javascript so please be gentle.
im mixing php with my calls and I have a slight issue.
I need to alter the function name that is called in an onclick event.
<div class=\"span4\" id=\"pass\">
<button class=\"btn btn-block btn-large btn-warning\" id=\"bypass\" disabled onclick=\"pass(); return false;\">
-
</button>
</div>
above is the div with the std call.
before this point some variables are set from another function call and I need to change the above call to "pinpass2() or pinpass3 etc.
function pincheck(id,type,lastid,pin){
document.getElementById('fade').style.display=\"block\";
document.getElementById('calc').style.display=\"block\";
var staffid = id;
document.getElementById('bypass').onclick = function (){\"pinpass\"+staffid();
return false;
};
}
the above is the function that should do it but i can't seem to get it working.
Any help appreciated.
p.s if i include the following into the pincheck function the desired staffid is diaplayed
alert(\"staff id\"+staffid);
document.getElementById('bypass').onclick = pinpass2;
That should work just fine. pinpass2 is already a function, you can assign it to onclick like any other object (yes, functions are objects in Javascript). So just change the onclick when you need it.
If you can't detect changes to the result of staffid(), then you should use a switch instead.
document.getElementById('bypass').onclick = function() {
switch(staffid()) {
case 1: pinpass(); break;
case 2: pinpass2(); break;
default: pinpass3(); break;
}
};
Though most of the time you don't have to do this. Also, I'm not sure if staffid is supposed to be a function or a variable, but it doesn't change anything.
By the way, this way of attaching handlers is quite old. There's a more powerful one:
document.getElementById('bypass').addEventListener('click', pinpass2, false);
With that you can attach more than one function. To remove one:
document.getElementById('bypass').removeEventListener('click', pinpass2, false);
You can change the onclick attribute the same way you'd change any attribute ?
var elem = document.getElementById('bypass');
elem.setAttribute('onclick', 'pinpass' + staffid + '(); return false;');
FIDDLE
In javascript functions are first class so you can literally just assign pincheck to another variable like this.
var pinpass2 = pincheck;
Now you can still call it like this
pinpass(1,2,3,4);
I'm not 100% from your question, but it looks like you are trying to call a different function based on the staffid variable. I.E. if it is 2 you want to call pinpass2().
If this is a global function you can call window[ 'pinpass' + staffid ]() and it will call the function you want (if it exists).
EXAMPLE: if staffid = 2 then window[ 'pinpass' + staffid ]() is eqivalent to window[ 'pinpass2' ]() which is the same as calling pinpass2().
This works because all global vars (including functions) are properties of the window object, and properties can be accessed using a dynamically generated name and square bracket notation.

Moving inline code into function, with object name generation

I am customizing Denis Gritcyuk's Popup date picker.
This pop-up script uses inline Javascript in a href link, to set the selected date into the input field, in the parent window, that is was called for. An example URL looks like:
<a href="javascript:window.opener.document.formname.field.value='03-10-2011';
window.close();">3</a>
The input field name, (e.g. document.formname.field), is passed to the script as a string parameter.
I would like to add things done when that link is clicked (e.g. change background color of field, set flag, etc.). So while this DOES work, it's getting ugly fast.
<a href="javascript:window.opener.document.formname.field.value='03-10-2011';
window.opener.document.formname.field.style.backgroundColor='#FFB6C1';
window.close();">3</a>
How would I move these inline commands into a JS function? This would give me much cleaner URLs and code. The URL would now look something like
3
with a function like (this example obviously does NOT work):
function updateField (str_target, str_datetime) {
var fieldName = "window.opener" + str_target;
[fieldName].value = str_datetime;
[fieldName].style.backgroundColor = '#FFB6C1';
// Set flag, etc.
window.close();
}
So any suggestions on how this can be done, please?
I'd prefer to hide the dom path tracing back from the current window back to the opener. It's appropriate to bake that into the function since the function will always be used in the context of that child popup. Then your function call is cleaner and more readable. Obviously, replace "myField" with the ID of the field you're intending to update.
3
function updateField ( str_date, str_fieldname ) {
var fieldToUpdate = document.getElementById( str_fieldname );
fieldToUpdate.value = str_date;
fieldToUpdate.style.backgroundColor = '#FFB6C1';
// Set flag, etc.
window.close();
}
You're acessing the property incorrectly. Try:
function updateField (str_target, str_datetime) {
var fieldName = window.opener;
str_target = str_target.split('.');
for (var i = 0; i < str_target.length; i++)
fieldName = fieldName[str_target[i]];
fieldName.value = str_datetime;
fieldName.style.backgroundColor = '#FFB6C1';
// Set flag, etc.
window.close();
}
The bracket notation ([]) is only used for properties of objects, not objects themselves. If you found my post helpful, please vote for it.
You can build a string and evaluate it as code using the eval function, but I would recommend against it.
There are a couple of things wrong with your code:
You cannot use the [] operator in a global context, you have to suffix it on an object, so you can say window["opener"] and this will be equivalent to window.opener, but there is no such thing as simply ["window"]
When navigating nested properties, as in window.opener.document you cannot navigate multiple levels using the [] operator. I.e. window["opener.document"] is not allowed. You must use window["opener"]["document"] instead.

Categories

Resources