Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

javascript questions about keyword this

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:

  1. Why in clickLink does this refer to the window object?
  2. Why does this in clickLink2 print to console as the value of the href of the link?
  3. Is there a better way to pass this to an unobtrusive attachment like this? how can you be sure what this refers to?

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>
like image 210
user17753 Avatar asked Feb 21 '23 03:02

user17753


2 Answers

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.

like image 108
Elias Van Ootegem Avatar answered Mar 06 '23 00:03

Elias Van Ootegem


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.

like image 41
Rocket Hazmat Avatar answered Mar 05 '23 23:03

Rocket Hazmat