Sample code:
<!DOCTYPE html>
<html>
<head>
<title>test</title>
<script language="javascript" type="text/javascript">
function init() {
var nodeList = document.getElementsByTagName("a");
var nodeArr = [];
for( var i = 0; i < nodeList.length; i++) // Copy NodeList to Array
nodeArr.push(nodeList[i]);
for( var i = 0; i < nodeArr.length; i++) // Loop over array
if( nodeArr[i].className == "clickLink" )
nodeArr[i].onclick = clickLink2; // Attach event function
}
window.onload = init; //Attach event function
function clickLink2() {
console.log("this: " + this); //Prints window URL in href
console.dir( this ); //show attributes of anchor
console.log( this.name ); // Prints name attribute
}
function clickLink( elem ) {
console.log( "this: " + this ); //Prints [object Window]
console.dir( this ); // Shows attributes, etc. al of [object Window]
console.log( "name: " + elem.name );
}
</script>
</head>
<body>
<!-- inline -->
<a href="#" name="blah1" onclick="clickLink(this); return false;">Test 1</a>
<a href="#" name="blah2" onclick="clickLink(this); return false;">Test 2</a>
<a href="#" name="blah3" onclick="clickLink(this); return false;">Test 3</a>
<a href="#" name="blah4" onclick="clickLink(this); return false;">Test 4</a>
<hr/>
<!-- not inline -->
<a href="#" name="blah5" class="clickLink">Test 5</a>
<a href="#?t" name="blah6" class="clickLink">Test 6</a>
<a href="#" name="blah7" class="clickLink">Test 7</a>
<a href="#" name="blah8" class="clickLink">Test 8</a>
</body>
</html>
I did the testing in firefox, with firebug to view the console output.
Now what I was wondering:
clickLink
does this refer to the window object?clickLink2
print to console as the value of the href of the link?OK, so I took pieces from the answers here and found that this is a bit quirky in some browsers. Also that assigning a function to onclick
is different then attaching (but unfortunately not all IE versions support that either see addEventListener
vs attachEvent
). For some reason older IE's also make this inside the body of an event trigger function still refer to the window object instead of the caller. So I used event.srcElement
. Here's some new sample code:
<!DOCTYPE html>
<html>
<head>
<title>test</title>
<script language="javascript" type="text/javascript">
function init() {
var nodeList = document.getElementsByTagName("a");
var nodeArr = [];
for( var i = 0; i < nodeList.length; i++) // Copy NodeList to Array
nodeArr.push(nodeList[i]);
for( var i = 0; i < nodeArr.length; i++) // Loop over array
if( nodeArr[i].className == "clickLink" ) {
var a = nodeArr[i];
if (a.addEventListener) { //IE9, other browsers
a.addEventListener('click', clickLink2); // Attach event function
} else if (a.attachEvent) { //IE6,7,8, etc.
a.attachEvent('onclick', clickLink2 ); // Legacy IE Attach event function
}
a.onclick = function() { return false }; // override default onclick behavior for these anchors so URL is not followed
}
}
window.onload = init; //Attach event function
function clickLink2() {
if( typeof(event) != 'undefined' ) {
elem = event.srcElement; //IE < 8 keeps this as window object
} else {
elem = this;
}
alert( elem.name );
}
function clickLink( elem ) {
alert( elem.name );
}
</script>
</head>
<body>
<!-- inline -->
<a href="#" name="blah1" onclick="clickLink(this); return false;">Test 1</a>
<a href="#" name="blah2" onclick="clickLink(this); return false;">Test 2</a>
<a href="#" name="blah3" onclick="clickLink(this); return false;">Test 3</a>
<a href="#" name="blah4" onclick="clickLink(this); return false;">Test 4</a>
<hr/>
<!-- not inline -->
<a href="#" name="blah5" class="clickLink">Test 5</a>
<a href="#?t" name="blah6" class="clickLink">Test 6</a>
<a href="#" name="blah7" class="clickLink">Test 7</a>
<a href="#" name="blah8" class="clickLink">Test 8</a>
</body>
</html>
1) Why in clickLink does this refer to the window object?
Put simply: clickLink isn't the event handler, by adding the attribute onclick
to the elements, you're using the native onclick
method as a handler that calls a function. this function is declared in the main scope, thus this points to the window object.
To be clear: the behaviour is similar to this:
<p onclick='function(){window.clickLink(this);}'>
Making the actual event handler an anonymous function, not clickLink. That's why this will point to window: the annonymous function, too, was declared in the global scope, thus the caller of clickLink is window.
2) Why does this in clickLink2 print to console as the value of the href of the link?
Think about this phrase 'Javascript allows you to manipulate the DOM, the DOM-tree, all events and behaviours.' In your init function, the elements onclick behaviour is manipulated. the default method of onclick
is being overruled, and replaced by the clickLink2
method here nodeArr[i].onclick = clickLink2;
.
Look at it like this: all your elements have a prototype onclick method, that accepts a parameter. In the first case, this parameter is a string, that evaluates to a call to the clickLink
function. The second case, however, the instances that have a particular class have their own onclick method, overruling the prototype's onclick.
I do hope this clears things up a bit for you. It's easy once you grasp the basic philosophy behind events, handlers, methods, prototypes etc... and overcome the quirks of JS.
3) Is there a better way to pass this to an unobtrusive attachment like this? how can you be sure what this refers to?
Well, sort of answerd that above, didn't I? but anyhow: if you define a method of an element or object yourself, in general, this will point to the owner object/element. If you rely on html and default behaviours, JS will mostly mind it's own business and this will point to window.
I don't know if this is an option, but maybe you could try something like this:
<a onclick='clickLink'>
this should -in theory- do the same thing as your init function
Edit: A working alternative to the above:
<p onclick='clickLink.call(this)'>
Call defines the p
element as the caller of clickLink, making this point to the element that called the function.
When inline event handlers are called, they are ran in the global scope. That's why this
is window
.
When event handlers are actually attached to an element, they are called in the context of that element, just like when running a method that's part of an object. That makes this
the element.
For example:
function test(){
console.log(this);
}
test(); // window
var obj = {
test: test
}
obj.test(); // obj
To be sure of what this
is, you should attach the event handlers using JS (with element.addEventListener
or element.onClick
), and not using inline handlers.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With