OnClick Function "this" Возвращает объект Window

Я столкнулся с проблемой царапин в моем приложении JavaScript.

Если я напишу такой элемент:

<li onClick="alert(this.tagName)"></li>

Я получаю "LI".

Однако, если я это сделаю:

<li onClick="foo()"></li>

Где "foo()":

function foo(){ alert(this.tagName); }

Я получаю "undefined."

Я прочь, как "this" должен работать в отношении прикрепленных функций. Но я сбив с толку, потому что "this" не подбирает элемент, но, по-видимому, по умолчанию "окно". Я не могу понять, почему это происходит.

Есть ли у кого есть объяснение?

Ответ 1

Это потому, что вы не передаете ссылку на это в вызове функции JavaScript. это в функции JavaScript не относится к тому же объекту, что и в примере onClick. Вместо этого попробуйте:

 <li onClick="foo(this)"></li>

 function foo(item){ alert(item.tagName); }

Ответ 2

В встроенном прослушивателе:

> <li onClick="alert(this.tagName)"></li>

Значение атрибута onclick эффективно заверяется в функцию и вызывается с установленным в нем элементом, например

function anon() {
  /* attribute value */
}

anon.call(element);

Когда вы помещаете функцию в тело, вы получаете:

function anon() {
  foo();
}

Здесь this внутри anon будет элементом, но поскольку foo вызывается без установки this, он будет undefined. В нестрогом режиме this по умолчанию будет глобальным объектом (window в браузере). В строгом режиме this внутри foo будет undefined.

Одним из решений является передача ссылки на элемент:

<li onclick="foo(this)" ... >

то в функции:

function foo(callingElement) {
  ...
}

или даже:

<li onclick="foo.call(this)" ... >

function foo() {
  var callingElement = this;
}

Ответ 3

Как уже упоминаются другие ответы, значение this будет зависеть от того, как вызывается функция, содержащая его. Но поскольку ваш пример посвящен обработчикам событий, я хотел бы подчеркнуть, что cjc343 сказал в комментариях:

Вы можете найти более разумным, если удалить обработчики событий inline.

Это довольно просто, на самом деле. Учитывая этот HTML:

<ul id="list">
    <li id="item1">item 1</li>
    <li id="item2">item 2</li>
    <li id="item3">item 3</li>
</ul>

Следующий JavaScript будет учитывать как удаление встроенных обработчиков, так и использование делегирования:

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);
});

http://jsfiddle.net/J3Gje/

Это будет работать на всех браузерах, совместимых с моделью событий W3C, включая IE9. Для более старого IE вы должны использовать attachEvent вместо addEventListener и добавлять имена событий в "on". Подробнее здесь.

Ответ 4

Другой вариант, поэтому вам не нужно передавать этот как параметр, использовать call или apply. Это встроенный механизм для установки значения этого внутри функции. Хотя я бы отметил, добавление обработчиков событий непосредственно к вашему html немного устарело. Вы можете проверить структуру JS для делегирования событий (jQuery, Prototype, Dojo, YUI и т.д.).

скрипт: 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); }

Ответ 5

Если вы новичок в JavaScript, не используйте ключевое слово this или new, потому что либо вы ошибетесь, либо ваш код будет излишне неэффективным и более сложным. То, что вы пытаетесь выполнить, заключается в следующем:

<li onclick="foo(this)">some text</li>

В этом примере событие click этого элемента списка запускает функцию с именем foo. Ключевое слово this передается как переменная. Ключевое слово this просто относится к сущности, которая вызвала эту функцию, и если она не может найти этот объект, она будет ссылаться на объект window браузера. В этом случае объект, который вызвал функцию, является элементом списка node из DOM.

Я по-прежнему предлагаю никогда не использовать ключевое слово this, чтобы избежать такого путаницы, продвигающегося вперед.

EDIT: Кроме того, не используйте свойство tagName, поскольку оно не является стандартным. Вместо этого используйте свойство nodeName.