Как способ назначения обработчика событий JavaScript влияет на его выполнение? - PullRequest
2 голосов
/ 19 июня 2020

Обратите внимание на следующий код.

<!DOCTYPE html>
<title>Question</title>
<script>
    function report(handler, _this) {
        alert(
            "handler:\n" + handler + "\n" +
            "this: " + _this + "\n" +
            "event: " + event + "\n"
        )
    }
</script>
<input type=button id=A value=A onclick="report(A.onclick, this)">
<script>function f() {report(B.onclick, this)}</script>
<input type=button id=B value=B onclick="f()">
<input type=button id=C value=C>
<script>C.onclick = function () {report(C.onclick, this)}</script>

Нажав на кнопки, я увидел, что:

  1. A.onclick и B.onclick заключены в функцию " onclick (event) {...} ".
  2. В B.onclick, this - это окно (а не кнопка).

Есть ли другие соображения?

Ответы [ 4 ]

1 голос
/ 27 июля 2020

Встроенные слушатели не имеют никаких преимуществ, напротив, это очень несовершенный способ добавления слушателей событий к элементам HTML.

Различия в исполнении

# 1 this значение внутри кода атрибута привязано к элементу с JavaScript with. Во встроенном коде функция (или любая глобальная переменная) сначала ищется из элемента. Если он не найден (что обычно бывает), встроенный слушатель ищет функцию у предков элемента. Если подходящее имя свойства не найдено, поиск достигает window и запускает глобальную функцию, которая была вызвана. Но если имя функции конфликтует с любым именем свойства в пути поиска, возникает ошибка или выполняется непредвиденная функция.

Пример того, как встроенный прослушиватель находит свойство action формы упаковки, просто нажмите ввод:

function action () {
  console.log('Not a function!?');
}
<form action="" method="post">
  <input onclick="console.log(action); action();">
</form>

# 2 Возвращаемое значение кода атрибута фактически используется в определенных событиях (например, onsubmit). Возврат false предотвращает действие события по умолчанию. Возвращаемое значение от слушателя, прикрепленного с помощью onxxxx или addEventListener, всегда полностью игнорируется (для значения нет получателя).

# 3 Все переменные и функции, используемые в обработчик должен быть доступен глобально. Это тоже можно считать недостатком.

Часто неправильно понимаемое поведение

Функция, вызываемая в коде атрибута, не является фактической функцией обработчика событий, код в атрибуте сам является прикрепленным обработчиком. Следовательно, объект события и правильное значение this присутствуют только в коде обработчика атрибута. Если вам понадобится какое-либо из этих значений в глобальной функции, вы должны передать их вручную при вызове глобальной функции.

Это нормальное поведение любого кода JavaScript, также в случае обработчик прикреплен с addEventListener. Если вы вызовете другую функцию из обработчика событий, вы должны передать объект события и связать / передать this значение, если этой другой функции нужны эти значения.

Пример вызова другой функции от слушателей событий.

function show () {
  console.log('Called:', typeof event, this.toString());
}

const inp = document.querySelectorAll('input')[1];
inp.addEventListener('click', function (e) {
  console.log('addEventListener:', typeof e, this.toString());
  show();
});
<input onclick="console.log('Inline:', typeof event, this.toString()); show();" placeholder="Inline listener">
<input placeholder="addEventListener">

Как мы видим, нет никакой разницы - между типами присоединения - как обрабатываются объект события и this значение. Когда возникает событие, во встроенном слушателе код в атрибуте - это код, выполняемый первым, тогда как для других типов присоединения первый выполняемый код является самой функцией обработчика. (Часть объекта события в примере частично не имеет значения, поскольку почти все браузеры в настоящее время реализуют глобальный объект события.)

Fl aws во встроенных слушателях

# 1 Вы можете присоединить к элементу только одного слушателя того же типа.

# 2 Встроенные слушатели являются потенциальными атакующими векторами, поскольку оба являются кодом слушателя в атрибуте, и любую функцию, вызываемую из кода атрибута, легко переопределить с помощью DevTools.

# 3 При написании встроенного прослушивателя правильно заключать строки в кавычки сложно. Сложность цитирования возрастает при написании тега Dynami c на сервере, так как вы должны позаботиться о цитировании HTML, JS и цитировании языка на стороне сервера.

# 4 Встроенные прослушиватели не работают в модулях и расширениях браузера, не принимаются многими фреймворками и не проходят никаких аудитов безопасности.

# 5 Встроенные слушатели ломают Принцип разделения проблем путем смешивания уровней представления и действий на странице.

element.onxxxx

Свойство

onxxxx не страдает от fl aws # 3, # 4 и # 5, но вы не можете добавить более одного слушателя со свойством onxxxx, и поскольку элементы в любом случае являются глобальными , слушатель легко переопределить с помощью DevTools.

addEventListener

В заключение: используйте addEventListener, чтобы прикрепить события к HTML элементов, у него нет fl aws. this правильно привязан, объект события передан правильно, можно присоединить несколько событий одного типа и без рисков для безопасности (когда обработчик не является глобально доступной функцией), потому что все происходит в инкапсулированном коде, без необходимости глобальная переменная или функция.

В качестве бонуса вы можете выбрать фазу, на которой запускается событие, без дополнительных усилий присоединить к элементам события, запускающие только один раз, и получить лучшую производительность прокрутки с определенными событиями.

1 голос
/ 23 июля 2020

Когда код вызывается из обработчика события, его this устанавливается на элемент DOM, на котором размещается слушатель:

<!-- this will be the button -->
<button onclick="alert(this.tagName.toLowerCase());">
    Show this
</button>

Обратите внимание, однако, что только внешний код имеет свои это настроено таким образом. В этом случае внутренняя функция this не установлена, поэтому она возвращает глобальный объект / объект окна (т.е. объект по умолчанию в режиме non–strict, где this не устанавливается вызовом).

Отметьте this в глобальном контексте и в контексте функции .

<!-- this will be the global object which is window in the browser -->
<button onclick="alert((function() { return this; })());">
    Show inner this
</button>

Еще одно соображение - когда вы используете addEventListener.

Проверьте прослушиватель событий с помощью анонимной функции и стрелочной функции и this в стрелочных функциях .

Обратите внимание, что при анонимности и стрелочные функции аналогичны, у них разные привязки this. В то время как анонимные (и все традиционные функции JavaScript) создают свои собственные привязки this, стрелочные функции наследуют привязку this содержащего контекста.

<button id="my_button" onclick="alert(this.tagName.toLowerCase());">
    Show this
</button>

<script>
    const btn = document.getElementById("my_button")
    btn.addEventListener("click", function () {
        // this will be the button
        console.log(this)
    })
    btn.addEventListener("click", () => {
        // this will be window
        console.log(this)
    })
</script>

Вот простая статья, которую я написал о JavaScript this ключевое слово , но не упоминает обработчик событий.

1 голос
/ 27 июля 2020

Нет никаких особых преимуществ в установке прослушивателей событий в HTML. Более того, это считается вредным, например CSP его запрещает https://developers.google.com/web/fundamentals/security/csp#inline_code_is_considered_harmful

1 голос
/ 28 июня 2020

Есть еще одно соображение. Рассмотрим следующий код.

<!DOCTYPE html>
<title>Answer</title>
<script type=module>function f() {alert(1)}</script>
<input type=button value=1 onclick=f()>
<input type=button value=2 id=button>
<script type=module>button.onclick = function () {alert(2)}</script>

f не определено в onclick для кнопки со значением = 1 (потому что f определено в модуле). Таким образом, когда обработчик события должен выполнить код в модуле (например, код, который использует import), обработчик события должен быть назначен в модуле.

...