How do i fix this JS scope in GM_xmlhttpRequest - javascript

When i run this code alert 2 shows 6 different href links. alert 3 shows the last href 6 times. How do i make it use the same object (linkdom aka thelink) as alert 2.
NOTE: This is in a greasemonkey script
{
var linkdom = thelink;
alert('2' + linkdom.getAttribute("href"));
GM_xmlhttpRequest({
method: 'GET',
url: href,
onload: function(resp){
//...
alert('3' + linkdom.getAttribute("href"));
}
});
//...
}

If this were your own function, I'd say to pass it in as a parameter. Or if JavaScript had default parameters, I'd say pass it in as a default parameter. The way it is now, though... try this.
{
var linkdom = thelink;
alert('2' + linkdom.getAttribute("href"));
GM_xmlhttpRequest({
method: 'GET',
url: href,
onload: (function() {
var localvar = linkdom;
return function(resp){
//...
alert('3' + localvar.getAttribute("href"));
}})()
});
//...
}
This creates an outer function and sets a local variable to the current value of linkdom. It then creates your function and returns it. I then immediately apply the outer function to get your function back. The outer functions won't share the same local variable, so the code should work.

Related

Javascript Not Passing Value to Function

Hi I am trying to pass 3 values into some Javascript. This works for the first function. All values are found to be correct here......
function handleClick(cb,colum,id) {
if (cb.checked == true){
var checked = 1;
} else {
var checked = 0;
}
sendHddToPHP2(checked,column,id);
}
But once the second function is called I get nothing....
function sendHddToPHP2(editableObj,column,id) {
window.alert(editableObj);
$.ajax({
url: "update/hdd.php",
type: "POST",
data:'column='+column+'&editval='+editableObj+'&id='+id,
success: function(data){
$(editableObj).css("background","#FDFDFD");
}
});
}
I added the alert to check if the variable was present and it didn't run. I can't see any typos and my knowledge isn't that great on Javascript. Is there something I have missed?
You have a typo: "colum" in the argument list.
Also, seems like you want to access the check box later in the Ajax callback, that will fail since you're trying to reference it via the "editableObj" value of the checkbox rather than the element.
As I understand you want your function to be like this:
function handleClick(cb,colum,id) {
var checked = (cb.checked)?1:0;//same as(cb.checked==true)1:0
sendHddToPHP2(checked,column,id);
}
but are you sure you want to return a 1 or 0? because in the second function you will be using $(1).css("background","#FDFDFD");
$(0).css("background","#FDFDFD");

Javascript Object Prototype falling out of scope

I am currently writing a small Javascript Object which will add click listeners onto certain elements which then trigger an AJAX call to a PHP function. This is all working fine however, I want to call a function when the AJAX responds. I have made this happen by passing a function to the AJAX call which will be triggered when the response is given.
The problem I am having is that I am losing the scope of the object when passing through the protoytype as a call back (in order to stop the aynschronous problems that can occur with AJAX calls). The 'this' object (or self) is set to the window and not the instance of the object I have created. Here is my code:
//Rating Submit
var Rater = function(element, videoId, ratingStars, userId) {
this.userId = userId,
this.element = element;
this.videoId = videoId;
this.ratingStars = ratingStars;
var self = this;
jQuery(this.ratingStars).click(function(e) {
e.preventDefault();
self.ratingClick(this, self.changeStar);
});
}
Rater.prototype.ratingClick = function(item, changeStar) {
jQuery.ajax({
type : 'post',
dataType : 'json',
url : 'api/rate-video',
data : "userId=" + this.userId + "&videoId=" + this.videoId + "&rating=" + jQuery(item).attr("data-score"),
success : function(data) {
changeStar(data, item);
}
});
}
Rater.prototype.changeStar = function(response, item) {
var maxCount = jQuery(item).attr("data-score");
//console.log(self);
jQuery(self.ratingStars).each(function(key, value) {
alert(key);
});
}
As you can see, I am passing the 'self.changestar' prototype function to the AJAX call for this to be called when a response is given. When I try and access any of the variable I set in the constructor for that particular instance though, it says it is the Window object and not an instance of the class. Is it possible to pass through a prototype function as a call back from within the instance? I hope I have explained myself ok....
Thanks
The problem is that when you do this:
self.ratingClick(this, self.changeStar);
you have exactly the same problem you had in Rating with the jQuery click callback, which you solved with your self variable: Only the function reference, changeStar, gets passed, nothing about what value to use as this when calling it.
One solution is to use Function#bind, which you call on a function to get back another function that, when called, will call the original with a specific this value (and optional arguments):
self.ratingClick(this, self.changeStar.bind(self));
Alternately, you could pass the value to use as this separately:
self.ratingClick(this, self.changeStar, self);
...and then use Function#call in the success handler:
Rater.prototype.ratingClick = function(item, changeStar, thisArg) { // <== Change here
jQuery.ajax({
type : 'post',
dataType : 'json',
url : 'api/rate-video',
data : "userId=" + this.userId + "&videoId=" + this.videoId + "&rating=" + jQuery(item).attr("data-score"),
success : function(data) {
changeStar.call(thisArg, data, item); // <=== And here
}
});
}

How to create global variable in query and usable with javascript

$(document).ready(function ()
{
$.ajax(
{
url: "Bibliotheek.xml",
dataType: "xml",
success: function (data)
{
var song = $(data).find('key').filter(function ()
{
return $(this).text().indexOf('Name') != -1;
}).each(function()
{
window['globalVar']= $(this).next('string').text();
console.log(globalVar);
});
}
});
});
I want to use globalVar outside that each loop. But once i put de console.log outside the function. It tells my globalVar is undefined.Is it also possible to use that variable later on in javascript code?
This probably happens, because you loop over an empty list (i.e. it never enters the .each callback). This thing is wrong: .find('key'). It searches for a key tag (which is not HTML, unless you actually are not dealing with HTML?). Perhaps you were looking for .find('.key')?
EDIT: It seems that you want to put console.log outside of ajax call. If you do, then you're out of luck, since you are trying to log a variable that does not exist yet. That's because a in ajax stands for asynchronous, i.e. the piece of code will run later.
EDIT 2: Welcome to asynchronous programming! It seems that you are trying to force ajax to be synchronous, which is wrong and pure eveil. Don't do it. You're code should be similar to this:
var my_fn = function(clb) { // <-- this is callback to be called later
var els = [];
$.ajax({
url: "Bibliotheek.xml",
dataType: "xml",
success: function (data) {
var song = $(data).find('key').filter(function () {
return $(this).text().indexOf('Name') != -1;
}).each(function() {
var el = $(this).next('string').text();
els.push(el);
});
clb(els); // <-- call it now
}
});
};
$(document).ready(function() {
my_fn(function(els) {
console.log(els);
// do coding here
});
});
Define the globalVar outside of the functions...
var globalVar;
var song = {...
console.log(globalVar);//will work here
};
console.log(globalVar);//and, will work here

Firing Events in JavaScript

I have a JavaScript class named 'Item'. 'Item' is defined as shown here:
function Item() { this.create(); }
Item.prototype = {
create: function () {
this.data = {
id: getNewID(),
}
},
save: function() {
$.ajax({
url: getBackendUrl(),
type: "POST",
data: JSON.stringify(this.data),
contentType: "application/json",
success: save_Succeeded,
error: save_Failed
});
},
function save_Succeeded(result) {
// Signal an event here that other JavaScript code can subscribe to.
}
function save_Failed(e1, e2, e3) {
// Signal an event here that other JavaScript code can subscript to.
}
}
Please note, I'm coming from a C# background. So I'm not even sure if what I want to accomplish is possible. But essentially, I want to create an object, subscribe to some event handlers, and attempt to save my object. For instance, I envision doing something like the following throughout my code.
var i = new Item();
i.item_save_succeeded = function() {
// Do stuff when the item has successfully saved
};
i.item_save_failed = function() {
// Do stuff when the item has failed to save
};
i.save(); // start the save process
Is this event-based approach even possible in JavaScript? If so, how? What am I missing? I keep getting a variety of errors that are vague. Because of that, I'm not sure if I'm getting closer or farther away.
If you are using jQuery, you can add an event handler to a custom event type.
The following snippet is taken from the jQuery docs
$('#foo').bind('custom', function(event, param1, param2) {
alert(param1 + "\n" + param2);
});
$('#foo').trigger('custom', ['Custom', 'Event']);
But since jQuery 1.7 deprecates bind, you should use on now. See the jQuery docs for on.
Not 100% sure and I look forward to seeing the answer from a JS pro, but here is what I would do.
Expose some properties within you Item object - namely the functions you wish to be subscribed to.
Upon instancing an item you could then provide callback functions for the events that you wish to be notified of. In your code you could then do something like this:
save: function() {
var self = this;
$.ajax({
url: getBackendUrl(),
type: "POST",
data: JSON.stringify(this.data),
contentType: "application/json",
success: function() { if(typeof(self.success) == "function") self.success(); }
error: function() { if(typeof(self.fail) == "function") self.fail(); }
});
},
In effect, pass the callback functions to the object and let it call them directly when needed. I'm sure someone will now suggest a better way of doing it. :-)

JQuery/JavaScript : refactoring nested functions

I have this interesting jQuery function. It basically adds a click handler to link, and when that is clicked, it will load a form to allow the user to edit the content. and the form is submitted by AJAX, and will display a success message when it's done.
The outline is below; needless to say, this is messy. I could have each of the callback as a class method. What other ways are there to refactor nested functions? I am also interested to see if there are ways that variables declare in a parent function still retain its value down to the nested function after refactoring
$('a.edit').click( function() {
// ..snipped..
// get form
$.ajax({
success: function() {
// add form
// submit handler for form
$(new_form).submit(function() {
// submit via ajax
$.ajax({
success: function(data) {
// display message
}
})
})
}}
)
}
I guess the interesting part of your question is how to refactor without loosing access to the closure variables. Here is my suggestion:
Version one: nested, with closures and variable access:
var a;
$('a.edit').click( function() {
var b;
$.ajax({
success: function() {
var c;
$(new_form).submit(function() {
var d;
$.ajax({
success: function(data) {
// a,b,c,d are all visible here.
// note that a references the same object for all calls of the success function, whereas d is a different variable for each call of submit.
// this behaviour is called closure: the 'enclosed' function has access to the outer var
}
})
})
}
})
})
Version two: less nested, but without closures and without variable access:
var a;
$('a.edit').click(onEdit);
var onEdit = function() {
var b;
$.ajax({success: onEditSuccess});
};
var onEditSuccess = function() {
var c;
$(new_form).submit(onSubmit);
};
var onSubmit = function() {
var d;
$.ajax({success: onSubmitSuccess});
}
var onSubmitSuccess = function(data) {
// a is visible (global var)
// b,c,d NOT visible here.
};
Version three: less nested and with unnamed functions and parameters to get access to the closure variables:
var a;
$('a.edit').click(function(){onEdit(a)});
var onEdit = function(a) {
var b;
$.ajax({success: function(){onEditSuccess(a,b)}});
};
var onEditSuccess = function(a,b) {
var c;
$(new_form).submit(function(){onSubmit(a,b,c)});
};
var onSubmit = function(a,b,c) {
var d;
$.ajax({success: function(data){onSubmitSuccess(data,a,b,c,d)}});
}
var onSubmitSuccess = function(data,a,b,c,d) {
// a,b,c,d are visible again
// nice side effect: people not familiar with closures see that the vars are available as they are function parameters
};
You can easily refactor this to make it much more readable. The key concept to grasp is that you can refer to named functions in callbacks as well as anonymous ones. So, for instance:
function clickHandler() {
alert("Link clicked");
}
$('a').click(clickHandler);
My preference is always to give the functions names according to what they do (e.g. loadImage, rather than the event that you intend to trigger them (e.g. clickLink. This makes your code clearer and makes later changes much easier. In this case, I would structure my code like this:
$(document).ready(function(){
$('a.edit').click(loadFormStart);
function loadFormStart() { // get form
$.ajax({
success: loadFormEnd
});
}
function loadFormEnd(data) { // add form & handler
$('new_form').submit(handleFormStart);
}
function handleFormStart() { // submit form
$.ajax({
success: handleFormEnd
});
}
function handleFormEnd(data) { // receive form data
//display message
}
});
I'd also advise you to read Code Organization on jqfundamentals which gives a similar approach to this using an object literal.
Interesting question. Personally I don't mind the above. Commenting is key, so you could consider qualifying the closing braces with some:
} //success: function(data)
}) //$.ajax({
}) //$(new_form).submit(
...etc
I would also look at aligning the brackets correctly (at first clance, your }} is a little mystifying).
If it comes to 'generic' nesting strategies, the only other suggestion I have is to move code out other functions. The of course means that you have the function decalred in memory, but may make it more readable.
You could also consider a specific strategy that relates to this code. For example, rather than manually binding a submit to new_form can you use the live function in some way to ensure that it is done automatically?
On a completely unrelated note, you should probably add some ; at the end of each of the bracketed lines!

Categories

Resources