onclick="" против обработчика событий

Если я хочу, чтобы функция выполнялась, я предпочитаю делать inline js:

<p id="element" onclick="doSomething();">Click me</p>

потому что это легче отлаживать.

Тем не менее, я слышу, как люди говорят, что не следует использовать встроенный JS, и делают:

document.getElementById('element').onclick = doSomething;

Почему рекомендуется прослушивать события js?

9 ответов

Решение

В основном это связано с тем, что все держится отдельно. Так что держите HTML/CSS/JS отдельно. Это делает ваш HTML более аккуратным и, я думаю, легче ориентироваться без него.

Затем, когда / если вам нужно внести большие изменения, у вас будет достаточно места для того, чтобы все равно перенести встроенный JS во внешний файл ИЛИ, если вы хотите применить одну и ту же функцию к более чем одной кнопке, тогда кода будет меньше. И чем меньше кода, тем счастливее

Если у вас есть файлы JS правильно и тщательно документированы, то навигация по ним со стороны постороннего человека становится проще.

Один большой аргумент против встроенных обработчиков событий, и аргумент, который рассматривается другими ответами здесь, является разделением представления и логики.

Однако на самом деле существует большая проблема IMO: каким-то неуловимым способом оценки встроенных обработчиков событий.

Как вы можете теперь, содержание on* Атрибуты будут использоваться в качестве тела функции обработчика событий. Но какими характеристиками обладает эта функция?

Одним из удивительных является то, что свойства некоторых элементов-предков и самого элемента находятся в области действия встроенного обработчика событий.

Вот пример:

<form>
    <input name="foo" />
    <button type="button" onclick="console.log(foo); console.log(window.foo);">
        Click me
    </button>
    <div onclick="console.log(foo);">Click me as well!</div>
</form>

Нажав на button бревна

[object HTMLInputElement]
undefined

в консоли. Дело в том, что window.foo является undefined говорит вам, что нет глобальной переменной foo, Так где же переменная foo родом из? Почему console.log(foo) войти элемент ввода и не сгенерировать ошибку ссылки?
Потому что свойства form элемент находится в области действия обработчика события и form Элемент имеет свойство для каждого именованного элемента управления формы, который он содержит. Вы можете легко проверить это с console.log(document.querySelector('form').foo),

Теперь, нажав на div элемент на самом деле выдает ошибку ссылки:

ReferenceError: foo is not defined

Так что, видимо, form Элемент находится только в области элементов управления формы, а не какой-либо потомок. Насколько это сбивает с толку?

Аналогично, свойстваdocument Объект также находится в области встроенных обработчиков событий, которые могут привести к некоторым неожиданным ошибкам (знаете ли вы, что document имеет свойство plugins?).

Как именно оцениваются встроенные обработчики событий, формализовано в спецификации HTML5. Создайте цикл на шаге 10, в частности, где описано создание цепочки областей действия.


Вывод:

Из-за этой неявной связи между элементами и встроенными обработчиками событий, ошибки могут быть действительно трудно отследить. Конечно, хорошо использовать встроенные обработчики событий, если вы просто хотите что-то протестировать. Но использование их в производственном коде сопряжено с более высокими затратами на обслуживание.

Статьи на quirksmode.org очень хорошо объясняют различные способы привязки обработчиков событий и их преимущества.

Есть много причин, чтобы избежать встроенного JavaScript, и одна из, пожалуй, самой важной - это поддержка кода.

Быстрый пример (я использую jQuery просто для демонстрационных целей).

<p class="element" onclick="doSomething();">Click me</p>
<p class="element" onclick="doSomething();">Click me</p>
<p class="element" onclick="doSomething();">Click me</p>
<p class="element" onclick="doSomething();">Click me</p>
<p class="element" onclick="doSomething();">Click me</p>
<p class="element" onclick="doSomething();">Click me</p>

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

<p class="element">Click me</p>
<p class="element">Click me</p>
<p class="element">Click me</p>
<p class="element">Click me</p>
<p class="element">Click me</p>
<p class="element">Click me</p>

$('.element').bind('click', doSomethingElse);

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

РЕДАКТИРОВАТЬ: Предоставление примера для моего комментария ниже.

Project = {
    // All the variables/constants/objects that need to be globally accessible inside the Project object.

    init : function(){
        // Main entry point...
        this.MainMenu.init();

        // Rest of the code which should execute the moment Project is initiated.
    }
}

Project.MainMenu = {
    // All the variables/constants/objects that need to be accessible only to MainMenu.

    init : function(){ // Is run immediatelly by Project.init()
        // Event handlers relevant to the main menu are bound here

        // Rest of the initialization code
    }
}

Project.SlideShow = {
    // All the variables/constants/objects that need to be accessible only to SlideShow.

    init : function(){ // Is run only on pages that really require it.
        // Event handlers for the slideshow.
    }
}

Несмотря на то, что могут подумать другие люди, я думаю, что есть ценность для встраивания слушателей в разметку. В частности, это дает вам гораздо больше свободы для изменения узлов DOM. Если вы добавляете прослушиватели через JavaScript, слушатели теряются, когда вы заменяете innerHTML любых родительских узлов. Если вы встраиваете слушателей в разметку, вы можете клонировать узел, внести в него изменения, а затем заменить исходный узел тем узлом, который вы только что клонировали и изменили.

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

Я вижу, что некоторые говорят, что должно быть разделение проблем относительно Представления и Деловой Логики.

ОП в своем примере действительно показывает это разделение! В встроенном обработчике событий нет логики, а есть просто ссылка на функцию / вызов, которая будет выполняться при событии "click"... сама логика может поддерживаться отдельно в другом месте.

Лично я предпочитаю такой подход из-за логического открытия потока. Если я никогда раньше не видел приложения... первое место, где я собираюсь начать свой обход кода, находится в DOM, и тут же станет ясно, какие обработчики событий находятся в игре, а какие функции обеспечивают логику для обработчики. С использованием, скажем, подхода к обработке событий "JQuery.On", просто просматривая html, вы не представляете, какие обработчики на самом деле подключены и обеспечивают функциональность.

Встроенные обработчики событий, которые просто предоставляют указатели на функции, просто связывают события и не пропускают логику в представление.

То же самое можно сказать о CSS и включении встроенных стилей. Было бы легко не просматривать файл css, чтобы найти текущий класс / id или родительские / дочерние элементы, с которыми вы работаете.

Неужели лучше связать клик, чтобы сказать 10 различных элементов в строке? Или только один обработчик событий, нацеленный на класс? Даже если вам не нужны 10 обработчиков щелчков, всегда лучше настроить ваш код на удобство сопровождения и выполнить обновление в будущем.

Помимо перспективы стандартов кодирования

Использование атрибутов:

<p id="element" onclick="doSomething();">Click me</p>

если вы добавите тот же атрибут еще раз (см. ниже) - рассматривается последний.

<p id="element" onclick="doSomethingMore();">Click me</p>

Использование обработчика событий:

document.getElementById('element').onclick = doSomething;

и скажем, вы добавили строку ниже

document.getElementById('element').onclick = doSomethingMore;Оба обработчика вызываются.

Я не знаю и преимущество использования встроенного обработчика против присоединения обработчика позже. По своему опыту я предпочитаю использовать встроенный метод, когда мне приходится иметь дело с динамическими элементами, например, если я хочу добавить обработчик для каждого изображения, и количество изображений изменяется в соответствии с другими данными (например, изображения в дБ). В этом случае использование inline позволяет мне добавлять обработчик к каждому изображению, в противном случае мне нужно пересчитать количество изображений в файле javascript для добавления и обработки к каждому изображению.

Конечно, когда вы можете использовать библиотеки, такие как jQuery, где вы можете легко получить список встроенных элементов, они не полезны

Я удивлен, что никто не упомянул политику безопасности контента (CSP) как причину использования свойств события вместо атрибутов события. Я думаю, что это превосходит любые личные предпочтения, данные до сих пор.

При правильной реализации (то есть без добавления "небезопасного встроенного", что, по моему мнению, полная чушь), CSP1.0 в основном блокирует все формы встроенного скрипта. Создание встроенных атрибутов событий невозможным. Хотя CSP2.0 пытается исправить это, предоставляя «одноразовый номер» для встроенного скрипта, он не предоставляет этого для встроенных атрибутов событий. CSP3.0 добавляет «небезопасные хэши» для встроенных событий, но по-прежнему не имеет никаких признаков одноразового номера.

Таким образом, в отсутствие одноразового номера для встроенных событий свойства события (или прослушиватели событий) - это очень хороший способ пойти с точки зрения CSP.

Однако я думаю, что встроенные атрибуты событий имеют свое место, и я верю в использование наиболее подходящего метода. CSP, заставляющий мою руку таким образом, глубоко меня раздражает, поэтому я использую библиотеку с именем scriptlock.js (scriptlock.com), которая делает то, что не делает CSP, и добавляет поддержку встроенного одноразового события.

Другие вопросы по тегам