I've come across a head scratching issue with my JavaScript application.
If I write an element like this:
<li onClick="alert(this.tagName)"></li>
I get "LI."
However if I do this:
<li onClick="foo()"></li>
Where "foo()" is:
function foo(){ alert(this.tagName); }
I get "undefined."
I am away how "this" is supposed to work in regards to attached functions. But, I am baffled because "this" is not picking up the element, but apparently defaulting to "window." I can't figure out why this is happening.
Does anyone have an explanation?
That's because you aren't passing a reference to this in the JavaScript function call. this in the JavaScript function doesn't refer to the same object as in the onClick example. Try this instead:
<li onClick="foo(this)"></li>
function foo(item){ alert(item.tagName); }
In an inline listener:
> <li onClick="alert(this.tagName)"></li>
The onclick attribute value is effectively wrapped in a function and called with the element set to this, e.g.
function anon() {
/* attribute value */
}
anon.call(element);
When you put a function in the body, you are essentially getting:
function anon() {
foo();
}
Here, this
within anon
will be the element, but since foo
is called without setting this
, it will be undefined. In non-strict mode, this
will default to the global object (window
in a browser). In strict mode, this
inside foo
will be undefined.
One solution is to pass an element reference to the function:
<li onclick="foo(this)" ... >
then in the function:
function foo(callingElement) {
...
}
or even:
<li onclick="foo.call(this)" ... >
function foo() {
var callingElement = this;
}
As other answers already mention, the value of this
will depend on how the function that contains it is called. But since your example is about event handlers, I'd like to highlight what cjc343 said on the comments:
You may find it to be more sensible if you remove the inline event handlers.
That's pretty simple, actually. Considering this HTML:
<ul id="list">
<li id="item1">item 1</li>
<li id="item2">item 2</li>
<li id="item3">item 3</li>
</ul>
The following JavaScript will account for both removing inline handlers, and using delegation:
var list = document.getElementById('list');
list.addEventListener('click', function(evt){
console.log("this is the element the event is bound to: " + this.id);
console.log("the event target is the clicked element: " + evt.target.id);
});
That will work on all browsers compliant to the W3C event model, including IE9. For older IE, you have to use attachEvent
instead of addEventListener
, and prepend the event names with "on"
. More details here.
Another option, so you don't have to pass this as a param, is to use call or apply. It's a built in mechanism to set the value of this within a function. Though I would point out, adding your event handlers directly to your html is a bit antiquated. You may want to check out a JS framework for event delegation (jQuery, Prototype, Dojo, YUI, etc.).
fiddle: http://jsfiddle.net/bboone/Q2CkV/2/
HTML
<div onClick='alert(this.tagName);'>test</div>
<div onClick='foo.call(this);'>test2</div>
JS
function foo(){ alert(this.tagName); }
If you are new to JavaScript do not use the this
or new
keyword, because either you will get it wrong or your code will be unnecessarily inefficient and more complex. What you are trying to accomplish, though is the following:
<li onclick="foo(this)">some text</li>
In that example the click event of that list item fires a function named foo. The this
keyword is passed in as a variable. The this
keyword merely refers to the entity that called the function in question and if it cannot find that entity it will refer to the window
object of the browser. In this case the entity that called the function is the list item node from the DOM.
I still suggest never using this
keyword in order to avoid this kind of confusion moving forward.
EDIT: Also, do not use a tagName property as this is not standard. Instead use the nodeName
property.
来源:https://stackoverflow.com/questions/12485759/onclick-function-this-returns-window-object