...

View Full Version : jQuery "this" confusion



Apothem
07-06-2010, 06:37 PM
I'm confused on how "this" works visavi jQuery bindings.

function Foo()
{
this.stuff = 0;
this.callback = function()
{
$(this).attr('src', '...'+this.stuff);
};
$('#image_id').load( this.callback );
}

For $("#image_id")... will the callback method be called?
For the callback method, will using "$(this)" act as if it was "$("#image_id")"? If so, how does the method know the difference between the image's "this" and the object's "this"? (to be exact, will it be able to access "this.stuff")

Beagle
07-06-2010, 07:15 PM
This is resolved at runtime. It refers to the running context. If you attach your callback function to 25 objects and then call each of them from those objects, this will resolve 25 times to 25 difference referents.

Apothem
07-06-2010, 07:23 PM
So basically, this.stuff would in fact output its setted value (0) and $(this) would also work? What if I called another method within the callback method? Could I still use $(this) to refer back to the $("#image_id")?

Beagle
07-06-2010, 08:55 PM
You'll have to do some basic experimentation. I'm not going to be able to answer that confidently without writing some test code first. Make a hypothesis, code an experiment, check the results. If you can't figure out why something works or doesn't work, post the code and ask your questions then.

But try it first.

Apothem
07-06-2010, 10:13 PM
First:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>TEst</title>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4/jquery.min.js"></script>
<script>
(function ($) {

$.event.special.load = {
setup: function(data, namespaces, hollaback) {
var retVal = false;

if (this.tagName.toLowerCase() === 'img' && this.src !== "") {
// Image is already complete, fire the hollaback (fixes browser issues were cached
// images isn't triggering the load event)
if (this.complete || this.readyState === 4) {
$(this).bind('load', data || {}, hollaback).trigger('load');
retVal = true;
}

// Check if data URI images is supported, fire 'error' event if not
else if (this.readyState === 'uninitialized' && this.src.indexOf('data:') >= 0) {
$(this).trigger('error');
retVal = true;
}
}

return retVal;
}
}

}(jQuery));

$(document).ready(function()
{
function Foo()
{
this.stuff = 0;
this.callback = function()
{
//$(this).attr('title', '...'+this.stuff);
};
alert('foo');
$('#image_id').bind( 'load', function(){alert('bar');} );
}

var f = new Foo();
});
</script>
</head>
<body>
<img src="http://codingforums.com/image.php?u=60583&dateline=1260771715" id="image_id" />
</body>
</html>

For some reason in FF 3.6.x, only the first alert pops up and nothing else.

Beagle
07-06-2010, 10:21 PM
FF is throwing a "too much recursion" error inside the jQuery library. I'm reviewing the code locally now.

Beagle
07-06-2010, 10:30 PM
change the alert('bar') event from 'load' to 'click' to get this working for testing purposes. I'm not interested in figuring out why the 'load' event is causing problems.

Apothem
07-06-2010, 11:44 PM
Well, for my personal project I actually do need an image to complete loading to activate a function. But I will do it for testing purposes.

Regardless, I still need to know what is wrong.

Edit: So I tested it, and this.stuff doesn't work. How can I make it work?

Beagle
07-06-2010, 11:54 PM
Paste the code that doesn't work please.

Apothem
07-06-2010, 11:58 PM
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>TEst</title>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4/jquery.min.js"></script>
<script>
(function ($) {

$.event.special.load = {
setup: function(data, namespaces, hollaback) {
var retVal = false;

if (this.tagName.toLowerCase() === 'img' && this.src !== "") {
// Image is already complete, fire the hollaback (fixes browser issues were cached
// images isn't triggering the load event)
if (this.complete || this.readyState === 4) {
$(this).bind('load', data || {}, hollaback).trigger('load');
retVal = true;
}

// Check if data URI images is supported, fire 'error' event if not
else if (this.readyState === 'uninitialized' && this.src.indexOf('data:') >= 0) {
$(this).trigger('error');
retVal = true;
}
}

return retVal;
}
}

}(jQuery));

$(document).ready(function()
{
function Foo()
{
this.stuff = 0;
this.callback = function()
{
$(this).attr('title', '...'+this.stuff);
};
//alert('foo');
$('#image_id').click( this.callback );
}

var f = new Foo();
});
</script>
</head>
<body>
<img src="http://codingforums.com/image.php?u=60583&dateline=1260771715" id="image_id" />
</body>
</html>

$(this).attr('title', '...'+this.stuff); results in the title "...undefined". I expected it to become "...0"

Beagle
07-07-2010, 12:14 AM
Right. So, as I said, this is resolved at runtime, let's try to figure out what the means.


function Foo() {
this.stuff = 0;
this.callback = function() {
$(this).attr('title', '...'+this.stuff);
};
$('#image_id').click( this.callback );
}

var f = new Foo();

So, Foo() is executing. It defines stuff and callback in the current scope. Then it assigns callback to the click handler.

Now, you have an issue. When callback is called, the running scope is different, so this is not the same as the defining context, hence stuff does not exist in it. How do we fix that? Well, one way is to make sure we always have a reference to the defining scope:


function Foo() {
this.stuff = 0;
var parent = this;
this.callback = function() {
$(this).attr('title', '...'+parent.stuff);
};
$('#image_id').click( this.callback );
}

var f = new Foo();

or, by not using the this keyword when defining the variables:


function Foo() {
stuff = 0;
this.callback = function() {
$(this).attr('title', '...'+stuff);
};
$('#image_id').click( this.callback );
}

var f = new Foo();

Both of these techniques work. You can even drop the this regarding callback since callback isn't directly referred to by name outside of it's defining scope.

Hope that helps!

Apothem
07-07-2010, 12:19 AM
Wait I thought that if you don't do this.var that means it is a global variable. I don't want a global variable; I want an object element so that multiple constructions can be made.

Beagle
07-07-2010, 12:28 AM
Wait I thought that if you don't do this.var that means it is a global variable. I don't want a global variable; I want an object element so that multiple constructions can be made.

No. If you define a var inside a function, it's local.


function a_x() {
var x = 'x';
alert(x);
}

a_x(); // 'x'

alert(x); // undef



EZ Archive Ads Plugin for vBulletin Copyright 2006 Computer Help Forum